UNPKG

@bemedev/i18n

Version:

Internationalization library for Bemedev projects, providing utilities for managing translations and locale-specific content.

116 lines (113 loc) 4.52 kB
import { decompose } from '@bemedev/decompose'; import { CustomMessage } from './message.js'; const defineTranslation = (...args) => { return new CustomMessage(args[0], args[1] || {}); }; const dt = defineTranslation; const addFn = (main, extensions) => { const out = main; if (extensions) { Object.assign(out, extensions); } return out; }; const innerArgs = (translation, KEY, args) => { const isCustom = translation instanceof CustomMessage; const arg = isCustom ? args?.[KEY] : {}; return arg; }; function getTranslation(locale, translations, key, args) { const translation = getTranslationByKey(translations, key); const argObj = args || {}; if (typeof translation === 'string') { return performSubstitution(locale, translation, argObj, {}); } if (translation instanceof CustomMessage) { const { translate, args } = translation; return performSubstitution(locale, translate, argObj, args); } if (Array.isArray(translation)) { return translation.map((t, index) => { const KEY = `${key}.[${index}]`; return getTranslation(locale, translations, KEY, innerArgs(t, KEY, args)); }); } const isObject = typeof translation === 'object' && translation !== null; if (isObject) { const obj = {}; const entries = Object.entries(translation).map(([k, t]) => { const KEY = `${key}.${k}`; const value = getTranslation(locale, translations, KEY, innerArgs(t, KEY, args)); return [k, value]; }); entries.forEach(([k, v]) => { obj[k] = v; }); return obj; } return undefined; } /** * Retrieves a translation value from a nested language messages object by flattening it and accessing the specified key. * * Use {@link decompose.low} * * @param obj - The language messages object to search in, typically a nested structure of translations. * @param key - The dot-separated key path to the desired translation (e.g., "section.subsection.key"). * @returns The translation value associated with the key, or undefined if not found. */ const getTranslationByKey = (obj, key) => { const decomposed = decompose.low(obj, { start: false, object: 'both', sep: '.', }); return decomposed[key]; }; function performSubstitution(locale, str, args, translationParams) { const entries = Object.entries(args); return entries.reduce((result, [argKey, argValue]) => { const match = result.match(`{${argKey}:?([^}]*)?}`); const [replaceKey, argType] = match; switch (argType) { case 'plural': { const pluralMap = translationParams.plural?.[argKey]; const pluralRules = new Intl.PluralRules(locale, { type: pluralMap?.type, }); const replacement = pluralMap[pluralRules.select(argValue)]; const numberFormatter = new Intl.NumberFormat(locale, translationParams.plural?.[argKey]?.formatter); return result.replace(replaceKey, replacement.replace(`{?}`, numberFormatter.format(argValue))); } case 'enum': { const replacement = translationParams.enum[argKey][argValue]; return result.replace(replaceKey, replacement); } case 'number': { const numberFormat = new Intl.NumberFormat(locale, translationParams.number?.[argKey]); return result.replace(replaceKey, numberFormat.format(argValue)); } case 'list': { const formatter = new Intl.ListFormat(locale, translationParams.list?.[argKey]); return result.replace(replaceKey, formatter.format(argValue)); } case 'date': { const dateFormat = new Intl.DateTimeFormat(locale, translationParams.date?.[argKey]); return result.replace(replaceKey, dateFormat.format(argValue)); } default: return result.replace(replaceKey, String(argValue)); } }, str); } const createConfig = (func) => { const isFunction = typeof func === 'function'; let config; if (isFunction) config = func(defineTranslation); else config = func; return config; }; export { addFn, createConfig, defineTranslation, dt, getTranslation, innerArgs }; //# sourceMappingURL=helpers.js.map