Merge pull request #3573 from SillyTavern/extensions-i18n Add locale data loading for extensions
Signed| @@ -7,7 +7,7 @@ import { renderTemplate, renderTemplateAsync } from './templates.js'; | ||
| 7 | 7 | import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js'; |
| 8 | 8 | import { getContext } from './st-context.js'; |
| 9 | 9 | import { isAdmin } from './user.js'; |
| 10 | 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; |
| 11 | 11 | import { debounce_timeout } from './constants.js'; |
| 12 | 12 | import { accountStorage } from './util/AccountStorage.js'; |
| 13 | 13 | |
| @@ -384,7 +384,7 @@ async function activateExtensions() { | ||
| 384 | 384 | if (meetsModuleRequirements && !isDisabled) { |
| 385 | 385 | try { |
| 386 | 386 | console.debug('Activating extension', name); |
| 387 | 387 | const promise = addExtensionLocale(name, manifest).finally(() => Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)])); |
| 388 | 388 | await promise |
| 389 | 389 | .then(() => activeExtensions.add(name)) |
| 390 | 390 | .catch(err => console.log('Could not activate extension', name, err)); |
| @@ -577,6 +577,42 @@ function addExtensionScript(name, manifest) { | ||
| 577 | 577 | } |
| 578 | 578 | |
| 579 | 579 | /** |
| 580 | + * Adds a localization data for an extension. | |
| 581 | + * @param {string} name Extension name | |
| 582 | + * @param {object} manifest Manifest object | |
| 583 | + */ | |
| 584 | +function addExtensionLocale(name, manifest) { | |
| 585 | + // No i18n data in the manifest | |
| 586 | + if (!manifest.i18n || typeof manifest.i18n !== 'object') { | |
| 587 | + return Promise.resolve(); | |
| 588 | + } | |
| 589 | + | |
| 590 | + const currentLocale = getCurrentLocale(); | |
| 591 | + const localeFile = manifest.i18n[currentLocale]; | |
| 592 | + | |
| 593 | + // Manifest doesn't provide a locale file for the current locale | |
| 594 | + if (!localeFile) { | |
| 595 | + return Promise.resolve(); | |
| 596 | + } | |
| 597 | + | |
| 598 | + return fetch(`/scripts/extensions/${name}/${localeFile}`) | |
| 599 | + .then(async response => { | |
| 600 | + if (!response.ok) { | |
| 601 | + throw new Error(`HTTP ${response.status}: ${response.statusText}`); | |
| 602 | + } | |
| 603 | + | |
| 604 | + const data = await response.json(); | |
| 605 | + | |
| 606 | + if (data && typeof data === 'object') { | |
| 607 | + addLocaleData(currentLocale, data); | |
| 608 | + } | |
| 609 | + }) | |
| 610 | + .catch(err => { | |
| 611 | + console.log('Could not load extension locale data for ' + name, err); | |
| 612 | + }); | |
| 613 | +} | |
| 614 | + | |
| 615 | +/** | |
| 580 | 616 | * Generates HTML string for displaying an extension in the UI. |
| 581 | 617 | * |
| 582 | 618 | * @param {string} name - The name of the extension. |
| @@ -12,6 +12,30 @@ var localeData; | ||
| 12 | 12 | export const getCurrentLocale = () => localeFile; |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | + * Adds additional localization data to the current locale file. | |
| 16 | + * @param {string} localeId Locale ID (e.g. 'fr-fr' or 'zh-cn') | |
| 17 | + * @param {Record<string, string>} data Localization data to add | |
| 18 | + */ | |
| 19 | +export function addLocaleData(localeId, data) { | |
| 20 | + if (!localeData) { | |
| 21 | + console.warn('Localization data not loaded yet. Additional data will not be added.'); | |
| 22 | + return; | |
| 23 | + } | |
| 24 | + | |
| 25 | + if (localeId !== localeFile) { | |
| 26 | + console.debug('Ignoring addLocaleData call for different locale', localeId); | |
| 27 | + return; | |
| 28 | + } | |
| 29 | + | |
| 30 | + for (const [key, value] of Object.entries(data)) { | |
| 31 | + // Overrides for default locale data are not allowed | |
| 32 | + if (!Object.hasOwn(localeData, key)) { | |
| 33 | + localeData[key] = value; | |
| 34 | + } | |
| 35 | + } | |
| 36 | +} | |
| 37 | + | |
| 38 | +/** | |
| 15 | 39 | * An observer that will check if any new i18n elements are added to the document |
| 16 | 40 | * @type {MutationObserver} |
| 17 | 41 | */ |
| @@ -54,7 +54,7 @@ import { | ||
| 54 | 54 | writeExtensionField, |
| 55 | 55 | } from './extensions.js'; |
| 56 | 56 | import { groups, openGroupChat, selected_group } from './group-chats.js'; |
| 57 | 57 | import { addLocaleData, getCurrentLocale, t, translate } from './i18n.js'; |
| 58 | 58 | import { hideLoader, showLoader } from './loader.js'; |
| 59 | 59 | import { MacrosParser } from './macros.js'; |
| 60 | 60 | import { getChatCompletionModel, oai_settings } from './openai.js'; |
| @@ -165,6 +165,8 @@ export function getContext() { | ||
| 165 | 165 | isMobile, |
| 166 | 166 | t, |
| 167 | 167 | translate, |
| 168 | + getCurrentLocale, | |
| 169 | + addLocaleData, | |
| 168 | 170 | tags, |
| 169 | 171 | tagMap: tag_map, |
| 170 | 172 | menuType: menu_type, |