Complete runtime API implementation
This commit is contained in:
parent
40ebbdebeb
commit
969744f4cb
@ -140,13 +140,15 @@ async function generateI18nMessages() {
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(messagesDirPath, "LanguageTag.ts"),
|
||||
pathJoin(messagesDirPath, "types.ts"),
|
||||
Buffer.from(
|
||||
[
|
||||
``,
|
||||
`export const languageTags = ${JSON.stringify(languages, null, 2)} as const;`,
|
||||
``,
|
||||
`export type LanguageTag = typeof languageTags[number];`,
|
||||
``,
|
||||
`export type MessageKey = keyof typeof import("./en")["default"];`,
|
||||
``
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
|
@ -29,9 +29,7 @@ export function generateMessageProperties(params: {
|
||||
Object.fromEntries(
|
||||
fs
|
||||
.readdirSync(baseMessagesDirPath)
|
||||
.filter(
|
||||
basename => basename !== "index.ts" && basename !== "LanguageTag.ts"
|
||||
)
|
||||
.filter(basename => basename !== "index.ts" && basename !== "types.ts")
|
||||
.map(basename => ({
|
||||
languageTag: basename.replace(/\.ts$/, ""),
|
||||
filePath: pathJoin(baseMessagesDirPath, basename)
|
||||
|
@ -1,6 +1,8 @@
|
||||
import type { GenericI18n } from "./GenericI18n";
|
||||
import type { LanguageTag } from "./messages_defaultSet/LanguageTag";
|
||||
import type { MessageKey_defaultSet, KcContextLike } from "./i18n";
|
||||
export type { MessageKey_defaultSet, KcContextLike };
|
||||
export type I18n = GenericI18n<MessageKey_defaultSet, LanguageTag>;
|
||||
export { createUseI18n, i18nApi } from "./useI18n";
|
||||
export * from "./withJsx";
|
||||
import type { GenericI18n } from "./withJsx/GenericI18n";
|
||||
import type {
|
||||
LanguageTag as LanguageTag_defaultSet,
|
||||
MessageKey as MessageKey_defaultSet
|
||||
} from "./messages_defaultSet/types";
|
||||
/** INTERNAL: DO NOT IMPORT THIS */
|
||||
export type I18n = GenericI18n<MessageKey_defaultSet, LanguageTag_defaultSet>;
|
||||
|
60
src/login/i18n/noJsx/GenericI18n_noJsx.ts
Normal file
60
src/login/i18n/noJsx/GenericI18n_noJsx.ts
Normal file
@ -0,0 +1,60 @@
|
||||
export type GenericI18n_noJsx<MessageKey extends string, LanguageTag extends string> = {
|
||||
/**
|
||||
* e.g: "en", "fr", "zh-CN"
|
||||
*
|
||||
* The current language
|
||||
*/
|
||||
currentLanguageTag: LanguageTag;
|
||||
/**
|
||||
* Redirect to this url to change the language.
|
||||
* After reload currentLanguageTag === newLanguageTag
|
||||
*/
|
||||
getChangeLocaleUrl: (newLanguageTag: string /*LanguageTag*/) => string;
|
||||
/**
|
||||
* e.g. "en" => "English", "fr" => "Français", ...
|
||||
*
|
||||
* Used to render a select that enable user to switch language.
|
||||
* ex: https://user-images.githubusercontent.com/6702424/186044799-38801eec-4e89-483b-81dd-8e9233e8c0eb.png
|
||||
* */
|
||||
labelBySupportedLanguageTag: Record<string, string>;
|
||||
/**
|
||||
*
|
||||
* Examples assuming currentLanguageTag === "en"
|
||||
* {
|
||||
* en: {
|
||||
* "access-denied": "Access denied",
|
||||
* "impersonateTitleHtml": "<strong>{0}</strong> Impersonate User",
|
||||
* "bar": "Bar {0}"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* msgStr("access-denied") === "Access denied"
|
||||
* msgStr("not-a-message-key") Throws an error
|
||||
* msgStr("impersonateTitleHtml", "Foo") === "<strong>Foo</strong> Impersonate User"
|
||||
* msgStr("${bar}", "<strong>c</strong>") === "Bar <strong>XXX</strong>"
|
||||
* The html in the arg is partially escaped for security reasons, it might come from an untrusted source, it's not safe to render it as html.
|
||||
*/
|
||||
msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
|
||||
/**
|
||||
* This is meant to be used when the key argument is variable, something that might have been configured by the user
|
||||
* in the Keycloak admin for example.
|
||||
*
|
||||
* Examples assuming currentLanguageTag === "en"
|
||||
* {
|
||||
* en: {
|
||||
* "access-denied": "Access denied",
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* advancedMsgStr("${access-denied}") === advancedMsgStr("access-denied") === msgStr("access-denied") === "Access denied"
|
||||
* advancedMsgStr("${not-a-message-key}") === advancedMsgStr("not-a-message-key") === "not-a-message-key"
|
||||
*/
|
||||
advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
|
||||
|
||||
/**
|
||||
* Initially the messages are in english (fallback language).
|
||||
* The translations in the current language are being fetched dynamically.
|
||||
* This property is true while the translations are being fetched.
|
||||
*/
|
||||
isFetchingTranslations: boolean;
|
||||
};
|
@ -1,13 +1,14 @@
|
||||
import "keycloakify/tools/Object.fromEntries";
|
||||
import { assert } from "tsafe/assert";
|
||||
import messages_defaultSet_fallbackLanguage from "./messages_defaultSet/en";
|
||||
import { fetchMessages_defaultSet } from "./messages_defaultSet";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import messages_defaultSet_fallbackLanguage from "../messages_defaultSet/en";
|
||||
import { fetchMessages_defaultSet } from "../messages_defaultSet";
|
||||
import type { KcContext } from "../../KcContext";
|
||||
import { FALLBACK_LANGUAGE_TAG } from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
import { is } from "tsafe/is";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { LanguageTag as LanguageTag_defaultSet } from "keycloakify/login/i18n/messages_defaultSet/LanguageTag";
|
||||
import type { LanguageTag as LanguageTag_defaultSet, MessageKey as MessageKey_defaultSet } from "../messages_defaultSet/types";
|
||||
import type { GenericI18n_noJsx } from "./GenericI18n_noJsx";
|
||||
|
||||
export type KcContextLike = {
|
||||
themeName: string;
|
||||
@ -22,69 +23,6 @@ export type KcContextLike = {
|
||||
|
||||
assert<KcContext extends KcContextLike ? true : false>();
|
||||
|
||||
export type GenericI18n_noJsx<MessageKey extends string, LanguageTag extends string> = {
|
||||
/**
|
||||
* e.g: "en", "fr", "zh-CN"
|
||||
*
|
||||
* The current language
|
||||
*/
|
||||
currentLanguageTag: LanguageTag;
|
||||
/**
|
||||
* Redirect to this url to change the language.
|
||||
* After reload currentLanguageTag === newLanguageTag
|
||||
*/
|
||||
getChangeLocaleUrl: (newLanguageTag: LanguageTag) => string;
|
||||
/**
|
||||
* e.g. "en" => "English", "fr" => "Français", ...
|
||||
*
|
||||
* Used to render a select that enable user to switch language.
|
||||
* ex: https://user-images.githubusercontent.com/6702424/186044799-38801eec-4e89-483b-81dd-8e9233e8c0eb.png
|
||||
* */
|
||||
labelBySupportedLanguageTag: Record<string, string>;
|
||||
/**
|
||||
*
|
||||
* Examples assuming currentLanguageTag === "en"
|
||||
* {
|
||||
* en: {
|
||||
* "access-denied": "Access denied",
|
||||
* "impersonateTitleHtml": "<strong>{0}</strong> Impersonate User",
|
||||
* "bar": "Bar {0}"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* msgStr("access-denied") === "Access denied"
|
||||
* msgStr("not-a-message-key") Throws an error
|
||||
* msgStr("impersonateTitleHtml", "Foo") === "<strong>Foo</strong> Impersonate User"
|
||||
* msgStr("${bar}", "<strong>c</strong>") === "Bar <strong>XXX</strong>"
|
||||
* The html in the arg is partially escaped for security reasons, it might come from an untrusted source, it's not safe to render it as html.
|
||||
*/
|
||||
msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
|
||||
/**
|
||||
* This is meant to be used when the key argument is variable, something that might have been configured by the user
|
||||
* in the Keycloak admin for example.
|
||||
*
|
||||
* Examples assuming currentLanguageTag === "en"
|
||||
* {
|
||||
* en: {
|
||||
* "access-denied": "Access denied",
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* advancedMsgStr("${access-denied}") === advancedMsgStr("access-denied") === msgStr("access-denied") === "Access denied"
|
||||
* advancedMsgStr("${not-a-message-key}") === advancedMsgStr("not-a-message-key") === "not-a-message-key"
|
||||
*/
|
||||
advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
|
||||
|
||||
/**
|
||||
* Initially the messages are in english (fallback language).
|
||||
* The translations in the current language are being fetched dynamically.
|
||||
* This property is true while the translations are being fetched.
|
||||
*/
|
||||
isFetchingTranslations: boolean;
|
||||
};
|
||||
|
||||
export type MessageKey_defaultSet = keyof typeof messages_defaultSet_fallbackLanguage;
|
||||
|
||||
export type ReturnTypeOfCreateGetI18n<MessageKey_themeDefined extends string, LanguageTag_notInDefaultSet extends string> = {
|
||||
getI18n: (params: { kcContext: KcContextLike }) => {
|
||||
i18n: GenericI18n_noJsx<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag_defaultSet | LanguageTag_notInDefaultSet>;
|
||||
@ -113,7 +51,9 @@ export function createGetI18n<
|
||||
|
||||
type LanguageTag = LanguageTag_defaultSet | LanguageTag_notInDefaultSet;
|
||||
|
||||
type I18n = GenericI18n_noJsx<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag>;
|
||||
type MessageKey = MessageKey_defaultSet | MessageKey_themeDefined;
|
||||
|
||||
type I18n = GenericI18n_noJsx<MessageKey, LanguageTag>;
|
||||
|
||||
type Result = { i18n: I18n; prI18n_currentLanguage: Promise<I18n> | undefined };
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type { LanguageTag as LanguageTag_defaultSet } from "keycloakify/login/i18n/messages_defaultSet/LanguageTag";
|
||||
import {
|
||||
type MessageKey_defaultSet,
|
||||
type ReturnTypeOfCreateGetI18n,
|
||||
createGetI18n
|
||||
} from "./i18n";
|
||||
import type {
|
||||
LanguageTag as LanguageTag_defaultSet,
|
||||
MessageKey as MessageKey_defaultSet
|
||||
} from "keycloakify/login/i18n/messages_defaultSet/types";
|
||||
import { type ReturnTypeOfCreateGetI18n, createGetI18n } from "./getI18n";
|
||||
|
||||
export type I18nInitializer<
|
||||
ThemeName extends string = never,
|
||||
@ -110,26 +109,3 @@ export const i18nInitializer = createI18nInitializer({
|
||||
extraLanguageTranslations: {},
|
||||
messagesByLanguageTag_themeDefined: {}
|
||||
});
|
||||
|
||||
/*
|
||||
const i18n = i18nInitializer
|
||||
.withThemeName<"my-theme-1" | "my-theme-2">()
|
||||
.withExtraLanguages({
|
||||
xx: async () => ({}) as any
|
||||
})
|
||||
.withCustomTranslations({
|
||||
en: {
|
||||
myCustomKey: {
|
||||
"my-theme-1": "my-theme-1-en",
|
||||
"my-theme-2": "my-theme-2-en"
|
||||
}
|
||||
},
|
||||
xx: {
|
||||
myCustomKey: {
|
||||
"my-theme-1": "my-theme-1-xx",
|
||||
"my-theme-2": "my-theme-2-xx"
|
||||
}
|
||||
}
|
||||
})
|
||||
.create();
|
||||
*/
|
2
src/login/i18n/noJsx/index.ts
Normal file
2
src/login/i18n/noJsx/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { i18nInitializer } from "./i18nInitializer";
|
||||
export type { MessageKey as MessageKey_defaultSet } from "../messages_defaultSet/types";
|
@ -1,4 +1,4 @@
|
||||
import type { GenericI18n_noJsx } from "./i18n";
|
||||
import type { GenericI18n_noJsx } from "../noJsx/GenericI18n_noJsx";
|
||||
|
||||
export type GenericI18n<MessageKey extends string, LanguageTag extends string> = GenericI18n_noJsx<MessageKey, LanguageTag> & {
|
||||
msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element;
|
111
src/login/i18n/withJsx/i18nInitializer.ts
Normal file
111
src/login/i18n/withJsx/i18nInitializer.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import type {
|
||||
LanguageTag as LanguageTag_defaultSet,
|
||||
MessageKey as MessageKey_defaultSet
|
||||
} from "../messages_defaultSet/types";
|
||||
import { type ReturnTypeOfCreateUseI18n, createUseI18n } from "../withJsx/useI18n";
|
||||
|
||||
export type I18nInitializer<
|
||||
ThemeName extends string = never,
|
||||
MessageKey_themeDefined extends string = never,
|
||||
LanguageTag_notInDefaultSet extends string = never,
|
||||
ExcludedMethod extends
|
||||
| "withThemeName"
|
||||
| "withExtraLanguages"
|
||||
| "withCustomTranslations" = never
|
||||
> = Omit<
|
||||
{
|
||||
withThemeName: <ThemeName extends string>() => I18nInitializer<
|
||||
ThemeName,
|
||||
MessageKey_themeDefined,
|
||||
LanguageTag_notInDefaultSet,
|
||||
ExcludedMethod | "withThemeName"
|
||||
>;
|
||||
withExtraLanguages: <
|
||||
LanguageTag_notInDefaultSet extends string
|
||||
>(extraLanguageTranslations: {
|
||||
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{
|
||||
default: Record<MessageKey_defaultSet, string>;
|
||||
}>;
|
||||
}) => I18nInitializer<
|
||||
ThemeName,
|
||||
MessageKey_themeDefined,
|
||||
LanguageTag_notInDefaultSet,
|
||||
ExcludedMethod | "withExtraLanguages"
|
||||
>;
|
||||
withCustomTranslations: <MessageKey_themeDefined extends string>(
|
||||
messagesByLanguageTag_themeDefined: Partial<{
|
||||
[LanguageTag in
|
||||
| LanguageTag_defaultSet
|
||||
| LanguageTag_notInDefaultSet]: Record<
|
||||
MessageKey_themeDefined,
|
||||
string | Record<ThemeName, string>
|
||||
>;
|
||||
}>
|
||||
) => I18nInitializer<
|
||||
ThemeName,
|
||||
MessageKey_themeDefined,
|
||||
LanguageTag_notInDefaultSet,
|
||||
ExcludedMethod | "withCustomTranslations"
|
||||
>;
|
||||
create: () => ReturnTypeOfCreateUseI18n<
|
||||
MessageKey_themeDefined,
|
||||
LanguageTag_notInDefaultSet
|
||||
>;
|
||||
},
|
||||
ExcludedMethod
|
||||
>;
|
||||
|
||||
function createI18nInitializer<
|
||||
ThemeName extends string = never,
|
||||
MessageKey_themeDefined extends string = never,
|
||||
LanguageTag_notInDefaultSet extends string = never
|
||||
>(params: {
|
||||
extraLanguageTranslations: {
|
||||
[LanguageTag in LanguageTag_notInDefaultSet]: () => Promise<{
|
||||
default: Record<MessageKey_defaultSet, string>;
|
||||
}>;
|
||||
};
|
||||
messagesByLanguageTag_themeDefined: Partial<{
|
||||
[LanguageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: Record<
|
||||
MessageKey_themeDefined,
|
||||
string | Record<ThemeName, string>
|
||||
>;
|
||||
}>;
|
||||
}): I18nInitializer<ThemeName, MessageKey_themeDefined, LanguageTag_notInDefaultSet> {
|
||||
const i18nInitializer: I18nInitializer<
|
||||
ThemeName,
|
||||
MessageKey_themeDefined,
|
||||
LanguageTag_notInDefaultSet
|
||||
> = {
|
||||
withThemeName: () =>
|
||||
createI18nInitializer({
|
||||
extraLanguageTranslations: params.extraLanguageTranslations,
|
||||
messagesByLanguageTag_themeDefined:
|
||||
params.messagesByLanguageTag_themeDefined as any
|
||||
}),
|
||||
withExtraLanguages: extraLanguageTranslations =>
|
||||
createI18nInitializer({
|
||||
extraLanguageTranslations,
|
||||
messagesByLanguageTag_themeDefined:
|
||||
params.messagesByLanguageTag_themeDefined as any
|
||||
}),
|
||||
withCustomTranslations: messagesByLanguageTag_themeDefined =>
|
||||
createI18nInitializer({
|
||||
extraLanguageTranslations: params.extraLanguageTranslations,
|
||||
messagesByLanguageTag_themeDefined
|
||||
}),
|
||||
create: () =>
|
||||
createUseI18n({
|
||||
extraLanguageTranslations: params.extraLanguageTranslations,
|
||||
messagesByLanguageTag_themeDefined:
|
||||
params.messagesByLanguageTag_themeDefined
|
||||
})
|
||||
};
|
||||
|
||||
return i18nInitializer;
|
||||
}
|
||||
|
||||
export const i18nInitializer = createI18nInitializer({
|
||||
extraLanguageTranslations: {},
|
||||
messagesByLanguageTag_themeDefined: {}
|
||||
});
|
2
src/login/i18n/withJsx/index.ts
Normal file
2
src/login/i18n/withJsx/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { i18nInitializer } from "./i18nInitializer";
|
||||
export type { MessageKey as MessageKey_defaultSet } from "../messages_defaultSet/types";
|
@ -1,32 +1,41 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { createGetI18n, type GenericI18n_noJsx, type KcContextLike, type MessageKey_defaultSet } from "./i18n";
|
||||
import { GenericI18n } from "./GenericI18n";
|
||||
import { createGetI18n, type KcContextLike } from "../noJsx/getI18n";
|
||||
import type { GenericI18n_noJsx } from "../noJsx/GenericI18n_noJsx";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { LanguageTag as LanguageTag_defaultSet } from "keycloakify/login/i18n/messages_defaultSet/LanguageTag";
|
||||
import type { GenericI18n } from "./GenericI18n";
|
||||
import type { LanguageTag as LanguageTag_defaultSet, MessageKey as MessageKey_defaultSet } from "../messages_defaultSet/types";
|
||||
|
||||
export const i18nApi = {
|
||||
withThemeName: <ThemeName extends string>() => ({
|
||||
withTranslations: <MessageKey_themeDefined extends string = never>(messagesByLanguageTag: {
|
||||
[languageTag: string]: { [key in MessageKey_themeDefined]: string | Record<ThemeName, string> };
|
||||
}) => ({
|
||||
create: () => createUseI18n<MessageKey_themeDefined, ThemeName>(messagesByLanguageTag)
|
||||
})
|
||||
})
|
||||
export type ReturnTypeOfCreateUseI18n<MessageKey_themeDefined extends string, LanguageTag_notInDefaultSet extends string> = {
|
||||
useI18n: (params: { kcContext: KcContextLike }) => {
|
||||
i18n: GenericI18n<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag_defaultSet | LanguageTag_notInDefaultSet>;
|
||||
};
|
||||
ofTypeI18n: GenericI18n<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag_defaultSet | LanguageTag_notInDefaultSet>;
|
||||
};
|
||||
|
||||
export function createUseI18n<
|
||||
MessageKey_themeDefined extends string = never,
|
||||
ThemeName extends string = string,
|
||||
LanguageTag extends string = LanguageTag_defaultSet
|
||||
MessageKey_themeDefined extends string = never,
|
||||
LanguageTag_notInDefaultSet extends string = never
|
||||
>(params: {
|
||||
messagesByLanguageTag: {
|
||||
[languageTag: string]: { [key in MessageKey_themeDefined]: string | Record<ThemeName, string> };
|
||||
extraLanguageTranslations: {
|
||||
[languageTag in LanguageTag_notInDefaultSet]: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>;
|
||||
};
|
||||
}) {
|
||||
messagesByLanguageTag_themeDefined: Partial<{
|
||||
[languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: {
|
||||
[key in MessageKey_themeDefined]: string | Record<ThemeName, string>;
|
||||
};
|
||||
}>;
|
||||
}): ReturnTypeOfCreateUseI18n<MessageKey_themeDefined, LanguageTag_notInDefaultSet> {
|
||||
const { extraLanguageTranslations, messagesByLanguageTag_themeDefined } = params;
|
||||
|
||||
type LanguageTag = LanguageTag_defaultSet | LanguageTag_notInDefaultSet;
|
||||
|
||||
type MessageKey = MessageKey_defaultSet | MessageKey_themeDefined;
|
||||
|
||||
type I18n = GenericI18n<MessageKey, LanguageTag>;
|
||||
|
||||
type Result = { i18n: I18n };
|
||||
|
||||
const { withJsx } = (() => {
|
||||
const cache = new WeakMap<GenericI18n_noJsx<MessageKey, LanguageTag>, GenericI18n<MessageKey, LanguageTag>>();
|
||||
|
||||
@ -80,9 +89,9 @@ export function createUseI18n<
|
||||
(styleElement.textContent = `[data-kc-msg] { display: inline-block; }`), document.head.prepend(styleElement);
|
||||
}
|
||||
|
||||
const { getI18n } = createGetI18n({ messagesByLanguageTag, extraLanguageTranslations });
|
||||
const { getI18n } = createGetI18n({ extraLanguageTranslations, messagesByLanguageTag_themeDefined });
|
||||
|
||||
function useI18n(params: { kcContext: KcContextLike }): { i18n: I18n } {
|
||||
function useI18n(params: { kcContext: KcContextLike }): Result {
|
||||
const { kcContext } = params;
|
||||
|
||||
const { i18n, prI18n_currentLanguage } = getI18n({ kcContext });
|
7
test/login/he.ts
Normal file
7
test/login/he.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { MessageKey_defaultSet } from "keycloakify/login/i18n";
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages: Record<MessageKey_defaultSet, string> = null as any;
|
||||
/* spell-checker: enable */
|
||||
|
||||
export default messages;
|
54
test/login/i18n.typelevel-spec.ts
Normal file
54
test/login/i18n.typelevel-spec.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { i18nInitializer } from "keycloakify/login/i18n";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
const { useI18n, ofTypeI18n } = i18nInitializer
|
||||
.withThemeName<"my-theme-1" | "my-theme-2">()
|
||||
.withExtraLanguages({
|
||||
he: () => import("./he")
|
||||
})
|
||||
.withCustomTranslations({
|
||||
en: {
|
||||
myCustomKey1: "my-custom-key-1-en",
|
||||
myCustomKey2: {
|
||||
"my-theme-1": "my-theme-1-en",
|
||||
"my-theme-2": "my-theme-2-en"
|
||||
}
|
||||
},
|
||||
he: {
|
||||
myCustomKey1: "my-custom-key-1-he",
|
||||
myCustomKey2: {
|
||||
"my-theme-1": "my-theme-1-xx",
|
||||
"my-theme-2": "my-theme-2-xx"
|
||||
}
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
type I18n = typeof ofTypeI18n;
|
||||
|
||||
{
|
||||
const { i18n } = useI18n({ kcContext: Reflect<any>() });
|
||||
|
||||
assert<Equals<typeof i18n, I18n>>;
|
||||
}
|
||||
|
||||
{
|
||||
const i18n = Reflect<I18n>();
|
||||
|
||||
const got = i18n.currentLanguageTag;
|
||||
|
||||
type Expected =
|
||||
| import("keycloakify/login/i18n/messages_defaultSet/types").LanguageTag
|
||||
| "he";
|
||||
|
||||
assert<Equals<typeof got, Expected>>;
|
||||
}
|
||||
|
||||
{
|
||||
const i18n = Reflect<I18n>();
|
||||
|
||||
const node = i18n.msg("myCustomKey2");
|
||||
|
||||
assert<Equals<typeof node, JSX.Element>>;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user