From d44159e982dc162da1064aba9f814c16c5cc2050 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Thu, 11 Apr 2024 21:51:37 +0200 Subject: [PATCH] feat(i18n): add types for translation keys --- .../i18n/locales/ar/language-info.ts | 8 ++--- src/livecodes/i18n/locales/ar/translation.ts | 6 ++-- .../i18n/locales/en/language-info.ts | 15 +++++---- src/livecodes/i18n/locales/en/translation.ts | 13 ++++---- src/livecodes/i18n/locales/models.ts | 31 +++++++++++++++++++ src/livecodes/i18n/locales/template.ts | 15 --------- .../i18n/locales/zh-CN/language-info.ts | 8 ++--- .../i18n/locales/zh-CN/translation.ts | 6 ++-- src/livecodes/i18n/utils.ts | 8 +++-- 9 files changed, 64 insertions(+), 46 deletions(-) create mode 100644 src/livecodes/i18n/locales/models.ts delete mode 100644 src/livecodes/i18n/locales/template.ts diff --git a/src/livecodes/i18n/locales/ar/language-info.ts b/src/livecodes/i18n/locales/ar/language-info.ts index e8f6a5bd3..979277739 100644 --- a/src/livecodes/i18n/locales/ar/language-info.ts +++ b/src/livecodes/i18n/locales/ar/language-info.ts @@ -1,9 +1,9 @@ -import type { I18nTranslation } from "../template"; +import type { I18nTranslation } from '../models'; const languageInfo: I18nTranslation = { - artTemplateDesc: { - textContent: "" - } + artTemplateDesc: { + textContent: '', + }, }; export default languageInfo; diff --git a/src/livecodes/i18n/locales/ar/translation.ts b/src/livecodes/i18n/locales/ar/translation.ts index 9fc191e01..0c622d7cd 100644 --- a/src/livecodes/i18n/locales/ar/translation.ts +++ b/src/livecodes/i18n/locales/ar/translation.ts @@ -1,9 +1,7 @@ -import type { I18nTranslation } from "../template"; +import type { I18nTranslation } from '../models'; const translation: I18nTranslation = { - welcome: { - textContent: "مرحبا" - } + welcome: 'مرحبا', }; export default translation; diff --git a/src/livecodes/i18n/locales/en/language-info.ts b/src/livecodes/i18n/locales/en/language-info.ts index e098e92c7..14f0ba4e6 100644 --- a/src/livecodes/i18n/locales/en/language-info.ts +++ b/src/livecodes/i18n/locales/en/language-info.ts @@ -1,9 +1,12 @@ -import type { I18nTranslation } from "../template"; +import { type I18nTranslationTemplate } from '../models'; -const languageInfo: I18nTranslation = { - artTemplateDesc: { - textContent: "High performance JavaScript templating engine." - } -}; +// This is used as a template for other translations. +// Other translations should be typed like this: +// const languageInfo: I18nTranslation = { /* translation here */ }; +const languageInfo = { + artTemplateDesc: { + textContent: 'High performance JavaScript templating engine.', + }, +} as const satisfies I18nTranslationTemplate; export default languageInfo; diff --git a/src/livecodes/i18n/locales/en/translation.ts b/src/livecodes/i18n/locales/en/translation.ts index cbf95f419..3993c2f42 100644 --- a/src/livecodes/i18n/locales/en/translation.ts +++ b/src/livecodes/i18n/locales/en/translation.ts @@ -1,9 +1,10 @@ -import type { I18nTranslation } from "../template"; +import { type I18nTranslationTemplate } from '../models'; -const translation: I18nTranslation = { - welcome: { - textContent: "Welcome" - } -}; +// This is used as a template for other translations. +// Other translations should be typed like this: +// const translation: I18nTranslation = { /* translation here */ }; +const translation = { + welcome: 'Welcome', +} as const satisfies I18nTranslationTemplate; export default translation; diff --git a/src/livecodes/i18n/locales/models.ts b/src/livecodes/i18n/locales/models.ts new file mode 100644 index 000000000..da4b2baa7 --- /dev/null +++ b/src/livecodes/i18n/locales/models.ts @@ -0,0 +1,31 @@ +/* eslint-disable import/no-internal-modules */ +import type translation from './en/translation'; +import type LangInfoTranslation from './en/language-info'; + +// Report error when no property is provided +type RequireAtLeastOne = { + [K in keyof T]-?: Required> & Partial>>; +}[keyof T]; + +type I18nAttributes = RequireAtLeastOne<{ + textContent?: string; + innerHTML?: string; + title?: string; + 'data-hint'?: string; +}>; + +export interface I18nTranslationTemplate { + [key: string]: I18nAttributes | string; +} + +export type I18nTranslation = RequireAtLeastOne< + { + [key in keyof typeof translation]: (typeof translation)[key] extends I18nAttributes + ? I18nAttributes + : string; + } & { + [key in keyof typeof LangInfoTranslation]: (typeof LangInfoTranslation)[key] extends I18nAttributes + ? I18nAttributes + : string; + } +>; diff --git a/src/livecodes/i18n/locales/template.ts b/src/livecodes/i18n/locales/template.ts deleted file mode 100644 index 7b00741b9..000000000 --- a/src/livecodes/i18n/locales/template.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Report error when no property is provided -type RequireAtLeastOne = { - [K in keyof T]-?: Required> & Partial>> -}[keyof T] - -interface _I18nAttributes { - textContent?: string; - title?: string -} - -export type I18nAttributes = RequireAtLeastOne<_I18nAttributes>; - -export interface I18nTranslation { - [key: string]: I18nAttributes; -} diff --git a/src/livecodes/i18n/locales/zh-CN/language-info.ts b/src/livecodes/i18n/locales/zh-CN/language-info.ts index 43e7d3da0..f4fb6228f 100644 --- a/src/livecodes/i18n/locales/zh-CN/language-info.ts +++ b/src/livecodes/i18n/locales/zh-CN/language-info.ts @@ -1,9 +1,9 @@ -import type { I18nTranslation } from "../template"; +import type { I18nTranslation } from '../models'; const languageInfo: I18nTranslation = { - artTemplateDesc: { - textContent: "高性能 JavaScript 模板引擎。" - } + artTemplateDesc: { + textContent: '高性能 JavaScript 模板引擎。', + }, }; export default languageInfo; diff --git a/src/livecodes/i18n/locales/zh-CN/translation.ts b/src/livecodes/i18n/locales/zh-CN/translation.ts index fb4538bec..09bdc830d 100644 --- a/src/livecodes/i18n/locales/zh-CN/translation.ts +++ b/src/livecodes/i18n/locales/zh-CN/translation.ts @@ -1,9 +1,7 @@ -import type { I18nTranslation } from "../template"; +import type { I18nTranslation } from '../models'; const translation: I18nTranslation = { - welcome: { - textContent: "欢迎" - } + welcome: '欢迎', }; export default translation; diff --git a/src/livecodes/i18n/utils.ts b/src/livecodes/i18n/utils.ts index 0ec85fcf3..f52ec3041 100644 --- a/src/livecodes/i18n/utils.ts +++ b/src/livecodes/i18n/utils.ts @@ -8,11 +8,13 @@ export const translate = ( if (!key) return; const props = (el.dataset.i18nProp || 'textContent').split(' '); props.forEach((prop) => { - if (prop.startsWith("data-")) { + const isObject = typeof i18n.t(key, { returnObjects: true }) === 'object'; + const lookupKey = isObject ? `${key}.${prop}` : key; + if (prop.startsWith('data-')) { prop = prop.slice(5); - el.dataset[prop] = i18n.t(`${key}.${prop}`, el.dataset[prop]!); + el.dataset[prop] = i18n.t(lookupKey, el.dataset[prop]!); } else { - (el as any)[prop] = i18n.t(`${key}.${prop}`, (el as any)[prop]); + (el as any)[prop] = i18n.t(lookupKey, (el as any)[prop]); } }); });