diff --git a/src/livecodes/UI/loading.ts b/src/livecodes/UI/loading.ts index f301ce79d..dd1051232 100644 --- a/src/livecodes/UI/loading.ts +++ b/src/livecodes/UI/loading.ts @@ -1,10 +1,8 @@ -// eslint-disable-next-line import/no-internal-modules -import { markElementForTranslation } from '../i18n/utils'; - -export const loadingMessage = (message = 'Loading...') => { +export const loadingMessage = ( + message = window.deps.translateString('loading.defaultMessage', 'Loading...'), +) => { const loadingDiv = document.createElement('div'); loadingDiv.innerHTML = message; loadingDiv.classList.add('modal-message'); - markElementForTranslation(loadingDiv, 'generic.loading'); return loadingDiv; }; diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index 02f27e95c..2ef58d6aa 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -139,14 +139,24 @@ import { import { customEvents } from './events/custom-events'; import { populateConfig } from './import/utils'; import { permanentUrlService } from './services/permanent-url'; -import { translate } from './i18n/utils'; +import type { I18nInterpolationType, I18nKeyType } from './i18n/utils'; + +// eslint-disable-next-line no-duplicate-imports +import { translate, translateString } from './i18n/utils'; // declare global dependencies -declare const window: Window & { - deps: { - showMode: typeof showMode; - }; -}; +declare global { + interface Window { + deps: { + showMode: typeof showMode; + translateString: ( + key: I18nKeyType, + value: string, + interpolation?: I18nInterpolationType, + ) => string; + }; + } +} const stores: Stores = createStores(); const eventsManager = createEventsManager(); @@ -3978,6 +3988,18 @@ const handleI18n = () => { }); }; +const translateStringMock = ( + _key: I18nKeyType, + value: string, + interpolation?: I18nInterpolationType, +) => { + if (!interpolation) return value; + for (const [k, v] of Object.entries(interpolation)) { + value = value.replaceAll(`{{${k}}}`, v as string); + } + return value; +}; + const basicHandlers = () => { notifications = createNotifications(); modal = createModal(); @@ -4536,7 +4558,11 @@ const createApi = (): API => { }; const initApp = async (config: Partial, baseUrl: string) => { - window.deps = { showMode }; + window.deps = { + showMode, + translateString: (key: I18nKeyType, value: string, interpolation?: I18nInterpolationType) => + translateString(i18n, key, value, interpolation), + }; await initializePlayground({ config, baseUrl }, async () => { basicHandlers(); await loadToolsPane(); @@ -4546,7 +4572,10 @@ const initApp = async (config: Partial, baseUrl: string) => { }; const initEmbed = async (config: Partial, baseUrl: string) => { - window.deps = { showMode }; + window.deps = { + showMode, + translateString: translateStringMock, + }; await initializePlayground({ config, baseUrl, isEmbed: true }, async () => { basicHandlers(); await loadToolsPane(); @@ -4554,14 +4583,20 @@ const initEmbed = async (config: Partial, baseUrl: string) => { return createApi(); }; const initLite = async (config: Partial, baseUrl: string) => { - window.deps = { showMode }; + window.deps = { + showMode, + translateString: translateStringMock, + }; await initializePlayground({ config, baseUrl, isEmbed: true, isLite: true }, () => { basicHandlers(); }); return createApi(); }; const initHeadless = async (config: Partial, baseUrl: string) => { - window.deps = { showMode: () => undefined }; + window.deps = { + showMode: () => undefined, + translateString: translateStringMock, + }; await initializePlayground({ config, baseUrl, isEmbed: true, isHeadless: true }, () => { notifications = { info: () => undefined, diff --git a/src/livecodes/i18n/utils.ts b/src/livecodes/i18n/utils.ts index f29bd63ee..071e88ee1 100644 --- a/src/livecodes/i18n/utils.ts +++ b/src/livecodes/i18n/utils.ts @@ -1,3 +1,5 @@ +import type { ParseKeys, CustomTypeOptions } from 'i18next'; + // eslint-disable-next-line import/no-internal-modules import { predefinedValues } from '../utils/utils'; @@ -140,21 +142,23 @@ export const translate = ( }); }; -type StringProps = { - [K in keyof T]: T[K] extends string | null ? K : never; -}[keyof T]; - -type AdditionalDataAttributes = 'data-hint'; +export type I18nKeyType = ParseKeys; +export interface I18nInterpolationType { + [key: string]: string | number; +} -export const markElementForTranslation = ( - elem: T, - key: string, - props?: Array | AdditionalDataAttributes>, +export const translateString = ( + i18n: typeof import('./i18n').default | undefined, + key: I18nKeyType, + value: string, + interpolation?: I18nInterpolationType, ) => { - elem.dataset.i18n = key; - if (props) { - elem.dataset.i18nProp = props.join(' '); - } + if (!i18n) return value; + return i18n.t(key, { + ...interpolation, + ...predefinedValues, + defaultValue: value, + }) as string; }; export const dispatchTranslationEvent = (elem: HTMLElement) => {