UNPKG

keycloakify

Version:

Framework to create custom Keycloak UIs

244 lines 11.9 kB
import "../../../tools/Object.fromEntries"; import { assert, is } from "tsafe/assert"; import { extractLastParenthesisContent } from "../../../tools/extractLastParenthesisContent"; import messages_defaultSet_fallbackLanguage from "../messages_defaultSet/en"; import { fetchMessages_defaultSet } from "../messages_defaultSet"; import { FALLBACK_LANGUAGE_TAG } from "../../../bin/shared/constants"; import { id } from "tsafe/id"; import { Reflect } from "tsafe/Reflect"; import { languageTags as languageTags_defaultSet } from "../messages_defaultSet/types"; assert(); export function createGetI18n(params) { const { extraLanguageTranslations, messagesByLanguageTag_themeDefined } = params; Object.keys(extraLanguageTranslations).forEach(languageTag_notInDefaultSet => { if (id(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(" ")); } }); const cachedResultByKcContext = new WeakMap(); function getI18n(params) { var _a, _b, _c; const { kcContext } = params; use_cache: { const cachedResult = cachedResultByKcContext.get(kcContext); if (cachedResult === undefined) { break use_cache; } return cachedResult; } const kcContextLocale = params.kcContext.realm.internationalizationEnabled ? params.kcContext.locale : undefined; { const currentLanguageTag = (_a = kcContextLocale === null || kcContextLocale === void 0 ? void 0 : kcContextLocale.currentLanguageTag) !== null && _a !== void 0 ? _a : FALLBACK_LANGUAGE_TAG; const html = document.querySelector("html"); assert(html !== null); html.lang = currentLanguageTag; const isRtl = (() => { const { rtl } = kcContextLocale !== null && kcContextLocale !== void 0 ? kcContextLocale : {}; if (rtl !== undefined) { return rtl; } return [ /* spell-checker: disable */ // Common RTL languages "ar", "fa", "he", "ur", "ps", "syr", "dv", "ku", "ug", "az", "sd", // Less common RTL languages "yi", "ha", "ks", "bal", "khw", "brh", "tmh", "bgn", "arc", "sam", "prd", "huz", "gbz", "urj" // Urdu in Romanized script (not always RTL, but to account for edge cases) /* spell-checker: enable */ ].includes(currentLanguageTag); })(); html.dir = isRtl ? "rtl" : "ltr"; } const getLanguageLabel = (languageTag) => { form_user_added_languages: { if (!(languageTag in extraLanguageTranslations)) { break form_user_added_languages; } assert(is(languageTag)); const entry = extraLanguageTranslations[languageTag]; return entry.label; } from_server: { if (kcContextLocale === undefined) { break from_server; } const supportedEntry = kcContextLocale.supported.find(entry => entry.languageTag === languageTag); if (supportedEntry === undefined) { break from_server; } const lastParenthesisContent = extractLastParenthesisContent(supportedEntry.label); if (lastParenthesisContent !== undefined) { return lastParenthesisContent; } return supportedEntry.label; } // NOTE: This should never happen return languageTag; }; const currentLanguage = (() => { var _a; const languageTag = id((_a = kcContextLocale === null || kcContextLocale === void 0 ? void 0 : kcContextLocale.currentLanguageTag) !== null && _a !== void 0 ? _a : FALLBACK_LANGUAGE_TAG); return { languageTag, label: getLanguageLabel(languageTag) }; })(); const enabledLanguages = (() => { var _a; const enabledLanguages = []; if (kcContextLocale !== undefined) { for (const entry of (_a = kcContextLocale.supported) !== null && _a !== void 0 ? _a : []) { const languageTag = id(entry.languageTag); enabledLanguages.push({ languageTag, label: getLanguageLabel(languageTag), href: entry.url }); } } if (enabledLanguages.find(({ languageTag }) => languageTag === currentLanguage.languageTag) === undefined) { enabledLanguages.push({ languageTag: currentLanguage.languageTag, label: getLanguageLabel(currentLanguage.languageTag), href: "#" }); } return enabledLanguages; })(); const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory({ themeName: kcContext.themeName, messages_themeDefined: (_c = (_b = messagesByLanguageTag_themeDefined[currentLanguage.languageTag]) !== null && _b !== void 0 ? _b : messagesByLanguageTag_themeDefined[id(FALLBACK_LANGUAGE_TAG)]) !== null && _c !== void 0 ? _c : (() => { const firstLanguageTag = Object.keys(messagesByLanguageTag_themeDefined)[0]; if (firstLanguageTag === undefined) { return undefined; } return messagesByLanguageTag_themeDefined[firstLanguageTag]; })(), messages_fromKcServer: kcContext["x-keycloakify"].messages }); const isCurrentLanguageFallbackLanguage = currentLanguage.languageTag === FALLBACK_LANGUAGE_TAG; const result = { i18n: Object.assign(Object.assign({ currentLanguage, enabledLanguages }, createI18nTranslationFunctions({ messages_defaultSet_currentLanguage: isCurrentLanguageFallbackLanguage ? messages_defaultSet_fallbackLanguage : undefined })), { isFetchingTranslations: !isCurrentLanguageFallbackLanguage }), prI18n_currentLanguage: isCurrentLanguageFallbackLanguage ? undefined : (async () => { const messages_defaultSet_currentLanguage = await (async () => { const currentLanguageTag = currentLanguage.languageTag; const fromDefaultSet = await fetchMessages_defaultSet(currentLanguageTag); const isEmpty = (() => { for (let _key in fromDefaultSet) { return false; } return true; })(); if (isEmpty) { assert(is(currentLanguageTag)); const entry = extraLanguageTranslations[currentLanguageTag]; assert(entry !== undefined); return entry.getMessages().then(({ default: messages }) => messages); } return fromDefaultSet; })(); const i18n_currentLanguage = Object.assign(Object.assign({ currentLanguage, enabledLanguages }, createI18nTranslationFunctions({ messages_defaultSet_currentLanguage })), { 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; })() }; cachedResultByKcContext.set(kcContext, result); return result; } return { getI18n, ofTypeI18n: Reflect() }; } function createI18nTranslationFunctionsFactory(params) { const { themeName, messages_themeDefined, messages_fromKcServer } = params; function createI18nTranslationFunctions(params) { const { messages_defaultSet_currentLanguage } = params; function resolveMsg(props) { var _a, _b, _c, _d, _e; const { key, args } = props; const message = (_d = (_b = (_a = id(messages_fromKcServer)[key]) !== null && _a !== void 0 ? _a : (() => { var _a; const messageOrMap = (_a = id(messages_themeDefined)) === null || _a === void 0 ? void 0 : _a[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; })()) !== null && _b !== void 0 ? _b : (_c = id(messages_defaultSet_currentLanguage)) === null || _c === void 0 ? void 0 : _c[key]) !== null && _d !== void 0 ? _d : id(messages_defaultSet_fallbackLanguage)[key]; if (message === undefined) { return undefined; } const startIndex = (_e = message .match(/{[0-9]+}/g)) === null || _e === void 0 ? void 0 : _e.map(g => g.match(/{([0-9]+)}/)[1]).map(indexStr => parseInt(indexStr)).sort((a, b) => a - b)[0]; if (startIndex === undefined) { // No {0} in message (no arguments expected) return message; } let messageWithArgsInjected = message; args.forEach((arg, i) => { if (arg === undefined) { return; } messageWithArgsInjected = messageWithArgsInjected.replace(new RegExp(`\\{${i + startIndex}\\}`, "g"), arg); }); return messageWithArgsInjected; } function resolveMsgAdvanced(props) { var _a; const { key, args } = props; const match = key.match(/^\$\{(.+)\}$/); return (_a = resolveMsg({ key: match !== null ? match[1] : key, args })) !== null && _a !== void 0 ? _a : key; } return { 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 }) }; } return { createI18nTranslationFunctions }; } //# sourceMappingURL=getI18n.js.map