Initial curated chimera source from a8ae5b1db07fb49926c94585e2718e5130330974
This commit is contained in:
commit
c737a37f71
531 files changed
+119270
No files matched your search
@@ -0,0 +1,84 @@
|
||||
// ── Chimera i18n engine ───────────────────────────────────────────────────────
|
||||
// Loaded first so window.t() is available to all modules.
|
||||
// Call window.i18n.load(code) to fetch a lang file and re-apply all translations.
|
||||
|
||||
window.chimeraI18nReady = new Promise(resolve => {
|
||||
window.chimeraResolveI18nReady = resolve;
|
||||
});
|
||||
|
||||
window.i18n = {
|
||||
current: 'en',
|
||||
strings: {},
|
||||
_observer: null,
|
||||
|
||||
/** Translate a key. Falls back to `fallback` if provided, then the key itself. */
|
||||
t(key, fallback) {
|
||||
const v = this.strings[key];
|
||||
if (v !== undefined) return v;
|
||||
return fallback !== undefined ? fallback : key;
|
||||
},
|
||||
|
||||
/** Walk every [data-i18n] element and set its textContent to the translated string.
|
||||
* If no translation exists for a key the original text is left unchanged. */
|
||||
apply(root = document) {
|
||||
document.documentElement.lang = this.current || 'en';
|
||||
const select = selector => [
|
||||
...(root.matches?.(selector) ? [root] : []),
|
||||
...(root.querySelectorAll?.(selector) || []),
|
||||
];
|
||||
select('[data-i18n]').forEach(el => {
|
||||
const val = this.strings[el.dataset.i18n];
|
||||
if (val !== undefined) el.textContent = val;
|
||||
});
|
||||
select('[data-i18n-placeholder]').forEach(el => {
|
||||
const val = this.strings[el.dataset.i18nPlaceholder];
|
||||
if (val !== undefined) el.placeholder = val;
|
||||
});
|
||||
select('[data-i18n-title]').forEach(el => {
|
||||
const val = this.strings[el.dataset.i18nTitle];
|
||||
if (val !== undefined) el.title = val;
|
||||
});
|
||||
select('[data-i18n-aria-label]').forEach(el => {
|
||||
const val = this.strings[el.dataset.i18nAriaLabel];
|
||||
if (val !== undefined) el.setAttribute('aria-label', val);
|
||||
});
|
||||
if (!this._observer && document.body) {
|
||||
this._observer = new MutationObserver(records => {
|
||||
for (const record of records) {
|
||||
for (const node of record.addedNodes) {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) this.apply(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
this._observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
},
|
||||
|
||||
/** Load a language file by code (e.g. "en", "fr", "de") via IPC, then apply. */
|
||||
async load(code) {
|
||||
if (!code) code = 'en';
|
||||
try {
|
||||
const data = await window.pork.langLoad(code);
|
||||
if (data && typeof data.strings === 'object') {
|
||||
this.strings = data.strings;
|
||||
this.current = data.meta?.code || code;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[i18n] Could not load language "' + code + '":', e.message || e);
|
||||
// Fall back to empty (original HTML text remains visible)
|
||||
this.strings = {};
|
||||
this.current = 'en';
|
||||
}
|
||||
this.apply();
|
||||
window.dispatchEvent(new CustomEvent('chimera:language-changed', {
|
||||
detail: { code: this.current },
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
/** Shorthand global translate function */
|
||||
window.t = (key, fallback) => window.i18n.t(key, fallback);
|
||||
window.tf = (key, fallback, values = {}) => Object.entries(values).reduce(
|
||||
(text, [name, value]) => text.replaceAll(`{${name}}`, String(value)),
|
||||
window.i18n.t(key, fallback),
|
||||
);
|
||||
Reference in new issue
Block a user