2024-06-05 21:13:58 +02:00
|
|
|
import "keycloakify/tools/Object.fromEntries";
|
|
|
|
import { assert } from "tsafe/assert";
|
2024-09-21 17:59:16 +02:00
|
|
|
import messages_defaultSet_fallbackLanguage from "../messages_defaultSet/en";
|
|
|
|
import { fetchMessages_defaultSet } from "../messages_defaultSet";
|
|
|
|
import type { KcContext } from "../../KcContext";
|
2024-07-13 19:33:59 +02:00
|
|
|
import { FALLBACK_LANGUAGE_TAG } from "keycloakify/bin/shared/constants";
|
2024-07-13 09:07:11 +02:00
|
|
|
import { id } from "tsafe/id";
|
2024-09-15 16:55:18 +02:00
|
|
|
import { is } from "tsafe/is";
|
|
|
|
import { Reflect } from "tsafe/Reflect";
|
2024-09-21 18:17:43 +02:00
|
|
|
import {
|
|
|
|
type LanguageTag as LanguageTag_defaultSet,
|
|
|
|
type MessageKey as MessageKey_defaultSet,
|
|
|
|
languageTags as languageTags_defaultSet
|
|
|
|
} from "../messages_defaultSet/types";
|
2024-09-21 17:59:16 +02:00
|
|
|
import type { GenericI18n_noJsx } from "./GenericI18n_noJsx";
|
2023-02-25 18:11:23 +01:00
|
|
|
|
|
|
|
export type KcContextLike = {
|
2024-09-15 16:55:18 +02:00
|
|
|
themeName: string;
|
2024-11-06 10:10:27 +01:00
|
|
|
realm: {
|
|
|
|
internationalizationEnabled: boolean;
|
|
|
|
};
|
2023-02-25 18:11:23 +01:00
|
|
|
locale?: {
|
|
|
|
currentLanguageTag: string;
|
|
|
|
supported: { languageTag: string; url: string; label: string }[];
|
2024-10-12 17:30:30 +02:00
|
|
|
rtl?: boolean;
|
2023-02-25 18:11:23 +01:00
|
|
|
};
|
2024-06-21 02:01:32 +02:00
|
|
|
"x-keycloakify": {
|
2024-07-10 22:18:24 +02:00
|
|
|
messages: Record<string, string>;
|
2024-06-21 02:01:32 +02:00
|
|
|
};
|
2023-02-25 18:11:23 +01:00
|
|
|
};
|
|
|
|
|
2023-03-18 06:25:19 +01:00
|
|
|
assert<KcContext extends KcContextLike ? true : false>();
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-09-15 16:55:18 +02:00
|
|
|
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>;
|
|
|
|
prI18n_currentLanguage:
|
|
|
|
| Promise<GenericI18n_noJsx<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag_defaultSet | LanguageTag_notInDefaultSet>>
|
|
|
|
| undefined;
|
|
|
|
};
|
|
|
|
ofTypeI18n: GenericI18n_noJsx<MessageKey_defaultSet | MessageKey_themeDefined, LanguageTag_defaultSet | LanguageTag_notInDefaultSet>;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function createGetI18n<
|
|
|
|
ThemeName extends string = string,
|
|
|
|
MessageKey_themeDefined extends string = never,
|
|
|
|
LanguageTag_notInDefaultSet extends string = never
|
|
|
|
>(params: {
|
|
|
|
extraLanguageTranslations: {
|
2024-09-21 21:33:04 +02:00
|
|
|
[languageTag in LanguageTag_notInDefaultSet]: {
|
|
|
|
label: string;
|
|
|
|
getMessages: () => Promise<{ default: Record<MessageKey_defaultSet, string> }>;
|
|
|
|
};
|
2024-09-15 16:55:18 +02:00
|
|
|
};
|
|
|
|
messagesByLanguageTag_themeDefined: Partial<{
|
|
|
|
[languageTag in LanguageTag_defaultSet | LanguageTag_notInDefaultSet]: {
|
|
|
|
[key in MessageKey_themeDefined]: string | Record<ThemeName, string>;
|
|
|
|
};
|
|
|
|
}>;
|
|
|
|
}): ReturnTypeOfCreateGetI18n<MessageKey_themeDefined, LanguageTag_notInDefaultSet> {
|
|
|
|
const { extraLanguageTranslations, messagesByLanguageTag_themeDefined } = params;
|
|
|
|
|
2024-09-21 18:17:43 +02:00
|
|
|
Object.keys(extraLanguageTranslations).forEach(languageTag_notInDefaultSet => {
|
|
|
|
if (id<readonly string[]>(languageTags_defaultSet).includes(languageTag_notInDefaultSet)) {
|
|
|
|
throw new Error(
|
|
|
|
[
|
|
|
|
`Language "${languageTag_notInDefaultSet}" is already in the default set, you don't need to provide your own base translations for it`,
|
|
|
|
`If you want to override some translations for this language, you can use the "withCustomTranslations" method`
|
|
|
|
].join(" ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-09-15 16:55:18 +02:00
|
|
|
type LanguageTag = LanguageTag_defaultSet | LanguageTag_notInDefaultSet;
|
|
|
|
|
2024-09-21 17:59:16 +02:00
|
|
|
type MessageKey = MessageKey_defaultSet | MessageKey_themeDefined;
|
|
|
|
|
|
|
|
type I18n = GenericI18n_noJsx<MessageKey, LanguageTag>;
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-06-08 17:55:05 +02:00
|
|
|
type Result = { i18n: I18n; prI18n_currentLanguage: Promise<I18n> | undefined };
|
|
|
|
|
|
|
|
const cachedResultByKcContext = new WeakMap<KcContextLike, Result>();
|
|
|
|
|
|
|
|
function getI18n(params: { kcContext: KcContextLike }): Result {
|
2023-03-19 14:03:06 +01:00
|
|
|
const { kcContext } = params;
|
|
|
|
|
2024-06-08 17:55:05 +02:00
|
|
|
use_cache: {
|
|
|
|
const cachedResult = cachedResultByKcContext.get(kcContext);
|
|
|
|
|
|
|
|
if (cachedResult === undefined) {
|
|
|
|
break use_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cachedResult;
|
|
|
|
}
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-11-06 10:10:27 +01:00
|
|
|
const kcContextLocale = params.kcContext.realm.internationalizationEnabled ? params.kcContext.locale : undefined;
|
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
{
|
2024-11-06 10:10:27 +01:00
|
|
|
const currentLanguageTag = kcContextLocale?.currentLanguageTag ?? FALLBACK_LANGUAGE_TAG;
|
2024-09-22 17:14:03 +02:00
|
|
|
const html = document.querySelector("html");
|
|
|
|
assert(html !== null);
|
|
|
|
html.lang = currentLanguageTag;
|
2024-10-12 17:30:30 +02:00
|
|
|
|
|
|
|
const isRtl = (() => {
|
2024-11-06 10:10:27 +01:00
|
|
|
const { rtl } = kcContextLocale ?? {};
|
2024-10-12 17:30:30 +02:00
|
|
|
|
|
|
|
if (rtl !== undefined) {
|
|
|
|
return rtl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [
|
|
|
|
/* spell-checker: disable */
|
|
|
|
// Common RTL languages
|
|
|
|
"ar", // Arabic
|
|
|
|
"fa", // Persian (Farsi)
|
|
|
|
"he", // Hebrew
|
|
|
|
"ur", // Urdu
|
|
|
|
"ps", // Pashto
|
|
|
|
"syr", // Syriac
|
|
|
|
"dv", // Divehi (Maldivian)
|
|
|
|
"ku", // Kurdish (Sorani)
|
|
|
|
"ug", // Uighur
|
|
|
|
"az", // Azerbaijani (Arabic script)
|
|
|
|
"sd", // Sindhi
|
|
|
|
|
|
|
|
// Less common RTL languages
|
|
|
|
"yi", // Yiddish
|
|
|
|
"ha", // Hausa (when written in Arabic script)
|
|
|
|
"ks", // Kashmiri (written in the Perso-Arabic script)
|
|
|
|
"bal", // Balochi (when written in Arabic script)
|
|
|
|
"khw", // Khowar (Chitrali)
|
|
|
|
"brh", // Brahui (when written in Arabic script)
|
|
|
|
"tmh", // Tamashek (some dialects use Arabic script)
|
|
|
|
"bgn", // Western Balochi
|
|
|
|
"arc", // Aramaic
|
|
|
|
"sam", // Samaritan Aramaic
|
|
|
|
"prd", // Parsi-Dari (a dialect of Persian)
|
|
|
|
"huz", // Hazaragi (a dialect of Persian)
|
|
|
|
"gbz", // Zaza (written in Arabic script in some areas)
|
|
|
|
"urj" // Urdu in Romanized script (not always RTL, but to account for edge cases)
|
|
|
|
/* spell-checker: enable */
|
|
|
|
].includes(currentLanguageTag);
|
|
|
|
})();
|
|
|
|
|
|
|
|
html.dir = isRtl ? "rtl" : "ltr";
|
2024-09-22 17:14:03 +02:00
|
|
|
}
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
const getLanguageLabel = (languageTag: LanguageTag) => {
|
|
|
|
form_user_added_languages: {
|
|
|
|
if (!(languageTag in extraLanguageTranslations)) {
|
|
|
|
break form_user_added_languages;
|
|
|
|
}
|
|
|
|
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(languageTag));
|
2023-03-19 14:03:06 +01:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
const entry = extraLanguageTranslations[languageTag];
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
return entry.label;
|
|
|
|
}
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
from_server: {
|
2024-11-06 10:10:27 +01:00
|
|
|
if (kcContextLocale === undefined) {
|
2024-09-22 17:14:03 +02:00
|
|
|
break from_server;
|
2024-09-21 21:33:04 +02:00
|
|
|
}
|
|
|
|
|
2024-11-06 10:10:27 +01:00
|
|
|
const supportedEntry = kcContextLocale.supported.find(entry => entry.languageTag === languageTag);
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
if (supportedEntry === undefined) {
|
|
|
|
break from_server;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cspell: disable-next-line
|
|
|
|
// from "Espagnol (Español)" we want to extract "Español"
|
|
|
|
const match = supportedEntry.label.match(/[^(]+\(([^)]+)\)/);
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
if (match !== null) {
|
|
|
|
return match[1];
|
|
|
|
}
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
return supportedEntry.label;
|
|
|
|
}
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
// NOTE: This should never happen
|
|
|
|
return languageTag;
|
|
|
|
};
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
const currentLanguage: I18n["currentLanguage"] = (() => {
|
2024-11-06 10:10:27 +01:00
|
|
|
const languageTag = id<string>(kcContextLocale?.currentLanguageTag ?? FALLBACK_LANGUAGE_TAG) as LanguageTag;
|
2024-09-22 17:14:03 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
languageTag,
|
|
|
|
label: getLanguageLabel(languageTag)
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
const enabledLanguages: I18n["enabledLanguages"] = (() => {
|
|
|
|
const enabledLanguages: I18n["enabledLanguages"] = [];
|
|
|
|
|
2024-11-06 10:10:27 +01:00
|
|
|
if (kcContextLocale !== undefined) {
|
|
|
|
for (const entry of kcContextLocale.supported ?? []) {
|
2024-09-22 23:46:45 +02:00
|
|
|
const languageTag = id<string>(entry.languageTag) as LanguageTag;
|
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
enabledLanguages.push({
|
2024-09-22 23:46:45 +02:00
|
|
|
languageTag,
|
|
|
|
label: getLanguageLabel(languageTag),
|
|
|
|
href: entry.url
|
2024-09-22 17:14:03 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
if (enabledLanguages.find(({ languageTag }) => languageTag === currentLanguage.languageTag) === undefined) {
|
|
|
|
enabledLanguages.push({
|
|
|
|
languageTag: currentLanguage.languageTag,
|
|
|
|
label: getLanguageLabel(currentLanguage.languageTag),
|
|
|
|
href: "#"
|
|
|
|
});
|
|
|
|
}
|
2024-09-21 21:33:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
return enabledLanguages;
|
|
|
|
})();
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({
|
2024-09-15 16:55:18 +02:00
|
|
|
themeName: kcContext.themeName,
|
2024-07-13 09:07:11 +02:00
|
|
|
messages_themeDefined:
|
2024-09-22 17:14:03 +02:00
|
|
|
messagesByLanguageTag_themeDefined[currentLanguage.languageTag] ??
|
|
|
|
messagesByLanguageTag_themeDefined[id<string>(FALLBACK_LANGUAGE_TAG) as LanguageTag] ??
|
2024-07-13 09:07:11 +02:00
|
|
|
(() => {
|
|
|
|
const firstLanguageTag = Object.keys(messagesByLanguageTag_themeDefined)[0];
|
|
|
|
if (firstLanguageTag === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
2024-09-15 16:55:18 +02:00
|
|
|
return messagesByLanguageTag_themeDefined[firstLanguageTag as LanguageTag];
|
2024-07-13 09:07:11 +02:00
|
|
|
})(),
|
|
|
|
messages_fromKcServer: kcContext["x-keycloakify"].messages
|
2024-06-08 17:55:05 +02:00
|
|
|
});
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-09-22 17:14:03 +02:00
|
|
|
const isCurrentLanguageFallbackLanguage = currentLanguage.languageTag === FALLBACK_LANGUAGE_TAG;
|
2023-03-19 14:03:06 +01:00
|
|
|
|
2024-06-08 17:55:05 +02:00
|
|
|
const result: Result = {
|
|
|
|
i18n: {
|
2024-09-22 17:14:03 +02:00
|
|
|
currentLanguage,
|
|
|
|
enabledLanguages,
|
2024-06-21 02:01:32 +02:00
|
|
|
...createI18nTranslationFunctions({
|
2024-07-13 09:07:11 +02:00
|
|
|
messages_defaultSet_currentLanguage: isCurrentLanguageFallbackLanguage ? messages_defaultSet_fallbackLanguage : undefined
|
2024-06-21 02:01:32 +02:00
|
|
|
}),
|
2024-06-08 17:55:05 +02:00
|
|
|
isFetchingTranslations: !isCurrentLanguageFallbackLanguage
|
|
|
|
},
|
|
|
|
prI18n_currentLanguage: isCurrentLanguageFallbackLanguage
|
|
|
|
? undefined
|
|
|
|
: (async () => {
|
2024-09-15 16:55:18 +02:00
|
|
|
const messages_defaultSet_currentLanguage = await (async () => {
|
2024-09-22 17:14:03 +02:00
|
|
|
const currentLanguageTag = currentLanguage.languageTag;
|
2024-09-15 16:55:18 +02:00
|
|
|
|
|
|
|
const fromDefaultSet = await fetchMessages_defaultSet(currentLanguageTag);
|
|
|
|
|
|
|
|
const isEmpty = (() => {
|
|
|
|
for (let _key in fromDefaultSet) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
})();
|
|
|
|
|
|
|
|
if (isEmpty) {
|
|
|
|
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(currentLanguageTag));
|
|
|
|
|
2024-09-21 21:33:04 +02:00
|
|
|
const entry = extraLanguageTranslations[currentLanguageTag];
|
2024-09-15 16:55:18 +02:00
|
|
|
|
2024-09-21 21:33:04 +02:00
|
|
|
assert(entry !== undefined);
|
2024-09-15 16:55:18 +02:00
|
|
|
|
2024-09-21 21:33:04 +02:00
|
|
|
return entry.getMessages().then(({ default: messages }) => messages);
|
2024-09-15 16:55:18 +02:00
|
|
|
}
|
2024-09-21 21:33:04 +02:00
|
|
|
|
|
|
|
return fromDefaultSet;
|
2024-09-15 16:55:18 +02:00
|
|
|
})();
|
2024-06-08 17:55:05 +02:00
|
|
|
|
|
|
|
const i18n_currentLanguage: I18n = {
|
2024-09-22 17:14:03 +02:00
|
|
|
currentLanguage,
|
|
|
|
enabledLanguages,
|
2024-07-13 09:07:11 +02:00
|
|
|
...createI18nTranslationFunctions({ messages_defaultSet_currentLanguage }),
|
2024-06-08 17:55:05 +02:00
|
|
|
isFetchingTranslations: false
|
|
|
|
};
|
|
|
|
|
|
|
|
// NOTE: This promise.resolve is just because without it we TypeScript
|
|
|
|
// gives a Variable 'result' is used before being assigned. error
|
|
|
|
await Promise.resolve().then(() => {
|
|
|
|
result.i18n = i18n_currentLanguage;
|
|
|
|
result.prI18n_currentLanguage = undefined;
|
|
|
|
});
|
|
|
|
|
|
|
|
return i18n_currentLanguage;
|
|
|
|
})()
|
|
|
|
};
|
2023-03-19 14:03:06 +01:00
|
|
|
|
2024-06-08 17:55:05 +02:00
|
|
|
cachedResultByKcContext.set(kcContext, result);
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-06-08 17:55:05 +02:00
|
|
|
return result;
|
|
|
|
}
|
2023-03-19 14:03:06 +01:00
|
|
|
|
2024-09-15 16:55:18 +02:00
|
|
|
return {
|
|
|
|
getI18n,
|
|
|
|
ofTypeI18n: Reflect<I18n>()
|
|
|
|
};
|
2024-06-08 17:55:05 +02:00
|
|
|
}
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
function createI18nTranslationFunctionsFactory<MessageKey_themeDefined extends string>(params: {
|
2024-09-15 16:55:18 +02:00
|
|
|
themeName: string;
|
|
|
|
messages_themeDefined: Record<MessageKey_themeDefined, string | Record<string, string>> | undefined;
|
2024-07-13 09:07:11 +02:00
|
|
|
messages_fromKcServer: Record<string, string>;
|
2024-06-08 15:50:04 +02:00
|
|
|
}) {
|
2024-09-15 16:55:18 +02:00
|
|
|
const { themeName, messages_themeDefined, messages_fromKcServer } = params;
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-06-08 15:50:04 +02:00
|
|
|
function createI18nTranslationFunctions(params: {
|
2024-07-13 09:07:11 +02:00
|
|
|
messages_defaultSet_currentLanguage: Partial<Record<MessageKey_defaultSet, string>> | undefined;
|
2024-09-15 16:55:18 +02:00
|
|
|
}): Pick<GenericI18n_noJsx<MessageKey_defaultSet | MessageKey_themeDefined, string>, "msgStr" | "advancedMsgStr"> {
|
2024-07-13 09:07:11 +02:00
|
|
|
const { messages_defaultSet_currentLanguage } = params;
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
function resolveMsg(props: { key: string; args: (string | undefined)[] }): string | undefined {
|
|
|
|
const { key, args } = props;
|
2024-06-21 02:01:32 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
const message =
|
|
|
|
id<Record<string, string | undefined>>(messages_fromKcServer)[key] ??
|
2024-09-15 16:55:18 +02:00
|
|
|
(() => {
|
|
|
|
const messageOrMap = id<Record<string, string | Record<string, string> | undefined> | undefined>(messages_themeDefined)?.[key];
|
|
|
|
|
|
|
|
if (messageOrMap === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof messageOrMap === "string") {
|
|
|
|
return messageOrMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
const message = messageOrMap[themeName];
|
|
|
|
|
|
|
|
assert(message !== undefined, `No translation for theme variant "${themeName}" for key "${key}"`);
|
|
|
|
|
|
|
|
return message;
|
|
|
|
})() ??
|
2024-07-13 09:07:11 +02:00
|
|
|
id<Record<string, string | undefined> | undefined>(messages_defaultSet_currentLanguage)?.[key] ??
|
|
|
|
id<Record<string, string | undefined>>(messages_defaultSet_fallbackLanguage)[key];
|
2024-07-10 22:18:24 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
if (message === undefined) {
|
2024-06-08 15:50:04 +02:00
|
|
|
return undefined;
|
2023-02-25 18:11:23 +01:00
|
|
|
}
|
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
const startIndex = message
|
|
|
|
.match(/{[0-9]+}/g)
|
|
|
|
?.map(g => g.match(/{([0-9]+)}/)![1])
|
|
|
|
.map(indexStr => parseInt(indexStr))
|
|
|
|
.sort((a, b) => a - b)[0];
|
2024-05-27 17:18:06 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
if (startIndex === undefined) {
|
|
|
|
// No {0} in message (no arguments expected)
|
|
|
|
return message;
|
2024-06-08 15:50:04 +02:00
|
|
|
}
|
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
let messageWithArgsInjected = message;
|
2024-06-08 15:50:04 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
args.forEach((arg, i) => {
|
|
|
|
if (arg === undefined) {
|
|
|
|
return;
|
2024-06-08 15:50:04 +02:00
|
|
|
}
|
2024-05-27 17:18:06 +02:00
|
|
|
|
2024-09-22 22:18:24 +02:00
|
|
|
messageWithArgsInjected = messageWithArgsInjected.replace(new RegExp(`\\{${i + startIndex}\\}`, "g"), arg);
|
2024-07-13 09:07:11 +02:00
|
|
|
});
|
2024-05-27 17:18:06 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
return messageWithArgsInjected;
|
|
|
|
}
|
2024-05-27 23:44:41 +02:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[] }): string {
|
|
|
|
const { key, args } = props;
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
const match = key.match(/^\$\{(.+)\}$/);
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-07-13 09:07:11 +02:00
|
|
|
return resolveMsg({ key: match !== null ? match[1] : key, args }) ?? key;
|
2024-06-08 15:50:04 +02:00
|
|
|
}
|
2023-02-25 18:11:23 +01:00
|
|
|
|
2024-06-08 15:50:04 +02:00
|
|
|
return {
|
2024-07-13 09:07:11 +02:00
|
|
|
msgStr: (key, ...args) => {
|
|
|
|
const resolvedMessage = resolveMsg({ key, args });
|
|
|
|
assert(resolvedMessage !== undefined, `Message with key "${key}" not found`);
|
|
|
|
return resolvedMessage;
|
|
|
|
},
|
|
|
|
advancedMsgStr: (key, ...args) => resolveMsgAdvanced({ key, args })
|
2024-06-08 15:50:04 +02:00
|
|
|
};
|
2023-02-25 18:11:23 +01:00
|
|
|
}
|
|
|
|
|
2024-06-08 15:50:04 +02:00
|
|
|
return { createI18nTranslationFunctions };
|
2023-02-25 18:11:23 +01:00
|
|
|
}
|