77 lines
3.0 KiB
JavaScript
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));
|