Files
rpg-companion-sillytavern/src/core/i18n.js
T
2025-11-26 07:49:59 +00:00

104 lines
3.5 KiB
JavaScript

//- No-op in case this is running outside of SillyTavern
const { extension_settings } = typeof self.SillyTavern !== 'undefined' ? self.SillyTavern.getContext() : { extension_settings: {} };
class Internationalization {
constructor() {
this.currentLanguage = 'en';
this.translations = {};
this._listeners = {};
}
addEventListener(event, callback) {
if (!this._listeners[event]) {
this._listeners[event] = [];
}
this._listeners[event].push(callback);
}
dispatchEvent(event, data) {
if (this._listeners[event]) {
this._listeners[event].forEach(callback => callback(data));
}
}
async init() {
const savedLanguage = localStorage.getItem('rpgCompanionLanguage') || 'en';
this.currentLanguage = savedLanguage;
await this.loadTranslations(this.currentLanguage);
this.applyTranslations(document.body);
const langSelect = document.getElementById('rpg-companion-language-select');
if (langSelect) {
langSelect.value = this.currentLanguage;
}
}
async loadTranslations(lang) {
const fetchUrl = `/scripts/extensions/third-party/rpg-companion-sillytavern/src/i18n/${lang}.json`;
try {
const response = await fetch(fetchUrl);
if (!response.ok) {
console.error(`[RPG-Companion-i18n] Failed to load translation file for ${lang}. Status: ${response.status}`);
if (lang !== 'en') {
return this.loadTranslations('en');
}
return;
}
this.translations = await response.json();
} catch (error) {
console.error('[RPG-Companion-i18n] CRITICAL error loading translation file:', error);
}
}
applyTranslations(rootElement) {
if (!rootElement) {
return;
}
// 1. Translate textContent
const textElements = rootElement.querySelectorAll('[data-i18n-key]');
textElements.forEach(element => {
const key = element.dataset.i18nKey;
const translation = this.getTranslation(key);
if (translation) {
element.textContent = translation;
}
});
// 2. Translate title attribute
const titleElements = rootElement.querySelectorAll('[data-i18n-title]');
titleElements.forEach(element => {
const key = element.dataset.i18nTitle;
const translation = this.getTranslation(key);
if (translation) {
element.setAttribute('title', translation);
}
});
// 3. Translate aria-label attribute
const ariaLabelElements = rootElement.querySelectorAll('[data-i18n-aria-label]');
ariaLabelElements.forEach(element => {
const key = element.dataset.i18nAriaLabel;
const translation = this.getTranslation(key);
if (translation) {
element.setAttribute('aria-label', translation);
}
});
}
getTranslation(key) {
return this.translations[key] || null;
}
async setLanguage(lang) {
this.currentLanguage = lang;
localStorage.setItem('rpgCompanionLanguage', lang);
await this.loadTranslations(lang);
this.applyTranslations(document.body);
this.dispatchEvent('languageChanged');
}
}
export const i18n = new Internationalization();