Important Update Effective February 1, 2024!
Due to recent changes in Jira and Confluence, we've made the tough decision to discontinue the OpenID Connect (OIDC)/OAuth app and no longer provide new versions for the newest Jira/Confluence releases as of January 31, 2024.
This is due to some necessary components no longer shipping with Jira/Confluence, which would require some extensive rewrites of the OIDC App.
Important Update! This app will be discontinued soon!
Due to recent changes in Jira, which no longer ships with some components required for our Read Receipts app to run, we've made the tough decision to discontinue the app, as of Februar 5, 2025.
Important Update! This app will be discontinued soon!
We've made the tough business decision to discontinue the app, as of January 11, 2025.
How can I create a multi-lanaguage IdP Selection Page?
How can I create a multi-lanaguage IdP Selection Page?
Problem
We are using the SAML App for our Atlassian platform. Would it be possible to have a multi-language version of the 'IdP Selection Page Template' (SAML Single Sign On Plugin Configuration → Page Templates)?
Solution
We currently don't have an option in the UI to support multi-language templates. However, you can use JavaScript inside the IdP Selection templates to achieve your goal.
Below you will find an example based on our default page template. Our example contains the languages German (de) and French (fr), however you can change or add the languages to your needs.
Multilanguage Template
<html>
<head>
<title>Select Identity Provider</title>
$webResourceManager.requireResource("com.atlassian.auiplugin:aui-spinner")
$webResourceManager.requireResource("$pluginproperties.pluginkey:resources")
<meta name="decorator" content="atl.general">
</head>
<body class="aui-layout aui-theme-default page-type-message" >
<section id="content" role="main">
<div class="aui-page-panel"><div class="aui-page-panel-inner">
<section class="aui-page-panel-content">
<div style="display: flex; justify-content: center;" id="initial-load-spinner">
<aui-spinner size="large"></aui-spinner>
</div>
<div class="form-body" style="display: none;" id="form-body">
<h1 data-translate="title">Select your Identity Provider</h1>
#if($idpSelected)
<p><span data-translate="wait">Select or wait 3 seconds to use $selectedName</span> <span class="aui-icon aui-icon-wait"></span></p>
<script>
var timeout = setTimeout("location.href = '$selectedUrl';", 3000);
window.onclick= function () { clearTimeout(timeout); }
</script>
#end
#foreach($idp in $idps)
<p>
<a href="$idp.ssoUrl" data-translate="$idp.name">$idp.name</a> $idp.description
</p>
#end
#if($loginurl) <p>
<a href="$loginurl" data-translate="nosso">Login with username and password</a>
</p>
#end
</div>
</section>
</div>
</div>
</section>
<script>
AJS.toInit(function() {
// TRANSLATION SETTINGS
// The key to this template is that elements above whose text is supposed to be translated all have this data-translate attribute, where they get a translation key
// The following map contains mappings from languages to translation keys to the actual translations.
// Add your translations here. The keys ('de', 'fr') can be either locales ('de-DE', 'de-AT') or languages ('de', 'fr'). When in doubt, just use the language it's going to be used as a fallback
var translations = {
de: {
title: "Wählen Sie Ihren Identity Provider",
wait: "Wählen Sie einen Identity Provider aus oder warten Sie 3 Sekunden um $selectedName zu benutzen",
nosso: "Login mit Benutzername und Passwort",
// IdP names are allowed as well
// If they contain non-alphanumeric characters, you'll have to write the name in "quotes"
// the keys are case-sensitive!!!
"Your IdP name": "Ihr IdP-Name"
},
fr: { // This translation has been generously provided by Google Translate, I would not trust it, but it's here as an example.
title: "Choisissez votre fournisseur d'identité",
wait: "Sélectionnez un fournisseur d'identité ou attendez 3 secondes pour utiliser $selectedName",
nosso: "Connectez-vous avec nom d'utilisateur et mot de passe",
"Your IdP name": "Votre nom IdP"
}
};
// This is supposed to be the language present in the fields that are given above
var defaultLanguage = "en";
// TRANSLATION CODE
// This is where the magic happens. Ideally, you shouldn't need to touch the code after this.
// Detect user language with fallbacks for different levels of old browsers
// This will be ordered from most preferred to least preferred
var userLanguages = navigator.languages || [navigator.language] || [navigator.userLanguage] || [];
// ensure default language can also match.
translations[defaultLanguage] = {};
var matchedLanguage = null;
// Check if one the user's preferred languages is available
userLanguages.forEach(function (languageString) {
if (matchedLanguage === null && languageString in translations) {
matchedLanguage = languageString
}
});
// if no match was found, try converting the locale strings into language strings and see if that helps
if (matchedLanguage === null) {
userLanguages.forEach(function (languageString) {
try {
var splitLanguageString = languageString.split('-')[0];
if (matchedLanguage === null && typeof splitLanguageString === 'string' && splitLanguageString in translations) {
matchedLanguage = splitLanguageString;
}
} catch (e) {
console.warn("Error preparing user language");
}
});
}
// if any (non-default language) match was found, we set the translated strings in the UI
if (matchedLanguage !== null && matchedLanguage !== defaultLanguage) {
var translationsToUse = translations[matchedLanguage];
AJS.$('[data-translate]').each(function() {
var elem = AJS.$(this);
var translationToUse = translationsToUse[elem.attr('data-translate')];
if (!translationToUse) {
console.warn("Did not find a", matchedLanguage, "translation for", elem.attr('data-translate'));
return;
}
elem.text(translationToUse);
})
}
// and now display the UI
AJS.$("#initial-load-spinner").hide();
AJS.$("#form-body").show();
})
</script>
</body>
</html>
A little more background / explanation
The script basically extends every element with text in it to have an attribute (data-translate) that contains a “translation key”, while the text is kept as the default language. After loading the page, it executes a JavaScript block. This contains the translations you want to have for each language and translation key (you can modify this according to the example and your need). Afterwards, there’s code that detects the user’s preferred language and does the substitution if necessary.
We have included extensive comments in the code. However, if you are not sure or need help please contact our Support or book a free screen share session.