Enable to add label to extra message not in the default set

This commit is contained in:
Joseph Garrone
2024-09-21 21:33:04 +02:00
parent 2a3ad58c18
commit 801a5cce17
7 changed files with 179 additions and 76 deletions

View File

@ -13,6 +13,7 @@ import { downloadKeycloakDefaultTheme } from "./shared/downloadKeycloakDefaultTh
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath"; import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
import { deepAssign } from "../src/tools/deepAssign"; import { deepAssign } from "../src/tools/deepAssign";
import { THEME_TYPES } from "../src/bin/shared/constants"; import { THEME_TYPES } from "../src/bin/shared/constants";
import { transformCodebase } from "../src/bin/tools/transformCodebase";
const propertiesParser: any = require("properties-parser"); const propertiesParser: any = require("properties-parser");
if (require.main === module) { if (require.main === module) {

View File

@ -3,6 +3,8 @@ import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "keycloakify/bin/shared/constants
import { id } from "tsafe/id"; import { id } from "tsafe/id";
import type { KcContext } from "./KcContext"; import type { KcContext } from "./KcContext";
import { BASE_URL } from "keycloakify/lib/BASE_URL"; 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`; const resourcesPath = `${BASE_URL}${WELL_KNOWN_DIRECTORY_BASE_NAME.DOT_KEYCLOAKIFY}/account`;
@ -38,35 +40,53 @@ export const kcContextCommonMock: KcContext.Common = {
exists: () => false exists: () => false
}, },
locale: { locale: {
supported: [ supported: (
/* spell-checker: disable */ [
["de", "Deutsch"], /* spell-checker: disable */
["no", "Norsk"], ["de", "Deutsch"],
["ru", "Русский"], ["no", "Norsk"],
["sv", "Svenska"], ["ru", "Русский"],
["pt-BR", "Português (Brasil)"], ["sv", "Svenska"],
["lt", "Lietuvių"], ["pt-BR", "Português (Brasil)"],
["en", "English"], ["lt", "Lietuvių"],
["it", "Italiano"], ["en", "English"],
["fr", "Français"], ["it", "Italiano"],
["zh-CN", "中文简体"], ["fr", "Français"],
["es", "Español"], ["zh-CN", "中文简体"],
["cs", "Čeština"], ["es", "Español"],
["ja", "日本語"], ["cs", "Čeština"],
["sk", "Slovenčina"], ["ja", "日本語"],
["pl", "Polski"], ["sk", "Slovenčina"],
["ca", "Català"], ["pl", "Polski"],
["nl", "Nederlands"], ["ca", "Català"],
["tr", "Türkçe"] ["nl", "Nederlands"],
/* spell-checker: enable */ ["tr", "Türkçe"],
].map( ["ar", "العربية"],
([languageTag, label]) => ["da", "Dansk"],
({ ["fi", "Suomi"],
languageTag, ["hu", "Magyar"],
label, ["lv", "Latviešu"]
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" /* spell-checker: enable */
}) as const ] as const
), ).map(([languageTag, label]) => {
{
type Got = typeof languageTag;
type Expected = LanguageTag;
type Missing = Exclude<Expected, Got>;
type Unexpected = Exclude<Got, Expected>;
assert<Equals<Missing, never>>;
assert<Equals<Unexpected, never>>;
}
return {
languageTag,
label,
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e"
} as const;
}),
currentLanguageTag: "en" currentLanguageTag: "en"
}, },
features: { features: {

View File

@ -7,6 +7,7 @@ import {
import { id } from "tsafe/id"; import { id } from "tsafe/id";
import { assert, type Equals } from "tsafe/assert"; import { assert, type Equals } from "tsafe/assert";
import { BASE_URL } from "keycloakify/lib/BASE_URL"; import { BASE_URL } from "keycloakify/lib/BASE_URL";
import type { LanguageTag } from "keycloakify/login/i18n/messages_defaultSet/types";
const attributesByName = Object.fromEntries( const attributesByName = Object.fromEntries(
id<Attribute[]>([ id<Attribute[]>([
@ -116,35 +117,59 @@ export const kcContextCommonMock: KcContext.Common = {
} }
}, },
locale: { locale: {
supported: [ supported: (
/* spell-checker: disable */ [
["de", "Deutsch"], /* spell-checker: disable */
["no", "Norsk"], ["de", "Deutsch"],
["ru", "Русский"], ["no", "Norsk"],
["sv", "Svenska"], ["ru", "Русский"],
["pt-BR", "Português (Brasil)"], ["sv", "Svenska"],
["lt", "Lietuvių"], ["pt-BR", "Português (Brasil)"],
["en", "English"], ["lt", "Lietuvių"],
["it", "Italiano"], ["en", "English"],
["fr", "Français"], ["it", "Italiano"],
["zh-CN", "中文简体"], ["fr", "Français"],
["es", "Español"], ["zh-CN", "中文简体"],
["cs", "Čeština"], ["es", "Español"],
["ja", "日本語"], ["cs", "Čeština"],
["sk", "Slovenčina"], ["ja", "日本語"],
["pl", "Polski"], ["sk", "Slovenčina"],
["ca", "Català"], ["pl", "Polski"],
["nl", "Nederlands"], ["ca", "Català"],
["tr", "Türkçe"] ["nl", "Nederlands"],
/* spell-checker: enable */ ["tr", "Türkçe"],
].map( ["ar", "العربية"],
([languageTag, label]) => ["da", "Dansk"],
({ ["el", "Ελληνικά"],
languageTag, ["fa", "فارسی"],
label, ["fi", "Suomi"],
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e" ["hu", "Magyar"],
}) as const ["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<Expected, Got>;
type Unexpected = Exclude<Got, Expected>;
assert<Equals<Missing, never>>;
assert<Equals<Unexpected, never>>;
}
return {
languageTag,
label,
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e"
} as const;
}),
currentLanguageTag: "en" currentLanguageTag: "en"
}, },

View File

@ -43,7 +43,10 @@ export function createGetI18n<
LanguageTag_notInDefaultSet extends string = never LanguageTag_notInDefaultSet extends string = never
>(params: { >(params: {
extraLanguageTranslations: { extraLanguageTranslations: {
[languageTag in LanguageTag_notInDefaultSet]: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>; [languageTag in LanguageTag_notInDefaultSet]: {
label: string;
getMessages: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>;
};
}; };
messagesByLanguageTag_themeDefined: Partial<{ messagesByLanguageTag_themeDefined: Partial<{
[languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: { [languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: {
@ -100,7 +103,44 @@ export function createGetI18n<
return targetSupportedLocale.url; 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<Record<string, string>>(
{},
{
get: function (...args) {
const [, languageTag] = args;
if (typeof languageTag !== "string") {
return window.Reflect.get(...args);
}
let label = labelBySupportedLanguageTag[languageTag];
if (label === undefined) {
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(languageTag));
const entry = extraLanguageTranslations[languageTag];
assert(entry !== undefined);
label = entry.label;
}
return label;
}
}
);
})()
}; };
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({ const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({
@ -147,12 +187,14 @@ export function createGetI18n<
if (isEmpty) { if (isEmpty) {
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(currentLanguageTag)); assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(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 = { const i18n_currentLanguage: I18n = {

View File

@ -23,9 +23,12 @@ export type I18nInitializer<
withExtraLanguages: < withExtraLanguages: <
LanguageTag_notInDefaultSet extends string LanguageTag_notInDefaultSet extends string
>(extraLanguageTranslations: { >(extraLanguageTranslations: {
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ [LanguageTag in LanguageTag_notInDefaultSet]: {
default: Record<MessageKey_defaultSet, string>; label: string;
}>; getMessages: () => Promise<{
default: Record<MessageKey_defaultSet, string>;
}>;
};
}) => I18nInitializer< }) => I18nInitializer<
ThemeName, ThemeName,
MessageKey_themeDefined, MessageKey_themeDefined,
@ -61,9 +64,12 @@ function createI18nInitializer<
LanguageTag_notInDefaultSet extends string = never LanguageTag_notInDefaultSet extends string = never
>(params: { >(params: {
extraLanguageTranslations: { extraLanguageTranslations: {
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ [LanguageTag in LanguageTag_notInDefaultSet]: {
default: Record<MessageKey_defaultSet, string>; label: string;
}>; getMessages: () => Promise<{
default: Record<MessageKey_defaultSet, string>;
}>;
};
}; };
messagesByLanguageTag_themeDefined: Partial<{ messagesByLanguageTag_themeDefined: Partial<{
[LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record< [LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record<

View File

@ -23,9 +23,12 @@ export type I18nInitializer<
withExtraLanguages: < withExtraLanguages: <
LanguageTag_notInDefaultSet extends string LanguageTag_notInDefaultSet extends string
>(extraLanguageTranslations: { >(extraLanguageTranslations: {
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ [LanguageTag in LanguageTag_notInDefaultSet]: {
default: Record<MessageKey_defaultSet, string>; label: string;
}>; getMessages: () => Promise<{
default: Record<MessageKey_defaultSet, string>;
}>;
};
}) => I18nInitializer< }) => I18nInitializer<
ThemeName, ThemeName,
MessageKey_themeDefined, MessageKey_themeDefined,
@ -61,9 +64,12 @@ function createI18nInitializer<
LanguageTag_notInDefaultSet extends string = never LanguageTag_notInDefaultSet extends string = never
>(params: { >(params: {
extraLanguageTranslations: { extraLanguageTranslations: {
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{ [LanguageTag in LanguageTag_notInDefaultSet]: {
default: Record<MessageKey_defaultSet, string>; label: string;
}>; getMessages: () => Promise<{
default: Record<MessageKey_defaultSet, string>;
}>;
};
}; };
messagesByLanguageTag_themeDefined: Partial<{ messagesByLanguageTag_themeDefined: Partial<{
[LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record< [LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record<

View File

@ -18,7 +18,10 @@ export function createUseI18n<
LanguageTag_notInDefaultSet extends string = never LanguageTag_notInDefaultSet extends string = never
>(params: { >(params: {
extraLanguageTranslations: { extraLanguageTranslations: {
[languageTag in LanguageTag_notInDefaultSet]: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>; [languageTag in LanguageTag_notInDefaultSet]: {
label: string;
getMessages: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>;
};
}; };
messagesByLanguageTag_themeDefined: Partial<{ messagesByLanguageTag_themeDefined: Partial<{
[languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: { [languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: {