From 801a5cce175270fbc198f3b5d7831a4a69c0c3c4 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 21 Sep 2024 21:33:04 +0200 Subject: [PATCH] Enable to add label to extra message not in the default set --- scripts/generate-i18n-messages.ts | 1 + src/account/KcContext/kcContextMocks.ts | 78 +++++++++++++-------- src/login/KcContext/kcContextMocks.ts | 83 +++++++++++++++-------- src/login/i18n/noJsx/getI18n.tsx | 52 ++++++++++++-- src/login/i18n/noJsx/i18nInitializer.ts | 18 +++-- src/login/i18n/withJsx/i18nInitializer.ts | 18 +++-- src/login/i18n/withJsx/useI18n.tsx | 5 +- 7 files changed, 179 insertions(+), 76 deletions(-) diff --git a/scripts/generate-i18n-messages.ts b/scripts/generate-i18n-messages.ts index 20956464..38d5d020 100644 --- a/scripts/generate-i18n-messages.ts +++ b/scripts/generate-i18n-messages.ts @@ -13,6 +13,7 @@ import { downloadKeycloakDefaultTheme } from "./shared/downloadKeycloakDefaultTh import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath"; import { deepAssign } from "../src/tools/deepAssign"; import { THEME_TYPES } from "../src/bin/shared/constants"; +import { transformCodebase } from "../src/bin/tools/transformCodebase"; const propertiesParser: any = require("properties-parser"); if (require.main === module) { diff --git a/src/account/KcContext/kcContextMocks.ts b/src/account/KcContext/kcContextMocks.ts index a80ed058..ebb93607 100644 --- a/src/account/KcContext/kcContextMocks.ts +++ b/src/account/KcContext/kcContextMocks.ts @@ -3,6 +3,8 @@ import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "keycloakify/bin/shared/constants import { id } from "tsafe/id"; import type { KcContext } from "./KcContext"; import { BASE_URL } from "keycloakify/lib/BASE_URL"; +import { assert, type Equals } from "tsafe/assert"; +import type { LanguageTag } from "keycloakify/account/i18n/messages_defaultSet/types"; const resourcesPath = `${BASE_URL}${WELL_KNOWN_DIRECTORY_BASE_NAME.DOT_KEYCLOAKIFY}/account`; @@ -38,35 +40,53 @@ export const kcContextCommonMock: KcContext.Common = { exists: () => false }, locale: { - supported: [ - /* spell-checker: disable */ - ["de", "Deutsch"], - ["no", "Norsk"], - ["ru", "Русский"], - ["sv", "Svenska"], - ["pt-BR", "Português (Brasil)"], - ["lt", "Lietuvių"], - ["en", "English"], - ["it", "Italiano"], - ["fr", "Français"], - ["zh-CN", "中文简体"], - ["es", "Español"], - ["cs", "Čeština"], - ["ja", "日本語"], - ["sk", "Slovenčina"], - ["pl", "Polski"], - ["ca", "Català"], - ["nl", "Nederlands"], - ["tr", "Türkçe"] - /* spell-checker: enable */ - ].map( - ([languageTag, label]) => - ({ - languageTag, - label, - url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" - }) as const - ), + supported: ( + [ + /* spell-checker: disable */ + ["de", "Deutsch"], + ["no", "Norsk"], + ["ru", "Русский"], + ["sv", "Svenska"], + ["pt-BR", "Português (Brasil)"], + ["lt", "Lietuvių"], + ["en", "English"], + ["it", "Italiano"], + ["fr", "Français"], + ["zh-CN", "中文简体"], + ["es", "Español"], + ["cs", "Čeština"], + ["ja", "日本語"], + ["sk", "Slovenčina"], + ["pl", "Polski"], + ["ca", "Català"], + ["nl", "Nederlands"], + ["tr", "Türkçe"], + ["ar", "العربية"], + ["da", "Dansk"], + ["fi", "Suomi"], + ["hu", "Magyar"], + ["lv", "Latviešu"] + /* spell-checker: enable */ + ] as const + ).map(([languageTag, label]) => { + { + type Got = typeof languageTag; + type Expected = LanguageTag; + + type Missing = Exclude; + type Unexpected = Exclude; + + assert>; + assert>; + } + + return { + languageTag, + label, + url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" + } as const; + }), + currentLanguageTag: "en" }, features: { diff --git a/src/login/KcContext/kcContextMocks.ts b/src/login/KcContext/kcContextMocks.ts index af190a45..37bd2456 100644 --- a/src/login/KcContext/kcContextMocks.ts +++ b/src/login/KcContext/kcContextMocks.ts @@ -7,6 +7,7 @@ import { import { id } from "tsafe/id"; import { assert, type Equals } from "tsafe/assert"; import { BASE_URL } from "keycloakify/lib/BASE_URL"; +import type { LanguageTag } from "keycloakify/login/i18n/messages_defaultSet/types"; const attributesByName = Object.fromEntries( id([ @@ -116,35 +117,59 @@ export const kcContextCommonMock: KcContext.Common = { } }, locale: { - supported: [ - /* spell-checker: disable */ - ["de", "Deutsch"], - ["no", "Norsk"], - ["ru", "Русский"], - ["sv", "Svenska"], - ["pt-BR", "Português (Brasil)"], - ["lt", "Lietuvių"], - ["en", "English"], - ["it", "Italiano"], - ["fr", "Français"], - ["zh-CN", "中文简体"], - ["es", "Español"], - ["cs", "Čeština"], - ["ja", "日本語"], - ["sk", "Slovenčina"], - ["pl", "Polski"], - ["ca", "Català"], - ["nl", "Nederlands"], - ["tr", "Türkçe"] - /* spell-checker: enable */ - ].map( - ([languageTag, label]) => - ({ - languageTag, - label, - url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" - }) as const - ), + supported: ( + [ + /* spell-checker: disable */ + ["de", "Deutsch"], + ["no", "Norsk"], + ["ru", "Русский"], + ["sv", "Svenska"], + ["pt-BR", "Português (Brasil)"], + ["lt", "Lietuvių"], + ["en", "English"], + ["it", "Italiano"], + ["fr", "Français"], + ["zh-CN", "中文简体"], + ["es", "Español"], + ["cs", "Čeština"], + ["ja", "日本語"], + ["sk", "Slovenčina"], + ["pl", "Polski"], + ["ca", "Català"], + ["nl", "Nederlands"], + ["tr", "Türkçe"], + ["ar", "العربية"], + ["da", "Dansk"], + ["el", "Ελληνικά"], + ["fa", "فارسی"], + ["fi", "Suomi"], + ["hu", "Magyar"], + ["ka", "ქართული"], + ["lv", "Latviešu"], + ["pt", "Português"], + ["th", "ไทย"], + ["uk", "Українська"], + ["zh-TW", "中文繁體"] + /* spell-checker: enable */ + ] as const + ).map(([languageTag, label]) => { + { + type Got = typeof languageTag; + type Expected = LanguageTag; + + type Missing = Exclude; + type Unexpected = Exclude; + + assert>; + assert>; + } + + return { + languageTag, + label, + url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" + } as const; + }), currentLanguageTag: "en" }, diff --git a/src/login/i18n/noJsx/getI18n.tsx b/src/login/i18n/noJsx/getI18n.tsx index 679721c2..42fd5c4e 100644 --- a/src/login/i18n/noJsx/getI18n.tsx +++ b/src/login/i18n/noJsx/getI18n.tsx @@ -43,7 +43,10 @@ export function createGetI18n< LanguageTag_notInDefaultSet extends string = never >(params: { extraLanguageTranslations: { - [languageTag in LanguageTag_notInDefaultSet]: () => Promise<{ default: Record }>; + [languageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ default: Record }>; + }; }; messagesByLanguageTag_themeDefined: Partial<{ [languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: { @@ -100,7 +103,44 @@ export function createGetI18n< return targetSupportedLocale.url; }, - labelBySupportedLanguageTag: Object.fromEntries((kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])) + labelBySupportedLanguageTag: (() => { + const labelBySupportedLanguageTag = Object.fromEntries( + (kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]) + ); + + // NOTE: For IE11 + if (typeof Proxy === undefined) { + return labelBySupportedLanguageTag; + } + + // NOTE: This is for convenience in Storybook + return new Proxy>( + {}, + { + get: function (...args) { + const [, languageTag] = args; + + if (typeof languageTag !== "string") { + return window.Reflect.get(...args); + } + + let label = labelBySupportedLanguageTag[languageTag]; + + if (label === undefined) { + assert(is>(languageTag)); + + const entry = extraLanguageTranslations[languageTag]; + + assert(entry !== undefined); + + label = entry.label; + } + + return label; + } + } + ); + })() }; const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory({ @@ -147,12 +187,14 @@ export function createGetI18n< if (isEmpty) { assert(is>(currentLanguageTag)); - const asyncFunction = extraLanguageTranslations[currentLanguageTag]; + const entry = extraLanguageTranslations[currentLanguageTag]; - assert(asyncFunction !== undefined); + assert(entry !== undefined); - return asyncFunction().then(({ default: messages }) => messages); + return entry.getMessages().then(({ default: messages }) => messages); } + + return fromDefaultSet; })(); const i18n_currentLanguage: I18n = { diff --git a/src/login/i18n/noJsx/i18nInitializer.ts b/src/login/i18n/noJsx/i18nInitializer.ts index 28a4378c..3c0f4a19 100644 --- a/src/login/i18n/noJsx/i18nInitializer.ts +++ b/src/login/i18n/noJsx/i18nInitializer.ts @@ -23,9 +23,12 @@ export type I18nInitializer< withExtraLanguages: < LanguageTag_notInDefaultSet extends string >(extraLanguageTranslations: { - [LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ - default: Record; - }>; + [LanguageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ + default: Record; + }>; + }; }) => I18nInitializer< ThemeName, MessageKey_themeDefined, @@ -61,9 +64,12 @@ function createI18nInitializer< LanguageTag_notInDefaultSet extends string = never >(params: { extraLanguageTranslations: { - [LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ - default: Record; - }>; + [LanguageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ + default: Record; + }>; + }; }; messagesByLanguageTag_themeDefined: Partial<{ [LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record< diff --git a/src/login/i18n/withJsx/i18nInitializer.ts b/src/login/i18n/withJsx/i18nInitializer.ts index e7379e8e..fad337e0 100644 --- a/src/login/i18n/withJsx/i18nInitializer.ts +++ b/src/login/i18n/withJsx/i18nInitializer.ts @@ -23,9 +23,12 @@ export type I18nInitializer< withExtraLanguages: < LanguageTag_notInDefaultSet extends string >(extraLanguageTranslations: { - [LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ - default: Record; - }>; + [LanguageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ + default: Record; + }>; + }; }) => I18nInitializer< ThemeName, MessageKey_themeDefined, @@ -61,9 +64,12 @@ function createI18nInitializer< LanguageTag_notInDefaultSet extends string = never >(params: { extraLanguageTranslations: { - [LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ - default: Record; - }>; + [LanguageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ + default: Record; + }>; + }; }; messagesByLanguageTag_themeDefined: Partial<{ [LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record< diff --git a/src/login/i18n/withJsx/useI18n.tsx b/src/login/i18n/withJsx/useI18n.tsx index 37f7f55a..0abc7bb8 100644 --- a/src/login/i18n/withJsx/useI18n.tsx +++ b/src/login/i18n/withJsx/useI18n.tsx @@ -18,7 +18,10 @@ export function createUseI18n< LanguageTag_notInDefaultSet extends string = never >(params: { extraLanguageTranslations: { - [languageTag in LanguageTag_notInDefaultSet]: () => Promise<{ default: Record }>; + [languageTag in LanguageTag_notInDefaultSet]: { + label: string; + getMessages: () => Promise<{ default: Record }>; + }; }; messagesByLanguageTag_themeDefined: Partial<{ [languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: {