"use strict"; // Core UI translations. The texts live in data files (i18n/core/.json) and the // list of languages in i18n/languages.json, so a language is added without code changes. // The Rust Core injects both into the window before this script runs; Node tests read the files. (function exposeI18n(root, factory) { const api = factory(); if (typeof module === "object" && module.exports) module.exports = api; else { root.CHJ_I18N = api; root.chjI18n = api.createI18n(api.defaultLanguage); } })(typeof globalThis !== "undefined" ? globalThis : this, () => { function loadData() { const injected = globalThis.__CHJ_I18N__; if (injected && injected.catalogs && injected.catalogs.core) return { manifest: injected.manifest, catalogs: injected.catalogs.core }; if (typeof require === "function" && typeof __dirname === "string") { const fs = require("fs"), path = require("path"), dir = path.join(__dirname, "i18n"); const manifest = JSON.parse(fs.readFileSync(path.join(dir, "languages.json"), "utf8")); const catalogs = {}; for (const language of manifest.languages) { const file = path.join(dir, "core", `${language.code}.json`); if (fs.existsSync(file)) catalogs[language.code] = JSON.parse(fs.readFileSync(file, "utf8")); } return { manifest, catalogs }; } return { manifest: { defaultLanguage: "cs", fallbackLanguage: "en", languages: [] }, catalogs: {} }; } const { manifest, catalogs } = loadData(); const languages = Object.freeze((manifest.languages || []).filter((entry) => catalogs[entry.code]).map((entry) => Object.freeze({ ...entry }))); const defaultLanguage = catalogs[manifest.defaultLanguage] ? manifest.defaultLanguage : (languages[0]?.code || "cs"); const sourceLanguage = catalogs.cs ? "cs" : defaultLanguage; const localeByLanguage = Object.freeze(Object.fromEntries(languages.map((entry) => [entry.code, entry.locale || entry.code]))); const supportedLanguages = Object.freeze(languages.map((entry) => entry.code)); const entryByLanguage = Object.freeze(Object.fromEntries(languages.map((entry) => [entry.code, entry]))); // Exact code first (zh-tw, de-at), then the base language (fr-CA → fr). function normalizeLanguage(value) { const language = String(value || "").toLowerCase().replace(/_/g, "-"); if (supportedLanguages.includes(language)) return language; const base = language.split("-")[0]; return supportedLanguages.includes(base) ? base : defaultLanguage; } // Lookup order: the language, its parent (regional variants), the fallback language, Czech. function lookupChain(language) { const parent = entryByLanguage[language]?.parent || (language.includes("-") ? language.split("-")[0] : null); return [...new Set([language, parent, manifest.fallbackLanguage, sourceLanguage].filter(Boolean))]; } function direction(language) { return entryByLanguage[language]?.dir === "rtl" ? "rtl" : "ltr"; } function interpolate(template, values = {}) { return String(template).replace(/\{([A-Za-z0-9_]+)\}/g, (match, key) => ( Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match )); } function createI18n(initialLanguage = defaultLanguage) { let language = normalizeLanguage(initialLanguage); const staticText = new WeakMap(); return Object.freeze({ apply(rootNode = document) { // Text the application has set itself is not the static translation; its renderers refresh it. rootNode.querySelectorAll("[data-i18n]").forEach((element) => { if (staticText.has(element) && element.textContent !== staticText.get(element)) return; const text = this.t(element.dataset.i18n); element.textContent = text; staticText.set(element, text); }); rootNode.querySelectorAll("[data-i18n-placeholder]").forEach((element) => { element.placeholder = this.t(element.dataset.i18nPlaceholder); }); rootNode.querySelectorAll("[data-i18n-aria-label]").forEach((element) => { element.setAttribute("aria-label", this.t(element.dataset.i18nAriaLabel)); }); }, formatNumber(value, options) { return Number(value).toLocaleString(localeByLanguage[language], options); }, getLanguage() { return language; }, getDirection() { return direction(language); }, getLocale() { return localeByLanguage[language]; }, setLanguage(value) { language = normalizeLanguage(value); return language; }, t(key, values) { const template = lookupChain(language).map((code) => catalogs[code]?.[key]).find((value) => value !== undefined) ?? key; return interpolate(template, values); } }); } return Object.freeze({ catalogs, createI18n, interpolate, normalizeLanguage, direction, supportedLanguages, languages, defaultLanguage }); });