262 lines
8.2 KiB
JavaScript
262 lines
8.2 KiB
JavaScript
// handels the language translation
|
|
|
|
(function attachAppLanguage(window, document) {
|
|
const DEFAULT_LANGUAGE = "de";
|
|
const DEFAULT_STORAGE_KEY = "preferredLanguage";
|
|
const DEFAULT_SUPPORTED_LANGUAGES = ["de", "en"];
|
|
|
|
function resolveSupportedLanguages(supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES) {
|
|
const normalizedLanguages = Array.isArray(supportedLanguages)
|
|
? supportedLanguages
|
|
.map((language) => String(language || "").trim().slice(0, 2).toLowerCase())
|
|
.filter(Boolean)
|
|
: [];
|
|
|
|
return normalizedLanguages.length > 0 ? normalizedLanguages : DEFAULT_SUPPORTED_LANGUAGES.slice();
|
|
}
|
|
|
|
function normalizeLanguage(
|
|
language,
|
|
{
|
|
supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES,
|
|
defaultLanguage = DEFAULT_LANGUAGE
|
|
} = {}
|
|
) {
|
|
const normalizedSupportedLanguages = resolveSupportedLanguages(supportedLanguages);
|
|
const normalizedDefaultLanguage = normalizedSupportedLanguages.includes(defaultLanguage)
|
|
? defaultLanguage
|
|
: normalizedSupportedLanguages[0] || DEFAULT_LANGUAGE;
|
|
const shortLanguage = String(language || "").trim().slice(0, 2).toLowerCase();
|
|
|
|
return normalizedSupportedLanguages.includes(shortLanguage) ? shortLanguage : normalizedDefaultLanguage;
|
|
}
|
|
|
|
function getStoredLanguage({ storageKey = DEFAULT_STORAGE_KEY } = {}) {
|
|
try {
|
|
return window.localStorage.getItem(storageKey);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function storeLanguage(language, { storageKey = DEFAULT_STORAGE_KEY } = {}) {
|
|
try {
|
|
window.localStorage.setItem(storageKey, language);
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
function detectBrowserLanguage(options = {}) {
|
|
return normalizeLanguage(navigator.language || navigator.userLanguage || options.defaultLanguage, options);
|
|
}
|
|
|
|
function getPreferredLanguage(
|
|
{
|
|
supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES,
|
|
defaultLanguage = DEFAULT_LANGUAGE,
|
|
storageKey = DEFAULT_STORAGE_KEY,
|
|
fallbackLanguage = defaultLanguage
|
|
} = {}
|
|
) {
|
|
return normalizeLanguage(
|
|
getStoredLanguage({ storageKey }) || detectBrowserLanguage({ supportedLanguages, defaultLanguage }) || fallbackLanguage,
|
|
{
|
|
supportedLanguages,
|
|
defaultLanguage: fallbackLanguage
|
|
}
|
|
);
|
|
}
|
|
|
|
function getNextLanguage(language, supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES) {
|
|
const normalizedSupportedLanguages = resolveSupportedLanguages(supportedLanguages);
|
|
const currentLanguage = normalizeLanguage(language, {
|
|
supportedLanguages: normalizedSupportedLanguages,
|
|
defaultLanguage: normalizedSupportedLanguages[0]
|
|
});
|
|
const currentIndex = normalizedSupportedLanguages.indexOf(currentLanguage);
|
|
|
|
if (currentIndex === -1 || normalizedSupportedLanguages.length < 2) {
|
|
return currentLanguage;
|
|
}
|
|
|
|
return normalizedSupportedLanguages[(currentIndex + 1) % normalizedSupportedLanguages.length];
|
|
}
|
|
|
|
function setDocumentLanguage(language) {
|
|
if (document.documentElement) {
|
|
document.documentElement.lang = language;
|
|
}
|
|
|
|
if (document.body) {
|
|
document.body.dataset.language = language;
|
|
}
|
|
}
|
|
|
|
function applyTextTranslations({
|
|
translate,
|
|
selector = "[data-i18n]",
|
|
allowHtml = () => false
|
|
} = {}) {
|
|
if (typeof translate !== "function") {
|
|
return;
|
|
}
|
|
|
|
document.querySelectorAll(selector).forEach((element) => {
|
|
const key = element.dataset.i18n;
|
|
const translatedText = translate(key, element);
|
|
|
|
if (translatedText === undefined || translatedText === null) {
|
|
return;
|
|
}
|
|
|
|
if (allowHtml(element, translatedText, key)) {
|
|
element.innerHTML = String(translatedText);
|
|
return;
|
|
}
|
|
|
|
element.textContent = String(translatedText);
|
|
});
|
|
}
|
|
|
|
function applyAttributeTranslations({
|
|
translate,
|
|
mappings = []
|
|
} = {}) {
|
|
if (typeof translate !== "function") {
|
|
return;
|
|
}
|
|
|
|
mappings.forEach(({ selector, datasetKey, attribute }) => {
|
|
document.querySelectorAll(selector).forEach((element) => {
|
|
const key = element.dataset[datasetKey];
|
|
const translatedText = translate(key, element, attribute);
|
|
|
|
if (translatedText === undefined || translatedText === null) {
|
|
return;
|
|
}
|
|
|
|
element.setAttribute(attribute, String(translatedText));
|
|
});
|
|
});
|
|
}
|
|
|
|
function applyTranslations({
|
|
translate,
|
|
selector = "[data-i18n]",
|
|
allowHtml,
|
|
attributeMappings = []
|
|
} = {}) {
|
|
applyTextTranslations({ translate, selector, allowHtml });
|
|
applyAttributeTranslations({ translate, mappings: attributeMappings });
|
|
}
|
|
|
|
function updateLanguageSwitch(
|
|
button,
|
|
{
|
|
language,
|
|
label = String(language || "").toUpperCase(),
|
|
ariaLabel,
|
|
title
|
|
} = {}
|
|
) {
|
|
if (!button) {
|
|
return;
|
|
}
|
|
|
|
button.textContent = label;
|
|
|
|
if (ariaLabel !== undefined) {
|
|
button.setAttribute("aria-label", ariaLabel);
|
|
}
|
|
|
|
if (title !== undefined) {
|
|
button.title = title;
|
|
}
|
|
}
|
|
|
|
function createController({
|
|
button = document.getElementById("langSwitch"),
|
|
defaultLanguage = DEFAULT_LANGUAGE,
|
|
supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES,
|
|
storageKey = DEFAULT_STORAGE_KEY,
|
|
onApply = () => { }
|
|
} = {}) {
|
|
const options = {
|
|
defaultLanguage,
|
|
supportedLanguages,
|
|
storageKey
|
|
};
|
|
|
|
let currentLanguage = normalizeLanguage(defaultLanguage, options);
|
|
|
|
function apply(language, source = "apply") {
|
|
currentLanguage = normalizeLanguage(language, options);
|
|
setDocumentLanguage(currentLanguage);
|
|
|
|
onApply(currentLanguage, {
|
|
source,
|
|
button,
|
|
applyTranslations,
|
|
updateLanguageSwitch: (switchOptions = {}) => updateLanguageSwitch(button, {
|
|
language: currentLanguage,
|
|
...switchOptions
|
|
}),
|
|
getNextLanguage: () => getNextLanguage(currentLanguage, supportedLanguages),
|
|
normalizeLanguage: (value) => normalizeLanguage(value, options)
|
|
});
|
|
|
|
storeLanguage(currentLanguage, { storageKey });
|
|
return currentLanguage;
|
|
}
|
|
|
|
function toggle() {
|
|
return apply(getNextLanguage(currentLanguage, supportedLanguages), "toggle");
|
|
}
|
|
|
|
function init(initialLanguage = getPreferredLanguage({
|
|
supportedLanguages,
|
|
defaultLanguage,
|
|
storageKey,
|
|
fallbackLanguage: defaultLanguage
|
|
})) {
|
|
if (button) {
|
|
button.addEventListener("click", toggle);
|
|
}
|
|
|
|
return apply(initialLanguage, "init");
|
|
}
|
|
|
|
return {
|
|
apply,
|
|
getCurrentLanguage: () => currentLanguage,
|
|
getPreferredLanguage: (fallbackLanguage = defaultLanguage) => getPreferredLanguage({
|
|
supportedLanguages,
|
|
defaultLanguage,
|
|
storageKey,
|
|
fallbackLanguage
|
|
}),
|
|
init,
|
|
toggle
|
|
};
|
|
}
|
|
|
|
window.AppLanguage = {
|
|
DEFAULT_LANGUAGE,
|
|
DEFAULT_STORAGE_KEY,
|
|
DEFAULT_SUPPORTED_LANGUAGES: DEFAULT_SUPPORTED_LANGUAGES.slice(),
|
|
applyAttributeTranslations,
|
|
applyTextTranslations,
|
|
applyTranslations,
|
|
createController,
|
|
detectBrowserLanguage,
|
|
getNextLanguage,
|
|
getPreferredLanguage,
|
|
getStoredLanguage,
|
|
normalizeLanguage,
|
|
setDocumentLanguage,
|
|
storeLanguage,
|
|
updateLanguageSwitch
|
|
};
|
|
})(window, document);
|