CH-J ToolsSSH によるサーバー管理
メニュー
公開済みのソース

CH-J Tools

特定のアプリケーションリリースのディレクトリとファイルを閲覧できます。

ソースの ZIP をダウンロード
CH-J / Server Manager 1704 ファイル
0.1.2-beta 追加日時: · ビルド日時:
i18n.js 追加日時: 日付の記録なし 5.6 KB · 104 行
コード ファイルをダウンロード
1"use strict";
3// Core UI translations. The texts live in data files (i18n/core/<language>.json) and the
4// list of languages in i18n/languages.json, so a language is added without code changes.
5// The Rust Core injects both into the window before this script runs; Node tests read the files.
6(function exposeI18n(root, factory) {
7 const api = factory();
8 if (typeof module === "object" && module.exports) module.exports = api;
9 else {
10 root.CHJ_I18N = api;
11 root.chjI18n = api.createI18n(api.defaultLanguage);
12 }
13})(typeof globalThis !== "undefined" ? globalThis : this, () => {
14 function loadData() {
15 const injected = globalThis.__CHJ_I18N__;
16 if (injected && injected.catalogs && injected.catalogs.core) return { manifest: injected.manifest, catalogs: injected.catalogs.core };
17 if (typeof require === "function" && typeof __dirname === "string") {
18 const fs = require("fs"), path = require("path"), dir = path.join(__dirname, "i18n");
19 const manifest = JSON.parse(fs.readFileSync(path.join(dir, "languages.json"), "utf8"));
20 const catalogs = {};
21 for (const language of manifest.languages) {
22 const file = path.join(dir, "core", `${language.code}.json`);
23 if (fs.existsSync(file)) catalogs[language.code] = JSON.parse(fs.readFileSync(file, "utf8"));
24 }
25 return { manifest, catalogs };
26 }
27 return { manifest: { defaultLanguage: "cs", fallbackLanguage: "en", languages: [] }, catalogs: {} };
28 }
30 const { manifest, catalogs } = loadData();
31 // The main window receives catalogs only for its language chain; `manifest.available` lists every language with a
32 // catalog in the Core, and `addCatalogs` adds one when the user switches to it.
33 const available = Array.isArray(manifest.available) ? manifest.available : null;
34 const languages = Object.freeze((manifest.languages || []).filter((entry) => (available ? available.includes(entry.code) : catalogs[entry.code])).map((entry) => Object.freeze({ ...entry })));
35 const known = (code) => Boolean(catalogs[code]) || Boolean(available && available.includes(code));
36 const defaultLanguage = known(manifest.defaultLanguage) ? manifest.defaultLanguage : (languages[0]?.code || "cs");
37 const sourceLanguage = known("cs") ? "cs" : defaultLanguage;
38 const localeByLanguage = Object.freeze(Object.fromEntries(languages.map((entry) => [entry.code, entry.locale || entry.code])));
39 const supportedLanguages = Object.freeze(languages.map((entry) => entry.code));
41 const entryByLanguage = Object.freeze(Object.fromEntries(languages.map((entry) => [entry.code, entry])));
43 // Exact code first (zh-tw, de-at), then the base language (fr-CA → fr).
44 function normalizeLanguage(value) {
45 const language = String(value || "").toLowerCase().replace(/_/g, "-");
46 if (supportedLanguages.includes(language)) return language;
47 const base = language.split("-")[0];
48 return supportedLanguages.includes(base) ? base : defaultLanguage;
49 }
51 // Lookup order: the language, its parent (regional variants), the fallback language, Czech.
52 function lookupChain(language) {
53 const parent = entryByLanguage[language]?.parent || (language.includes("-") ? language.split("-")[0] : null);
54 return [...new Set([language, parent, manifest.fallbackLanguage, sourceLanguage].filter(Boolean))];
55 }
57 function direction(language) {
58 return entryByLanguage[language]?.dir === "rtl" ? "rtl" : "ltr";
59 }
61 function interpolate(template, values = {}) {
62 return String(template).replace(/\{([A-Za-z0-9_]+)\}/g, (match, key) => (
63 Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match
64 ));
65 }
67 function createI18n(initialLanguage = defaultLanguage) {
68 let language = normalizeLanguage(initialLanguage);
69 const staticText = new WeakMap();
70 return Object.freeze({
71 apply(rootNode = document) {
72 // Text the application has set itself is not the static translation; its renderers refresh it.
73 rootNode.querySelectorAll("[data-i18n]").forEach((element) => {
74 if (staticText.has(element) && element.textContent !== staticText.get(element)) return;
75 const text = this.t(element.dataset.i18n);
76 element.textContent = text;
77 staticText.set(element, text);
78 });
79 rootNode.querySelectorAll("[data-i18n-placeholder]").forEach((element) => { element.placeholder = this.t(element.dataset.i18nPlaceholder); });
80 rootNode.querySelectorAll("[data-i18n-aria-label]").forEach((element) => { element.setAttribute("aria-label", this.t(element.dataset.i18nAriaLabel)); });
81 },
82 formatNumber(value, options) { return Number(value).toLocaleString(localeByLanguage[language], options); },
83 getLanguage() { return language; },
84 getDirection() { return direction(language); },
85 getLocale() { return localeByLanguage[language]; },
86 setLanguage(value) { language = normalizeLanguage(value); return language; },
87 t(key, values) {
88 const template = lookupChain(language).map((code) => catalogs[code]?.[key]).find((value) => value !== undefined) ?? key;
89 return interpolate(template, values);
90 }
91 });
92 }
94 // Whether `language` and its lookup chain are loaded (otherwise fetch them before switching).
95 function hasLanguage(language) {
96 return lookupChain(normalizeLanguage(language)).every((code) => !known(code) || catalogs[code]);
97 }
99 function addCatalogs(core) {
100 for (const [code, catalog] of Object.entries(core || {})) if (!catalogs[code]) catalogs[code] = catalog;
101 }
103 return Object.freeze({ catalogs, createI18n, interpolate, normalizeLanguage, direction, supportedLanguages, languages, defaultLanguage, hasLanguage, addCatalogs });
104});
SHA-2566a9abba60d30c04d7c1d1c50eb188cebe50739ce0d208d31dc85b72413728150

CH-J Proprietary Software License 1.14 · ソースは CH-J Proprietary Software License 1.14 に基づいて提供されています。公開されていても、ライセンス条項が変わったり、追加の権利が付与されたりすることはありません。

アーカイブの SHA-256ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
ファイル