'use strict'; var IntlMessageFormat = require('intl-messageformat'); var React = require('react'); var initializeConfig = require('./initializeConfig-BhfMSHP7.js'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var IntlMessageFormat__default = /*#__PURE__*/_interopDefault(IntlMessageFormat); function setTimeZoneInFormats(formats, timeZone) { if (!formats) return formats; // The only way to set a time zone with `intl-messageformat` is to merge it into the formats // https://github.com/formatjs/formatjs/blob/8256c5271505cf2606e48e3c97ecdd16ede4f1b5/packages/intl/src/message.ts#L15 return Object.keys(formats).reduce((acc, key) => { acc[key] = { timeZone, ...formats[key] }; return acc; }, {}); } /** * `intl-messageformat` uses separate keys for `date` and `time`, but there's * only one native API: `Intl.DateTimeFormat`. Additionally you might want to * include both a time and a date in a value, therefore the separation doesn't * seem so useful. We offer a single `dateTime` namespace instead, but we have * to convert the format before `intl-messageformat` can be used. */ function convertFormatsToIntlMessageFormat(formats, timeZone) { const formatsWithTimeZone = timeZone ? { ...formats, dateTime: setTimeZoneInFormats(formats.dateTime, timeZone) } : formats; const mfDateDefaults = IntlMessageFormat__default.default.formats.date; const defaultDateFormats = timeZone ? setTimeZoneInFormats(mfDateDefaults, timeZone) : mfDateDefaults; const mfTimeDefaults = IntlMessageFormat__default.default.formats.time; const defaultTimeFormats = timeZone ? setTimeZoneInFormats(mfTimeDefaults, timeZone) : mfTimeDefaults; return { ...formatsWithTimeZone, date: { ...defaultDateFormats, ...formatsWithTimeZone.dateTime }, time: { ...defaultTimeFormats, ...formatsWithTimeZone.dateTime } }; } // Placed here for improved tree shaking. Somehow when this is placed in // `formatters.tsx`, then it can't be shaken off from `next-intl`. function createMessageFormatter(cache, intlFormatters) { const getMessageFormat = initializeConfig.memoFn(function () { return new IntlMessageFormat__default.default(arguments.length <= 0 ? undefined : arguments[0], arguments.length <= 1 ? undefined : arguments[1], arguments.length <= 2 ? undefined : arguments[2], { formatters: intlFormatters, ...(arguments.length <= 3 ? undefined : arguments[3]) }); }, cache.message); return getMessageFormat; } function resolvePath(locale, messages, key, namespace) { const fullKey = initializeConfig.joinPath(namespace, key); if (!messages) { throw new Error("No messages available at `".concat(namespace, "`.") ); } let message = messages; key.split('.').forEach(part => { const next = message[part]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (part == null || next == null) { throw new Error("Could not resolve `".concat(fullKey, "` in messages for locale `").concat(locale, "`.") ); } message = next; }); return message; } function prepareTranslationValues(values) { if (Object.keys(values).length === 0) return undefined; // Workaround for https://github.com/formatjs/formatjs/issues/1467 const transformedValues = {}; Object.keys(values).forEach(key => { let index = 0; const value = values[key]; let transformed; if (typeof value === 'function') { transformed = chunks => { const result = value(chunks); return /*#__PURE__*/React.isValidElement(result) ? /*#__PURE__*/React.cloneElement(result, { key: key + index++ }) : result; }; } else { transformed = value; } transformedValues[key] = transformed; }); return transformedValues; } function getMessagesOrError(locale, messages, namespace) { let onError = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : initializeConfig.defaultOnError; try { if (!messages) { throw new Error("No messages were configured on the provider." ); } const retrievedMessages = namespace ? resolvePath(locale, messages, namespace) : messages; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!retrievedMessages) { throw new Error("No messages for namespace `".concat(namespace, "` found.") ); } return retrievedMessages; } catch (error) { const intlError = new initializeConfig.IntlError(initializeConfig.IntlErrorCode.MISSING_MESSAGE, error.message); onError(intlError); return intlError; } } function getPlainMessage(candidate, values) { if (values) return undefined; const unescapedMessage = candidate.replace(/'([{}])/gi, '$1'); // Placeholders can be in the message if there are default values, // or if the user has forgotten to provide values. In the latter // case we need to compile the message to receive an error. const hasPlaceholders = /<|{/.test(unescapedMessage); if (!hasPlaceholders) { return unescapedMessage; } return undefined; } function createBaseTranslator(config) { const messagesOrError = getMessagesOrError(config.locale, config.messages, config.namespace, config.onError); return createBaseTranslatorImpl({ ...config, messagesOrError }); } function createBaseTranslatorImpl(_ref) { let { cache, defaultTranslationValues, formats: globalFormats, formatters, getMessageFallback = initializeConfig.defaultGetMessageFallback, locale, messagesOrError, namespace, onError, timeZone } = _ref; const hasMessagesError = messagesOrError instanceof initializeConfig.IntlError; function getFallbackFromErrorAndNotify(key, code, message) { const error = new initializeConfig.IntlError(code, message); onError(error); return getMessageFallback({ error, key, namespace }); } function translateBaseFn(/** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */ key, /** Key value pairs for values to interpolate into the message. */ values, /** Provide custom formats for numbers, dates and times. */ formats) { if (hasMessagesError) { // We have already warned about this during render return getMessageFallback({ error: messagesOrError, key, namespace }); } const messages = messagesOrError; let message; try { message = resolvePath(locale, messages, key, namespace); } catch (error) { return getFallbackFromErrorAndNotify(key, initializeConfig.IntlErrorCode.MISSING_MESSAGE, error.message); } if (typeof message === 'object') { let code, errorMessage; if (Array.isArray(message)) { code = initializeConfig.IntlErrorCode.INVALID_MESSAGE; { errorMessage = "Message at `".concat(initializeConfig.joinPath(namespace, key), "` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/messages#arrays-of-messages"); } } else { code = initializeConfig.IntlErrorCode.INSUFFICIENT_PATH; { errorMessage = "Message at `".concat(initializeConfig.joinPath(namespace, key), "` resolved to an object, but only strings are supported. Use a `.` to retrieve nested messages. See https://next-intl.dev/docs/usage/messages#structuring-messages"); } } return getFallbackFromErrorAndNotify(key, code, errorMessage); } let messageFormat; // Hot path that avoids creating an `IntlMessageFormat` instance const plainMessage = getPlainMessage(message, values); if (plainMessage) return plainMessage; // Lazy init the message formatter for better tree // shaking in case message formatting is not used. if (!formatters.getMessageFormat) { formatters.getMessageFormat = createMessageFormatter(cache, formatters); } try { messageFormat = formatters.getMessageFormat(message, locale, convertFormatsToIntlMessageFormat({ ...globalFormats, ...formats }, timeZone), { formatters: { ...formatters, getDateTimeFormat(locales, options) { // Workaround for https://github.com/formatjs/formatjs/issues/4279 return formatters.getDateTimeFormat(locales, { timeZone, ...options }); } } }); } catch (error) { const thrownError = error; return getFallbackFromErrorAndNotify(key, initializeConfig.IntlErrorCode.INVALID_MESSAGE, thrownError.message + ('originalMessage' in thrownError ? " (".concat(thrownError.originalMessage, ")") : '') ); } try { const formattedMessage = messageFormat.format( // @ts-expect-error `intl-messageformat` expects a different format // for rich text elements since a recent minor update. This // needs to be evaluated in detail, possibly also in regards // to be able to format to parts. prepareTranslationValues({ ...defaultTranslationValues, ...values })); if (formattedMessage == null) { throw new Error("Unable to format `".concat(key, "` in ").concat(namespace ? "namespace `".concat(namespace, "`") : 'messages') ); } // Limit the function signature to return strings or React elements return /*#__PURE__*/React.isValidElement(formattedMessage) || // Arrays of React elements Array.isArray(formattedMessage) || typeof formattedMessage === 'string' ? formattedMessage : String(formattedMessage); } catch (error) { return getFallbackFromErrorAndNotify(key, initializeConfig.IntlErrorCode.FORMATTING_ERROR, error.message); } } function translateFn(/** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */ key, /** Key value pairs for values to interpolate into the message. */ values, /** Provide custom formats for numbers, dates and times. */ formats) { const result = translateBaseFn(key, values, formats); if (typeof result !== 'string') { return getFallbackFromErrorAndNotify(key, initializeConfig.IntlErrorCode.INVALID_MESSAGE, "The message `".concat(key, "` in ").concat(namespace ? "namespace `".concat(namespace, "`") : 'messages', " didn't resolve to a string. If you want to format rich text, use `t.rich` instead.") ); } return result; } translateFn.rich = translateBaseFn; // Augment `translateBaseFn` to return plain strings translateFn.markup = (key, values, formats) => { const result = translateBaseFn(key, // @ts-expect-error -- `MarkupTranslationValues` is practically a sub type // of `RichTranslationValues` but TypeScript isn't smart enough here. values, formats); // When only string chunks are provided to the parser, only // strings should be returned here. Note that we need a runtime // check for this since rich text values could be accidentally // inherited from `defaultTranslationValues`. if (typeof result !== 'string') { const error = new initializeConfig.IntlError(initializeConfig.IntlErrorCode.FORMATTING_ERROR, "`t.markup` only accepts functions for formatting that receive and return strings.\n\nE.g. t.markup('markup', {b: (chunks) => `${chunks}`})" ); onError(error); return getMessageFallback({ error, key, namespace }); } return result; }; translateFn.raw = key => { if (hasMessagesError) { // We have already warned about this during render return getMessageFallback({ error: messagesOrError, key, namespace }); } const messages = messagesOrError; try { return resolvePath(locale, messages, key, namespace); } catch (error) { return getFallbackFromErrorAndNotify(key, initializeConfig.IntlErrorCode.MISSING_MESSAGE, error.message); } }; translateFn.has = key => { if (hasMessagesError) { return false; } try { resolvePath(locale, messagesOrError, key, namespace); return true; } catch (_unused) { return false; } }; return translateFn; } /** * For the strictly typed messages to work we have to wrap the namespace into * a mandatory prefix. See https://stackoverflow.com/a/71529575/343045 */ function resolveNamespace(namespace, namespacePrefix) { return namespace === namespacePrefix ? undefined : namespace.slice((namespacePrefix + '.').length); } const SECOND = 1; const MINUTE = SECOND * 60; const HOUR = MINUTE * 60; const DAY = HOUR * 24; const WEEK = DAY * 7; const MONTH = DAY * (365 / 12); // Approximation const QUARTER = MONTH * 3; const YEAR = DAY * 365; const UNIT_SECONDS = { second: SECOND, seconds: SECOND, minute: MINUTE, minutes: MINUTE, hour: HOUR, hours: HOUR, day: DAY, days: DAY, week: WEEK, weeks: WEEK, month: MONTH, months: MONTH, quarter: QUARTER, quarters: QUARTER, year: YEAR, years: YEAR }; function resolveRelativeTimeUnit(seconds) { const absValue = Math.abs(seconds); if (absValue < MINUTE) { return 'second'; } else if (absValue < HOUR) { return 'minute'; } else if (absValue < DAY) { return 'hour'; } else if (absValue < WEEK) { return 'day'; } else if (absValue < MONTH) { return 'week'; } else if (absValue < YEAR) { return 'month'; } return 'year'; } function calculateRelativeTimeValue(seconds, unit) { // We have to round the resulting values, as `Intl.RelativeTimeFormat` // will include fractions like '2.1 hours ago'. return Math.round(seconds / UNIT_SECONDS[unit]); } function createFormatter(_ref) { let { _cache: cache = initializeConfig.createCache(), _formatters: formatters = initializeConfig.createIntlFormatters(cache), formats, locale, now: globalNow, onError = initializeConfig.defaultOnError, timeZone: globalTimeZone } = _ref; function applyTimeZone(options) { var _options; if (!((_options = options) !== null && _options !== void 0 && _options.timeZone)) { if (globalTimeZone) { options = { ...options, timeZone: globalTimeZone }; } else { onError(new initializeConfig.IntlError(initializeConfig.IntlErrorCode.ENVIRONMENT_FALLBACK, "The `timeZone` parameter wasn't provided and there is no global default configured. Consider adding a global default to avoid markup mismatches caused by environment differences. Learn more: https://next-intl.dev/docs/configuration#time-zone" )); } } return options; } function resolveFormatOrOptions(typeFormats, formatOrOptions) { let options; if (typeof formatOrOptions === 'string') { const formatName = formatOrOptions; options = typeFormats === null || typeFormats === void 0 ? void 0 : typeFormats[formatName]; if (!options) { const error = new initializeConfig.IntlError(initializeConfig.IntlErrorCode.MISSING_FORMAT, "Format `".concat(formatName, "` is not available. You can configure it on the provider or provide custom options.") ); onError(error); throw error; } } else { options = formatOrOptions; } return options; } function getFormattedValue(formatOrOptions, typeFormats, formatter, getFallback) { let options; try { options = resolveFormatOrOptions(typeFormats, formatOrOptions); } catch (_unused) { return getFallback(); } try { return formatter(options); } catch (error) { onError(new initializeConfig.IntlError(initializeConfig.IntlErrorCode.FORMATTING_ERROR, error.message)); return getFallback(); } } function dateTime(/** If a number is supplied, this is interpreted as a UTC timestamp. */ value, /** If a time zone is supplied, the `value` is converted to that time zone. * Otherwise the user time zone will be used. */ formatOrOptions) { return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.dateTime, options => { options = applyTimeZone(options); return formatters.getDateTimeFormat(locale, options).format(value); }, () => String(value)); } function dateTimeRange(/** If a number is supplied, this is interpreted as a UTC timestamp. */ start, /** If a number is supplied, this is interpreted as a UTC timestamp. */ end, /** If a time zone is supplied, the values are converted to that time zone. * Otherwise the user time zone will be used. */ formatOrOptions) { return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.dateTime, options => { options = applyTimeZone(options); return formatters.getDateTimeFormat(locale, options).formatRange(start, end); }, () => [dateTime(start), dateTime(end)].join(' – ')); } function number(value, formatOrOptions) { return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.number, options => formatters.getNumberFormat(locale, options).format(value), () => String(value)); } function getGlobalNow() { if (globalNow) { return globalNow; } else { onError(new initializeConfig.IntlError(initializeConfig.IntlErrorCode.ENVIRONMENT_FALLBACK, "The `now` parameter wasn't provided and there is no global default configured. Consider adding a global default to avoid markup mismatches caused by environment differences. Learn more: https://next-intl.dev/docs/configuration#now" )); return new Date(); } } function relativeTime(/** The date time that needs to be formatted. */ date, /** The reference point in time to which `date` will be formatted in relation to. */ nowOrOptions) { try { let nowDate, unit; const opts = {}; if (nowOrOptions instanceof Date || typeof nowOrOptions === 'number') { nowDate = new Date(nowOrOptions); } else if (nowOrOptions) { if (nowOrOptions.now != null) { nowDate = new Date(nowOrOptions.now); } else { nowDate = getGlobalNow(); } unit = nowOrOptions.unit; opts.style = nowOrOptions.style; // @ts-expect-error -- Types are slightly outdated opts.numberingSystem = nowOrOptions.numberingSystem; } if (!nowDate) { nowDate = getGlobalNow(); } const dateDate = new Date(date); const seconds = (dateDate.getTime() - nowDate.getTime()) / 1000; if (!unit) { unit = resolveRelativeTimeUnit(seconds); } // `numeric: 'auto'` can theoretically produce output like "yesterday", // but it only works with integers. E.g. -1 day will produce "yesterday", // but -1.1 days will produce "-1.1 days". Rounding before formatting is // not desired, as the given dates might cross a threshold were the // output isn't correct anymore. Example: 2024-01-08T23:00:00.000Z and // 2024-01-08T01:00:00.000Z would produce "yesterday", which is not the // case. By using `always` we can ensure correct output. The only exception // is the formatting of times <1 second as "now". opts.numeric = unit === 'second' ? 'auto' : 'always'; const value = calculateRelativeTimeValue(seconds, unit); return formatters.getRelativeTimeFormat(locale, opts).format(value, unit); } catch (error) { onError(new initializeConfig.IntlError(initializeConfig.IntlErrorCode.FORMATTING_ERROR, error.message)); return String(date); } } function list(value, formatOrOptions) { const serializedValue = []; const richValues = new Map(); // `formatToParts` only accepts strings, therefore we have to temporarily // replace React elements with a placeholder ID that can be used to retrieve // the original value afterwards. let index = 0; for (const item of value) { let serializedItem; if (typeof item === 'object') { serializedItem = String(index); richValues.set(serializedItem, item); } else { serializedItem = String(item); } serializedValue.push(serializedItem); index++; } return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.list, // @ts-expect-error -- `richValues.size` is used to determine the return type, but TypeScript can't infer the meaning of this correctly options => { const result = formatters.getListFormat(locale, options).formatToParts(serializedValue).map(part => part.type === 'literal' ? part.value : richValues.get(part.value) || part.value); if (richValues.size > 0) { return result; } else { return result.join(''); } }, () => String(value)); } return { dateTime, number, relativeTime, list, dateTimeRange }; } exports.createBaseTranslator = createBaseTranslator; exports.createFormatter = createFormatter; exports.resolveNamespace = resolveNamespace;