owlcub-academy/node_modules/use-intl/dist/development/createFormatter-QqAaZwGD.js

545 lines
21 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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) => `<b>${chunks}</b>`})" );
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;