Files
Wegwichtel/public/js/i18n.js
T
Florian Zumpe 5c4d20bd1d
Sonarqube Scanner / Prepare environment (push) Successful in 19s
Sonarqube Scanner / Build and test (push) Successful in 43s
Sonarqube Scanner / SonarQube analysis (push) Failing after 2m28s
Sonarqube Scanner / Deploy to Server (push) Successful in 16s
fix sonar findings and translation structure
2026-10-01 16:31:46 +02:00

77 lines
3.0 KiB
JavaScript

globalThis.Wegwichtel ??= {};
(function (ns) {
'use strict';
class WegwichtelI18n {
static #languages = new Map(ns.TextCatalogs);
static #fallbackLanguage = this.#languages.keys().next().value;
static #language = this.#fallbackLanguage;
static get language() { return this.#language; }
static detectLanguage(preferences = globalThis.navigator?.languages ?? [globalThis.navigator?.language]) {
for (const language of preferences) {
const requested = String(language ?? '').toLowerCase();
if (this.#languages.has(requested)) return requested;
const base = requested.split('-')[0];
if (this.#languages.has(base)) return base;
}
return this.#fallbackLanguage;
}
static register(language, messages) {
this.#languages.set(language.toLowerCase(), Object.freeze({ ...messages }));
}
static setLanguage(language, root = globalThis.document) {
const requested = String(language).toLowerCase();
const base = requested.split('-')[0];
this.#language = this.#fallbackLanguage;
if (this.#languages.has(base)) this.#language = base;
if (this.#languages.has(requested)) this.#language = requested;
this.apply(root);
return this.#language;
}
static t(key, variables = {}) {
let value = this.#lookup(this.#languages.get(this.#language), key)
?? this.#lookup(this.#languages.get(this.#fallbackLanguage), key);
if (value == null) throw new RangeError(`Missing translation: ${key}`);
if (typeof value === 'object') {
const plural = new Intl.PluralRules(this.#language).select(Number(variables.count));
value = value[plural] ?? value.other;
}
return value.replaceAll(/\{(\w+)\}/g, (placeholder, name) => {
if (!Object.hasOwn(variables, name)) return placeholder;
return String(variables[name] ?? '');
});
}
static #lookup(catalog, key) {
if (Object.hasOwn(catalog, key)) return catalog[key];
let value = catalog;
for (const part of key.split('.')) {
if (!value || !Object.hasOwn(value, part)) return undefined;
value = value[part];
}
return value;
}
static apply(root = globalThis.document) {
if (!root?.querySelectorAll) return;
if (root.documentElement) root.documentElement.lang = this.#language;
for (const element of root.querySelectorAll('[data-i18n]')) {
if (element.dataset?.i18n) element.textContent = this.t(element.dataset.i18n);
}
for (const [attribute, key] of [['title', 'i18nTitle'], ['aria-label', 'i18nAriaLabel'], ['placeholder', 'i18nPlaceholder'], ['alt', 'i18nAlt']]) {
for (const element of root.querySelectorAll(`[data-i18n-${attribute}]`)) {
if (element.dataset?.[key] && typeof element.setAttribute === 'function') element.setAttribute(attribute, this.t(element.dataset[key]));
}
}
}
}
ns.I18n = WegwichtelI18n;
WegwichtelI18n.setLanguage(WegwichtelI18n.detectLanguage());
}(globalThis.Wegwichtel));