UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

314 lines (313 loc) 11 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.combineWithExternalTranslations = combineWithExternalTranslations; exports.default = useTranslation; exports.formatMessage = formatMessage; exports.renderMessage = renderMessage; exports.useAdditionalUtils = useAdditionalUtils; var _hasOwn = _interopRequireDefault(require("core-js-pure/stable/object/has-own.js")); var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js")); var _react = require("react"); var _Context = _interopRequireDefault(require("./Context.js")); var _index = _interopRequireDefault(require("./locales/index.js")); var _componentHelper = require("./component-helper.js"); var _defaults = require("./defaults.js"); var _jsxRuntime = require("react/jsx-runtime"); var _br; function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function useTranslation(messages, args) { const { locale, translation, messageFormatter } = (0, _react.useContext)(_Context.default); const { translations: contextTranslations } = (0, _react.useContext)(_Context.default); const { assignUtils } = useAdditionalUtils(messageFormatter); const { extMessages, fallbackLocale, baseOverride, warnLabel } = (0, _react.useMemo)(() => { var _arg$fallbackLocale; const defaultLocaleKeys = Object.keys(_index.default); const defaultLocale = defaultLocaleKeys[0]; const arg = (0, _componentHelper.isObject)(messages) && ('messages' in messages || 'fallbackLocale' in messages) ? messages : { messages }; return { extMessages: arg.messages, fallbackLocale: (_arg$fallbackLocale = arg.fallbackLocale) !== null && _arg$fallbackLocale !== void 0 ? _arg$fallbackLocale : defaultLocale, baseOverride: arg.base, warnLabel: arg.warnLabel || 'useTranslation' }; }, [messages]); return (0, _react.useMemo)(() => { var _ref; const id = typeof messages === 'string' ? messages : undefined; if (id) { return formatMessage(id, args, translation); } let translationLocale = locale; if (locale.startsWith('en-') && !Object.keys(_index.default).some(l => l === locale)) { translationLocale = 'en-GB'; } const base = assignUtils(combineWithExternalTranslations({ translation: baseOverride || translation, messages: extMessages, locale: translationLocale }), translationLocale); if (!fallbackLocale) { return base; } const explicitMessages = extMessages; let hasExplicitCurrent = false; let currentMessages = undefined; if (explicitMessages && (0, _hasOwn.default)(explicitMessages, locale)) { hasExplicitCurrent = true; currentMessages = explicitMessages[locale]; } else if (contextTranslations && (0, _hasOwn.default)(contextTranslations, locale)) { hasExplicitCurrent = true; currentMessages = contextTranslations[locale]; } const fallbackMessages = (_ref = (explicitMessages === null || explicitMessages === void 0 ? void 0 : explicitMessages[fallbackLocale]) || (contextTranslations === null || contextTranslations === void 0 ? void 0 : contextTranslations[fallbackLocale]) || _index.default[fallbackLocale]) !== null && _ref !== void 0 ? _ref : undefined; if (!fallbackMessages || !hasExplicitCurrent) { return base; } const currentHasContent = (0, _componentHelper.isObject)(currentMessages) && Object.keys(currentMessages).length > 0; if (!currentHasContent) { warnMissing(locale, warnLabel); const obj = generateTranslationKeyReferences('', fallbackMessages); return withUtils(base, obj); } const { result, hasMissing } = mergeMissingKeys(base, fallbackMessages); if (hasMissing) { warnMissing(locale, warnLabel); return withUtils(base, result); } return base; }, [messages, locale, assignUtils, baseOverride, translation, extMessages, fallbackLocale, contextTranslations, args, warnLabel]); } function withUtils(base, obj) { return Object.assign({}, base, obj, { formatMessage: base.formatMessage, renderMessage: base.renderMessage, countries: base.countries }); } function mergeMissingKeys(target, source) { const resultLocal = { ...target }; let hasMissing = false; const keys = Object.keys(source); for (const key of keys) { const sourceValue = source[key]; const targetValue = resultLocal[key]; if ((0, _componentHelper.isObject)(sourceValue)) { if (!targetValue) { resultLocal[key] = generateTranslationKeyReferences(key, sourceValue); hasMissing = true; } else if ((0, _componentHelper.isObject)(targetValue)) { const nested = mergeMissingKeys(targetValue, sourceValue); resultLocal[key] = nested.result; if (nested.hasMissing) { hasMissing = true; } } } else if (targetValue === undefined) { resultLocal[key] = key; hasMissing = true; } } return { result: resultLocal, hasMissing }; } function generateTranslationKeyReferences(baseKey, sourceValue) { if (!(0, _componentHelper.isObject)(sourceValue)) { return baseKey ? baseKey : sourceValue; } const result = {}; const entries = Object.entries(sourceValue); for (const [key, value] of entries) { const translationKey = baseKey ? `${baseKey}.${key}` : key; result[key] = (0, _componentHelper.isObject)(value) ? generateTranslationKeyReferences(translationKey, value) : translationKey; } return result; } function warnMissing(locale, label = 'useTranslation') { (0, _componentHelper.warn)(`${label}: No translations found for locale "${locale}"!`); } function useAdditionalUtils(messageFormatter) { const translationsRef = (0, _react.useRef)(undefined); const localeRef = (0, _react.useRef)(_defaults.LOCALE); const messageFormatterRef = (0, _react.useRef)(messageFormatter); messageFormatterRef.current = messageFormatter; const fM = (0, _react.useCallback)((id, args) => { return formatMessage(id, args, translationsRef.current, localeRef.current, messageFormatterRef.current); }, []); const rM = (0, _react.useCallback)(message => { return renderMessage(message); }, []); const assignUtils = (0, _react.useCallback)((translations, locale) => { translationsRef.current = translations; if (locale) { localeRef.current = locale; } Object.assign(translations, { formatMessage: fM, renderMessage: rM }); return translations; }, [fM, rM]); return { assignUtils }; } function combineWithExternalTranslations({ translation, messages, locale }) { let combined = { ...translation }; if (messages) { if (Object.keys(_index.default).some(locale => messages[locale])) { if (messages[locale]) { combined = messages[locale]; } } for (const key in messages) { combined[key] = { ...translation[key], ...messages[key] }; } } return combined; } function formatMessage(id, args, messages, locale, messageFormatter) { let str = undefined; if (typeof id === 'string') { var _id$includes; let found = false; if (messages[id]) { str = messages[id]; found = true; } else if (id !== null && id !== void 0 && (_id$includes = id.includes) !== null && _id$includes !== void 0 && _id$includes.call(id, '.')) { const keys = id.split('.'); for (const key of keys) { if (messages[key]) { messages = messages[key]; } else { break; } } if (typeof messages === 'string') { str = messages; found = true; } } if (!found && typeof id === 'string') { if (id.includes('.')) { (0, _componentHelper.warn)(`formatMessage: Could not resolve translation key "${id}". It may be missing or incorrectly formatted.`); } str = id; } } else if (typeof id === 'function') { str = id(messages); } if (typeof str === 'string') { var _str$match; if (args && messageFormatter && messageFormatter.isICU(str)) { try { const result = messageFormatter.format(str, args, locale || _defaults.LOCALE); if (Array.isArray(result)) { return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, { children: result.map((part, i) => (0, _jsxRuntime.jsx)(_react.Fragment, { children: part }, i)) }); } return result; } catch (e) { (0, _componentHelper.warn)(`formatMessage: ICU formatting failed for "${typeof id === 'string' ? id : '(function)'}":`, e); return typeof id === 'string' ? id : str; } } let hasTagHandlers = false; for (const t in args) { if (typeof args[t] === 'function' && str.includes(`<${t}>`)) { hasTagHandlers = true; continue; } const value = typeof args[t] === 'function' ? args[t]() : args[t]; const regex = new RegExp(`{${t}}`, 'g'); str = str.replace(regex, value); } const unreplaced = (_str$match = str.match(/\{(\w+)\}/g)) === null || _str$match === void 0 ? void 0 : _str$match.filter(p => p !== '{br}'); if ((unreplaced === null || unreplaced === void 0 ? void 0 : unreplaced.length) > 0) { (0, _componentHelper.warn)(`formatMessage: Unreplaced placeholder(s) ${unreplaced.join(', ')} in "${typeof id === 'string' ? id : '(function)'}".`); } if (hasTagHandlers) { return renderTags(str, args); } } return str !== null && str !== void 0 ? str : id; } function renderMessage(text) { let element = text; if (typeof text === 'string') { element = text.split('{br}'); } if (Array.isArray(element)) { return element.map((item, index) => (0, _jsxRuntime.jsxs)(_react.Fragment, { children: [item, _br || (_br = (0, _jsxRuntime.jsx)("br", {}))] }, index)); } return text; } function renderTags(str, args) { const TAG_RE = /<(\w+)>([\s\S]*?)<\/\1>/; const parts = []; let remaining = str; let match; while ((match = TAG_RE.exec(remaining)) !== null) { const [fullMatch, tagName, content] = match; if (match.index > 0) { (0, _push.default)(parts).call(parts, remaining.slice(0, match.index)); } const handler = args[tagName]; if (typeof handler === 'function') { (0, _push.default)(parts).call(parts, handler(content)); } else { (0, _push.default)(parts).call(parts, fullMatch); } remaining = remaining.slice(match.index + fullMatch.length); } if (remaining) { (0, _push.default)(parts).call(parts, remaining); } if (parts.length === 1 && typeof parts[0] === 'string') { return parts[0]; } return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, { children: parts.map((part, i) => (0, _jsxRuntime.jsx)(_react.Fragment, { children: part }, i)) }); } //# sourceMappingURL=useTranslation.js.map