Skip to content

Commit

Permalink
feat(i18n): string-level i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
zyf722 committed May 3, 2024
1 parent 49921db commit d01bab0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 28 deletions.
8 changes: 3 additions & 5 deletions src/livecodes/UI/loading.ts
Original file line number Diff line number Diff line change
@@ -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;
};
55 changes: 45 additions & 10 deletions src/livecodes/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -4536,7 +4558,11 @@ const createApi = (): API => {
};

const initApp = async (config: Partial<Config>, 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();
Expand All @@ -4546,22 +4572,31 @@ const initApp = async (config: Partial<Config>, baseUrl: string) => {
};

const initEmbed = async (config: Partial<Config>, baseUrl: string) => {
window.deps = { showMode };
window.deps = {
showMode,
translateString: translateStringMock,
};
await initializePlayground({ config, baseUrl, isEmbed: true }, async () => {
basicHandlers();
await loadToolsPane();
});
return createApi();
};
const initLite = async (config: Partial<Config>, 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<Config>, baseUrl: string) => {
window.deps = { showMode: () => undefined };
window.deps = {
showMode: () => undefined,
translateString: translateStringMock,
};
await initializePlayground({ config, baseUrl, isEmbed: true, isHeadless: true }, () => {
notifications = {
info: () => undefined,
Expand Down
30 changes: 17 additions & 13 deletions src/livecodes/i18n/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ParseKeys, CustomTypeOptions } from 'i18next';

// eslint-disable-next-line import/no-internal-modules
import { predefinedValues } from '../utils/utils';

Expand Down Expand Up @@ -140,21 +142,23 @@ export const translate = (
});
};

type StringProps<T> = {
[K in keyof T]: T[K] extends string | null ? K : never;
}[keyof T];

type AdditionalDataAttributes = 'data-hint';
export type I18nKeyType = ParseKeys<keyof CustomTypeOptions['resources']>;
export interface I18nInterpolationType {
[key: string]: string | number;
}

export const markElementForTranslation = <T extends HTMLElement>(
elem: T,
key: string,
props?: Array<StringProps<T> | 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) => {
Expand Down

0 comments on commit d01bab0

Please sign in to comment.