SillyTavern/public/scripts/i18n.js

129 lines
4.7 KiB
JavaScript
Raw Normal View History

2023-12-02 19:04:51 +01:00
import { registerDebugFunction } from './power-user.js';
import { waitUntilCondition } from './utils.js';
2023-12-02 19:04:51 +01:00
const storageKey = 'language';
2024-03-10 00:03:51 +01:00
const overrideLanguage = localStorage.getItem('language');
const localeFile = overrideLanguage || navigator.userLanguage || 'lang';
export const localeData = await fetch(`./locales/${localeFile}.json`).then(response => {
console.log(`Loading locale data from ./locales/${localeFile}.json`);
if (!response.ok) {
throw new Error(`Failed to load locale data: ${response.status} ${response.statusText}`);
}
return response.json();
});
function getMissingTranslations() {
const missingData = [];
2024-03-10 00:03:51 +01:00
for (const language of localeData) {
2023-12-02 19:04:51 +01:00
$(document).find('[data-i18n]').each(function () {
const keys = $(this).data('i18n').split(';'); // Multi-key entries are ; delimited
for (const key of keys) {
const attributeMatch = key.match(/\[(\S+)\](.+)/); // [attribute]key
if (attributeMatch) { // attribute-tagged key
2024-03-10 00:03:51 +01:00
const localizedValue = localeData?.[attributeMatch[2]];
if (!localizedValue) {
missingData.push({ key, language, value: $(this).attr(attributeMatch[1]) });
}
} else { // No attribute tag, treat as 'text'
2024-03-10 00:03:51 +01:00
const localizedValue = localeData?.[key];
if (!localizedValue) {
missingData.push({ key, language, value: $(this).text().trim() });
}
}
}
});
}
// Remove duplicates
const uniqueMissingData = [];
for (const { key, language, value } of missingData) {
if (!uniqueMissingData.some(x => x.key === key && x.language === language && x.value === value)) {
uniqueMissingData.push({ key, language, value });
}
}
// Sort by language, then key
uniqueMissingData.sort((a, b) => a.language.localeCompare(b.language) || a.key.localeCompare(b.key));
// Map to { language: { key: value } }
2024-03-10 00:03:51 +01:00
let missingDataMap = {};
for (const { key, value } of uniqueMissingData) {
if (!missingDataMap) {
missingDataMap = {};
}
2024-03-10 00:03:51 +01:00
missingDataMap[key] = value;
}
console.table(uniqueMissingData);
console.log(missingDataMap);
2023-08-27 22:20:43 +02:00
toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`);
}
export function applyLocale(root = document) {
2023-12-02 19:04:51 +01:00
const $root = root instanceof Document ? $(root) : $(new DOMParser().parseFromString(root, 'text/html'));
//find all the elements with `data-i18n` attribute
2023-12-02 19:04:51 +01:00
$root.find('[data-i18n]').each(function () {
//read the translation from the language data
2023-12-02 19:04:51 +01:00
const keys = $(this).data('i18n').split(';'); // Multi-key entries are ; delimited
for (const key of keys) {
const attributeMatch = key.match(/\[(\S+)\](.+)/); // [attribute]key
if (attributeMatch) { // attribute-tagged key
2024-03-10 00:03:51 +01:00
const localizedValue = localeData?.[attributeMatch[2]];
if (localizedValue) {
$(this).attr(attributeMatch[1], localizedValue);
}
} else { // No attribute tag, treat as 'text'
2024-03-10 00:03:51 +01:00
const localizedValue = localeData?.[key];
if (localizedValue) {
$(this).text(localizedValue);
}
}
}
});
if (root !== document) {
return $root.get(0).body.innerHTML;
}
}
2024-03-10 00:03:51 +01:00
async function addLanguagesToDropdown() {
const langs = await fetch('/locales/lang.json').then(response => response.json());
2024-03-10 00:03:51 +01:00
for (const lang of langs) {
const option = document.createElement('option');
option.value = lang;
option.innerText = lang;
$('#ui_language_select').append(option);
}
const selectedLanguage = localStorage.getItem(storageKey);
if (selectedLanguage) {
$('#ui_language_select').val(selectedLanguage);
}
}
export function initLocales() {
waitUntilCondition(() => !!localeData);
applyLocale();
addLanguagesToDropdown();
$('#ui_language_select').on('change', async function () {
2023-08-22 13:30:49 +02:00
const language = String($(this).val());
if (language) {
localStorage.setItem(storageKey, language);
} else {
localStorage.removeItem(storageKey);
}
location.reload();
});
2023-08-27 22:20:43 +02:00
registerDebugFunction('getMissingTranslations', 'Get missing translations', 'Detects missing localization data and dumps the data into the browser console.', getMissingTranslations);
registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);
2023-12-02 16:15:03 +01:00
}