Files
SpineViewer/release/UNIVERSAL_SpineThumbnailGenerateServer/js/i18n.js
T
2026-10-30 16:08:36 +08:00

113 lines
3.1 KiB
JavaScript

// Shared localization for the service panel and progress window.
(function () {
'use strict';
const fs = require('fs');
const path = require('path');
const LANGUAGES = ['en', 'zh_CN', 'zh_TW', 'ja_JP', 'ko_KR', 'ru_RU', 'es_ES', 'de_DE'];
const FALLBACK = 'zh_CN';
function normalizeLocale(raw) {
if (!raw) return null;
const tag = String(raw).trim().toLowerCase().replace(/-/g, '_');
if (tag === 'zh_tw' || tag === 'zh_hk' || tag === 'zh_hant' || tag === 'zh_mo') return 'zh_TW';
for (const code of LANGUAGES) {
if (code.toLowerCase() === tag) return code;
}
const base = tag.split('_')[0];
if (base === 'zh') return 'zh_CN';
for (const code of LANGUAGES) {
if (code.toLowerCase().split('_')[0] === base) return code;
}
return null;
}
function requestedLocale() {
try {
const params = new URLSearchParams(location.search);
const fromUrl = params.get('lang') || params.get('locale');
if (fromUrl) return fromUrl;
} catch (error) { /* No browser location. */ }
try {
if (typeof eagle !== 'undefined' && eagle.app && eagle.app.locale) return eagle.app.locale;
} catch (error) { /* Eagle API is injected asynchronously. */ }
try {
return navigator.language;
} catch (error) {
return null;
}
}
function pluginRoot() {
if (window.SpinePluginPath) return window.SpinePluginPath;
if (window.SpinePluginRoot) return window.SpinePluginRoot;
return typeof __dirname !== 'undefined' ? __dirname : null;
}
function readLocale(code) {
try {
const root = pluginRoot();
if (!root) return {};
return JSON.parse(fs.readFileSync(path.join(root, '_locales', code + '.json'), 'utf8'));
} catch (error) {
return {};
}
}
function lookup(table, key) {
let node = table;
for (const part of String(key).split('.')) {
if (!node || typeof node !== 'object') return undefined;
node = node[part];
}
return typeof node === 'string' ? node : undefined;
}
function apply(root) {
const scope = root || document;
for (const el of scope.querySelectorAll('[data-i18n]')) el.textContent = t(el.dataset.i18n);
for (const el of scope.querySelectorAll('[data-i18n-title]')) el.title = t(el.dataset.i18nTitle);
for (const el of scope.querySelectorAll('[data-i18n-aria-label]')) el.setAttribute('aria-label', t(el.dataset.i18nAriaLabel));
}
let fallbackMessages = readLocale(FALLBACK);
let locale = FALLBACK;
let messages = fallbackMessages;
function t(key, vars) {
let text = lookup(messages, key);
if (text === undefined) text = lookup(fallbackMessages, key);
if (text === undefined) return key;
if (vars) {
text = text.replace(/\{(\w+)\}/g, (whole, name) =>
Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : whole);
}
return text;
}
function setLocale(raw) {
locale = normalizeLocale(raw) || FALLBACK;
fallbackMessages = readLocale(FALLBACK);
messages = locale === FALLBACK ? fallbackMessages : readLocale(locale);
document.documentElement.lang = locale.replace('_', '-');
apply(document);
return locale;
}
const api = {
languages: LANGUAGES,
fallback: FALLBACK,
get locale() { return locale; },
normalizeLocale,
requestedLocale,
setLocale,
t,
apply,
};
window.SpineThumbI18n = api;
setLocale(requestedLocale());
})();