use-intl 3.25.1 → 4.0.0-beta-00a79d4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core.d.ts +2 -1
- package/dist/esm/development/core.js +82 -0
- package/dist/esm/development/index.js +7 -0
- package/dist/{development/createFormatter-D2v4ATzl.js → esm/development/initializeConfig-DSmAVNvj.js} +277 -129
- package/dist/esm/development/react.js +184 -0
- package/dist/esm/production/core.js +1 -0
- package/dist/esm/production/index.js +1 -0
- package/dist/esm/production/initializeConfig-BE7tJS3N.js +1 -0
- package/dist/esm/production/react.js +1 -0
- package/dist/types/{src/core → core}/AbstractIntlMessages.d.ts +3 -2
- package/dist/types/core/AppConfig.d.ts +25 -0
- package/dist/types/{src/core → core}/DateTimeFormatOptions.d.ts +1 -1
- package/dist/types/{src/core → core}/Formats.d.ts +2 -2
- package/dist/types/core/ICUArgs.d.ts +3 -0
- package/dist/types/core/ICUTags.d.ts +2 -0
- package/dist/types/{src/core → core}/IntlConfig.d.ts +6 -13
- package/dist/types/core/IntlError.d.ts +6 -0
- package/dist/types/{src/core/IntlError.d.ts → core/IntlErrorCode.d.ts} +2 -6
- package/dist/types/core/MessageKeys.d.ts +10 -0
- package/dist/types/core/TranslationValues.d.ts +9 -0
- package/dist/types/{src/core → core}/convertFormatsToIntlMessageFormat.d.ts +4 -4
- package/dist/types/{src/core → core}/createBaseTranslator.d.ts +9 -12
- package/dist/types/core/createFormatter.d.ts +28 -0
- package/dist/types/core/createTranslator.d.ts +52 -0
- package/dist/types/core/createTranslatorImpl.d.ts +17 -0
- package/dist/types/{src/core → core}/defaults.d.ts +1 -1
- package/dist/types/{src/core → core}/formatters.d.ts +1 -1
- package/dist/types/core/hasLocale.d.ts +10 -0
- package/dist/types/core/index.d.ts +20 -0
- package/dist/types/{src/core → core}/initializeConfig.d.ts +4 -4
- package/dist/types/core/types.d.ts +3 -0
- package/dist/types/{src/core → core}/validateMessages.d.ts +2 -2
- package/dist/types/core.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/{src/react → react}/IntlContext.d.ts +2 -2
- package/dist/types/react/IntlProvider.d.ts +7 -0
- package/dist/types/react/index.d.ts +7 -0
- package/dist/types/{src/react → react}/useFormatter.d.ts +1 -1
- package/dist/types/{src/react → react}/useIntlContext.d.ts +1 -1
- package/dist/types/react/useLocale.d.ts +2 -0
- package/dist/types/react/useMessages.d.ts +2 -0
- package/dist/types/react/useNow.d.ts +8 -0
- package/dist/types/react/useTimeZone.d.ts +1 -0
- package/dist/types/react/useTranslations.d.ts +12 -0
- package/dist/types/react/useTranslationsImpl.d.ts +9 -0
- package/dist/types/react.d.ts +1 -0
- package/package.json +18 -24
- package/react.d.ts +2 -1
- package/_IntlProvider.d.ts +0 -1
- package/_useLocale.d.ts +0 -1
- package/dist/_IntlProvider.js +0 -7
- package/dist/_useLocale.js +0 -7
- package/dist/core.js +0 -7
- package/dist/development/IntlContext-BKfsnzBx.js +0 -7
- package/dist/development/_IntlProvider.js +0 -61
- package/dist/development/_useLocale-0Rl9uR82.js +0 -19
- package/dist/development/_useLocale.js +0 -11
- package/dist/development/core.js +0 -69
- package/dist/development/index.js +0 -31
- package/dist/development/initializeConfig-BhfMSHP7.js +0 -185
- package/dist/development/react.js +0 -151
- package/dist/esm/IntlContext-DoS4CDM3.js +0 -1
- package/dist/esm/_IntlProvider.js +0 -1
- package/dist/esm/_useLocale-v-ZT5JoE.js +0 -1
- package/dist/esm/_useLocale.js +0 -1
- package/dist/esm/core.js +0 -1
- package/dist/esm/createFormatter-zqPbsiYR.js +0 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/initializeConfig-D2A8plWf.js +0 -1
- package/dist/esm/react.js +0 -1
- package/dist/index.js +0 -7
- package/dist/production/IntlContext-DcFt0tgW.js +0 -1
- package/dist/production/_IntlProvider.js +0 -1
- package/dist/production/_useLocale-CpTrqBDt.js +0 -1
- package/dist/production/_useLocale.js +0 -1
- package/dist/production/core.js +0 -1
- package/dist/production/createFormatter-CZeYe_QF.js +0 -1
- package/dist/production/index.js +0 -1
- package/dist/production/initializeConfig-AbYTngyP.js +0 -1
- package/dist/production/react.js +0 -1
- package/dist/react.js +0 -7
- package/dist/types/src/_IntlProvider.d.ts +0 -1
- package/dist/types/src/_useLocale.d.ts +0 -1
- package/dist/types/src/core/TranslationValues.d.ts +0 -6
- package/dist/types/src/core/createFormatter.d.ts +0 -27
- package/dist/types/src/core/createFormatter.test.d.ts +0 -1
- package/dist/types/src/core/createTranslator.d.ts +0 -71
- package/dist/types/src/core/createTranslator.test.d.ts +0 -1
- package/dist/types/src/core/createTranslatorImpl.d.ts +0 -17
- package/dist/types/src/core/index.d.ts +0 -18
- package/dist/types/src/core/utils/MessageKeys.d.ts +0 -5
- package/dist/types/src/core/utils/NamespaceKeys.d.ts +0 -5
- package/dist/types/src/core/utils/NestedKeyOf.d.ts +0 -4
- package/dist/types/src/core/utils/NestedValueOf.d.ts +0 -2
- package/dist/types/src/core.d.ts +0 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/react/IntlProvider.d.ts +0 -7
- package/dist/types/src/react/IntlProvider.test.d.ts +0 -1
- package/dist/types/src/react/index.d.ts +0 -7
- package/dist/types/src/react/index.test.d.ts +0 -1
- package/dist/types/src/react/useFormatter.test.d.ts +0 -1
- package/dist/types/src/react/useLocale.d.ts +0 -1
- package/dist/types/src/react/useLocale.test.d.ts +0 -1
- package/dist/types/src/react/useMessages.d.ts +0 -2
- package/dist/types/src/react/useMessages.test.d.ts +0 -1
- package/dist/types/src/react/useNow.d.ts +0 -23
- package/dist/types/src/react/useNow.test.d.ts +0 -1
- package/dist/types/src/react/useTimeZone.d.ts +0 -1
- package/dist/types/src/react/useTimeZone.test.d.ts +0 -1
- package/dist/types/src/react/useTranslations.d.ts +0 -62
- package/dist/types/src/react/useTranslations.test.d.ts +0 -1
- package/dist/types/src/react/useTranslationsImpl.d.ts +0 -9
- package/dist/types/src/react.d.ts +0 -1
- package/dist/types/test/setup.d.ts +0 -1
- /package/dist/types/{src/core → core}/NumberFormatOptions.d.ts +0 -0
- /package/dist/types/{src/core → core}/RelativeTimeFormatOptions.d.ts +0 -0
- /package/dist/types/{src/core → core}/TimeZone.d.ts +0 -0
- /package/dist/types/{src/core → core}/joinPath.d.ts +0 -0
- /package/dist/types/{src/core → core}/resolveNamespace.d.ts +0 -0
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
|
+
import { isValidElement, cloneElement } from 'react';
|
|
3
|
+
import { memoize, strategies } from '@formatjs/fast-memoize';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// https://github.com/formatjs/formatjs/blob/8256c5271505cf2606e48e3c97ecdd16ede4f1b5/packages/intl/src/message.ts#L15
|
|
16
|
-
return Object.keys(formats).reduce((acc, key) => {
|
|
17
|
-
acc[key] = {
|
|
18
|
-
timeZone,
|
|
19
|
-
...formats[key]
|
|
20
|
-
};
|
|
21
|
-
return acc;
|
|
22
|
-
}, {});
|
|
5
|
+
class IntlError extends Error {
|
|
6
|
+
constructor(code, originalMessage) {
|
|
7
|
+
let message = code;
|
|
8
|
+
if (originalMessage) {
|
|
9
|
+
message += ': ' + originalMessage;
|
|
10
|
+
}
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
if (originalMessage) {
|
|
14
|
+
this.originalMessage = originalMessage;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
23
17
|
}
|
|
24
18
|
|
|
19
|
+
var IntlErrorCode = /*#__PURE__*/function (IntlErrorCode) {
|
|
20
|
+
IntlErrorCode["MISSING_MESSAGE"] = "MISSING_MESSAGE";
|
|
21
|
+
IntlErrorCode["MISSING_FORMAT"] = "MISSING_FORMAT";
|
|
22
|
+
IntlErrorCode["ENVIRONMENT_FALLBACK"] = "ENVIRONMENT_FALLBACK";
|
|
23
|
+
IntlErrorCode["INSUFFICIENT_PATH"] = "INSUFFICIENT_PATH";
|
|
24
|
+
IntlErrorCode["INVALID_MESSAGE"] = "INVALID_MESSAGE";
|
|
25
|
+
IntlErrorCode["INVALID_KEY"] = "INVALID_KEY";
|
|
26
|
+
IntlErrorCode["FORMATTING_ERROR"] = "FORMATTING_ERROR";
|
|
27
|
+
return IntlErrorCode;
|
|
28
|
+
}(IntlErrorCode || {});
|
|
29
|
+
|
|
25
30
|
/**
|
|
26
31
|
* `intl-messageformat` uses separate keys for `date` and `time`, but there's
|
|
27
32
|
* only one native API: `Intl.DateTimeFormat`. Additionally you might want to
|
|
@@ -29,43 +34,124 @@ function setTimeZoneInFormats(formats, timeZone) {
|
|
|
29
34
|
* seem so useful. We offer a single `dateTime` namespace instead, but we have
|
|
30
35
|
* to convert the format before `intl-messageformat` can be used.
|
|
31
36
|
*/
|
|
32
|
-
function convertFormatsToIntlMessageFormat(
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const defaultTimeFormats = timeZone ? setTimeZoneInFormats(mfTimeDefaults, timeZone) : mfTimeDefaults;
|
|
41
|
-
return {
|
|
42
|
-
...formatsWithTimeZone,
|
|
37
|
+
function convertFormatsToIntlMessageFormat(globalFormats, inlineFormats, timeZone) {
|
|
38
|
+
const mfDateDefaults = IntlMessageFormat.formats.date;
|
|
39
|
+
const mfTimeDefaults = IntlMessageFormat.formats.time;
|
|
40
|
+
const dateTimeFormats = {
|
|
41
|
+
...globalFormats?.dateTime,
|
|
42
|
+
...inlineFormats?.dateTime
|
|
43
|
+
};
|
|
44
|
+
const allFormats = {
|
|
43
45
|
date: {
|
|
44
|
-
...
|
|
45
|
-
...
|
|
46
|
+
...mfDateDefaults,
|
|
47
|
+
...dateTimeFormats
|
|
46
48
|
},
|
|
47
49
|
time: {
|
|
48
|
-
...
|
|
49
|
-
...
|
|
50
|
+
...mfTimeDefaults,
|
|
51
|
+
...dateTimeFormats
|
|
52
|
+
},
|
|
53
|
+
number: {
|
|
54
|
+
...globalFormats?.number,
|
|
55
|
+
...inlineFormats?.number
|
|
56
|
+
}
|
|
57
|
+
// (list is not supported in ICU messages)
|
|
58
|
+
};
|
|
59
|
+
if (timeZone) {
|
|
60
|
+
// The only way to set a time zone with `intl-messageformat` is to merge it into the formats
|
|
61
|
+
// https://github.com/formatjs/formatjs/blob/8256c5271505cf2606e48e3c97ecdd16ede4f1b5/packages/intl/src/message.ts#L15
|
|
62
|
+
['date', 'time'].forEach(property => {
|
|
63
|
+
const formats = allFormats[property];
|
|
64
|
+
for (const [key, value] of Object.entries(formats)) {
|
|
65
|
+
formats[key] = {
|
|
66
|
+
timeZone,
|
|
67
|
+
...value
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return allFormats;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function joinPath(...parts) {
|
|
76
|
+
return parts.filter(Boolean).join('.');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Contains defaults that are used for all entry points into the core.
|
|
81
|
+
* See also `InitializedIntlConfiguration`.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
function defaultGetMessageFallback(props) {
|
|
85
|
+
return joinPath(props.namespace, props.key);
|
|
86
|
+
}
|
|
87
|
+
function defaultOnError(error) {
|
|
88
|
+
console.error(error);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function createCache() {
|
|
92
|
+
return {
|
|
93
|
+
dateTime: {},
|
|
94
|
+
number: {},
|
|
95
|
+
message: {},
|
|
96
|
+
relativeTime: {},
|
|
97
|
+
pluralRules: {},
|
|
98
|
+
list: {},
|
|
99
|
+
displayNames: {}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function createMemoCache(store) {
|
|
103
|
+
return {
|
|
104
|
+
create() {
|
|
105
|
+
return {
|
|
106
|
+
get(key) {
|
|
107
|
+
return store[key];
|
|
108
|
+
},
|
|
109
|
+
set(key, value) {
|
|
110
|
+
store[key] = value;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
50
113
|
}
|
|
51
114
|
};
|
|
52
115
|
}
|
|
116
|
+
function memoFn(fn, cache) {
|
|
117
|
+
return memoize(fn, {
|
|
118
|
+
cache: createMemoCache(cache),
|
|
119
|
+
strategy: strategies.variadic
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function memoConstructor(ConstructorFn, cache) {
|
|
123
|
+
return memoFn((...args) => new ConstructorFn(...args), cache);
|
|
124
|
+
}
|
|
125
|
+
function createIntlFormatters(cache) {
|
|
126
|
+
const getDateTimeFormat = memoConstructor(Intl.DateTimeFormat, cache.dateTime);
|
|
127
|
+
const getNumberFormat = memoConstructor(Intl.NumberFormat, cache.number);
|
|
128
|
+
const getPluralRules = memoConstructor(Intl.PluralRules, cache.pluralRules);
|
|
129
|
+
const getRelativeTimeFormat = memoConstructor(Intl.RelativeTimeFormat, cache.relativeTime);
|
|
130
|
+
const getListFormat = memoConstructor(Intl.ListFormat, cache.list);
|
|
131
|
+
const getDisplayNames = memoConstructor(Intl.DisplayNames, cache.displayNames);
|
|
132
|
+
return {
|
|
133
|
+
getDateTimeFormat,
|
|
134
|
+
getNumberFormat,
|
|
135
|
+
getPluralRules,
|
|
136
|
+
getRelativeTimeFormat,
|
|
137
|
+
getListFormat,
|
|
138
|
+
getDisplayNames
|
|
139
|
+
};
|
|
140
|
+
}
|
|
53
141
|
|
|
54
142
|
// Placed here for improved tree shaking. Somehow when this is placed in
|
|
55
143
|
// `formatters.tsx`, then it can't be shaken off from `next-intl`.
|
|
56
144
|
function createMessageFormatter(cache, intlFormatters) {
|
|
57
|
-
const getMessageFormat =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
}, cache.message);
|
|
145
|
+
const getMessageFormat = memoFn((...args) => new IntlMessageFormat(args[0], args[1], args[2], {
|
|
146
|
+
formatters: intlFormatters,
|
|
147
|
+
...args[3]
|
|
148
|
+
}), cache.message);
|
|
63
149
|
return getMessageFormat;
|
|
64
150
|
}
|
|
65
151
|
function resolvePath(locale, messages, key, namespace) {
|
|
66
|
-
const fullKey =
|
|
152
|
+
const fullKey = joinPath(namespace, key);
|
|
67
153
|
if (!messages) {
|
|
68
|
-
throw new Error(
|
|
154
|
+
throw new Error(`No messages available at \`${namespace}\`.` );
|
|
69
155
|
}
|
|
70
156
|
let message = messages;
|
|
71
157
|
key.split('.').forEach(part => {
|
|
@@ -73,15 +159,13 @@ function resolvePath(locale, messages, key, namespace) {
|
|
|
73
159
|
|
|
74
160
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
75
161
|
if (part == null || next == null) {
|
|
76
|
-
throw new Error(
|
|
162
|
+
throw new Error(`Could not resolve \`${fullKey}\` in messages for locale \`${locale}\`.` );
|
|
77
163
|
}
|
|
78
164
|
message = next;
|
|
79
165
|
});
|
|
80
166
|
return message;
|
|
81
167
|
}
|
|
82
168
|
function prepareTranslationValues(values) {
|
|
83
|
-
if (Object.keys(values).length === 0) return undefined;
|
|
84
|
-
|
|
85
169
|
// Workaround for https://github.com/formatjs/formatjs/issues/1467
|
|
86
170
|
const transformedValues = {};
|
|
87
171
|
Object.keys(values).forEach(key => {
|
|
@@ -91,7 +175,7 @@ function prepareTranslationValues(values) {
|
|
|
91
175
|
if (typeof value === 'function') {
|
|
92
176
|
transformed = chunks => {
|
|
93
177
|
const result = value(chunks);
|
|
94
|
-
return /*#__PURE__*/
|
|
178
|
+
return /*#__PURE__*/isValidElement(result) ? /*#__PURE__*/cloneElement(result, {
|
|
95
179
|
key: key + index++
|
|
96
180
|
}) : result;
|
|
97
181
|
};
|
|
@@ -102,37 +186,38 @@ function prepareTranslationValues(values) {
|
|
|
102
186
|
});
|
|
103
187
|
return transformedValues;
|
|
104
188
|
}
|
|
105
|
-
function getMessagesOrError(locale, messages, namespace) {
|
|
106
|
-
let onError = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : initializeConfig.defaultOnError;
|
|
189
|
+
function getMessagesOrError(locale, messages, namespace, onError = defaultOnError) {
|
|
107
190
|
try {
|
|
108
191
|
if (!messages) {
|
|
109
|
-
throw new Error(
|
|
192
|
+
throw new Error(`No messages were configured on the provider.` );
|
|
110
193
|
}
|
|
111
194
|
const retrievedMessages = namespace ? resolvePath(locale, messages, namespace) : messages;
|
|
112
195
|
|
|
113
196
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
114
197
|
if (!retrievedMessages) {
|
|
115
|
-
throw new Error(
|
|
198
|
+
throw new Error(`No messages for namespace \`${namespace}\` found.` );
|
|
116
199
|
}
|
|
117
200
|
return retrievedMessages;
|
|
118
201
|
} catch (error) {
|
|
119
|
-
const intlError = new
|
|
202
|
+
const intlError = new IntlError(IntlErrorCode.MISSING_MESSAGE, error.message);
|
|
120
203
|
onError(intlError);
|
|
121
204
|
return intlError;
|
|
122
205
|
}
|
|
123
206
|
}
|
|
124
207
|
function getPlainMessage(candidate, values) {
|
|
125
|
-
|
|
126
|
-
|
|
208
|
+
{
|
|
209
|
+
// Keep fast path in development
|
|
210
|
+
if (values) return undefined;
|
|
127
211
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
212
|
+
// Despite potentially no values being available, there can still be
|
|
213
|
+
// placeholders in the message if the user has forgotten to provide
|
|
214
|
+
// values. In this case we compile the message to receive an error.
|
|
215
|
+
const unescapedMessage = candidate.replace(/'([{}])/gi, '$1');
|
|
216
|
+
const hasPlaceholders = /<|{/.test(unescapedMessage);
|
|
217
|
+
if (!hasPlaceholders) {
|
|
218
|
+
return unescapedMessage;
|
|
219
|
+
}
|
|
134
220
|
}
|
|
135
|
-
return undefined;
|
|
136
221
|
}
|
|
137
222
|
function createBaseTranslator(config) {
|
|
138
223
|
const messagesOrError = getMessagesOrError(config.locale, config.messages, config.namespace, config.onError);
|
|
@@ -141,22 +226,20 @@ function createBaseTranslator(config) {
|
|
|
141
226
|
messagesOrError
|
|
142
227
|
});
|
|
143
228
|
}
|
|
144
|
-
function createBaseTranslatorImpl(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
} = _ref;
|
|
157
|
-
const hasMessagesError = messagesOrError instanceof initializeConfig.IntlError;
|
|
229
|
+
function createBaseTranslatorImpl({
|
|
230
|
+
cache,
|
|
231
|
+
formats: globalFormats,
|
|
232
|
+
formatters,
|
|
233
|
+
getMessageFallback = defaultGetMessageFallback,
|
|
234
|
+
locale,
|
|
235
|
+
messagesOrError,
|
|
236
|
+
namespace,
|
|
237
|
+
onError,
|
|
238
|
+
timeZone
|
|
239
|
+
}) {
|
|
240
|
+
const hasMessagesError = messagesOrError instanceof IntlError;
|
|
158
241
|
function getFallbackFromErrorAndNotify(key, code, message) {
|
|
159
|
-
const error = new
|
|
242
|
+
const error = new IntlError(code, message);
|
|
160
243
|
onError(error);
|
|
161
244
|
return getMessageFallback({
|
|
162
245
|
error,
|
|
@@ -181,19 +264,19 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
181
264
|
try {
|
|
182
265
|
message = resolvePath(locale, messages, key, namespace);
|
|
183
266
|
} catch (error) {
|
|
184
|
-
return getFallbackFromErrorAndNotify(key,
|
|
267
|
+
return getFallbackFromErrorAndNotify(key, IntlErrorCode.MISSING_MESSAGE, error.message);
|
|
185
268
|
}
|
|
186
269
|
if (typeof message === 'object') {
|
|
187
270
|
let code, errorMessage;
|
|
188
271
|
if (Array.isArray(message)) {
|
|
189
|
-
code =
|
|
272
|
+
code = IntlErrorCode.INVALID_MESSAGE;
|
|
190
273
|
{
|
|
191
|
-
errorMessage =
|
|
274
|
+
errorMessage = `Message at \`${joinPath(namespace, key)}\` resolved to an array, but only strings are supported. See https://next-intl-docs.vercel.app/docs/usage/messages#arrays-of-messages`;
|
|
192
275
|
}
|
|
193
276
|
} else {
|
|
194
|
-
code =
|
|
277
|
+
code = IntlErrorCode.INSUFFICIENT_PATH;
|
|
195
278
|
{
|
|
196
|
-
errorMessage =
|
|
279
|
+
errorMessage = `Message at \`${joinPath(namespace, key)}\` resolved to an object, but only strings are supported. Use a \`.\` to retrieve nested messages. See https://next-intl-docs.vercel.app/docs/usage/messages#structuring-messages`;
|
|
197
280
|
}
|
|
198
281
|
}
|
|
199
282
|
return getFallbackFromErrorAndNotify(key, code, errorMessage);
|
|
@@ -210,10 +293,7 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
210
293
|
formatters.getMessageFormat = createMessageFormatter(cache, formatters);
|
|
211
294
|
}
|
|
212
295
|
try {
|
|
213
|
-
messageFormat = formatters.getMessageFormat(message, locale, convertFormatsToIntlMessageFormat({
|
|
214
|
-
...globalFormats,
|
|
215
|
-
...formats
|
|
216
|
-
}, timeZone), {
|
|
296
|
+
messageFormat = formatters.getMessageFormat(message, locale, convertFormatsToIntlMessageFormat(globalFormats, formats, timeZone), {
|
|
217
297
|
formatters: {
|
|
218
298
|
...formatters,
|
|
219
299
|
getDateTimeFormat(locales, options) {
|
|
@@ -227,7 +307,7 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
227
307
|
});
|
|
228
308
|
} catch (error) {
|
|
229
309
|
const thrownError = error;
|
|
230
|
-
return getFallbackFromErrorAndNotify(key,
|
|
310
|
+
return getFallbackFromErrorAndNotify(key, IntlErrorCode.INVALID_MESSAGE, thrownError.message + ('originalMessage' in thrownError ? ` (${thrownError.originalMessage})` : '') );
|
|
231
311
|
}
|
|
232
312
|
try {
|
|
233
313
|
const formattedMessage = messageFormat.format(
|
|
@@ -235,20 +315,17 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
235
315
|
// for rich text elements since a recent minor update. This
|
|
236
316
|
// needs to be evaluated in detail, possibly also in regards
|
|
237
317
|
// to be able to format to parts.
|
|
238
|
-
prepareTranslationValues(
|
|
239
|
-
...defaultTranslationValues,
|
|
240
|
-
...values
|
|
241
|
-
}));
|
|
318
|
+
values ? prepareTranslationValues(values) : values);
|
|
242
319
|
if (formattedMessage == null) {
|
|
243
|
-
throw new Error(
|
|
320
|
+
throw new Error(`Unable to format \`${key}\` in ${namespace ? `namespace \`${namespace}\`` : 'messages'}` );
|
|
244
321
|
}
|
|
245
322
|
|
|
246
323
|
// Limit the function signature to return strings or React elements
|
|
247
|
-
return /*#__PURE__*/
|
|
324
|
+
return /*#__PURE__*/isValidElement(formattedMessage) ||
|
|
248
325
|
// Arrays of React elements
|
|
249
326
|
Array.isArray(formattedMessage) || typeof formattedMessage === 'string' ? formattedMessage : String(formattedMessage);
|
|
250
327
|
} catch (error) {
|
|
251
|
-
return getFallbackFromErrorAndNotify(key,
|
|
328
|
+
return getFallbackFromErrorAndNotify(key, IntlErrorCode.FORMATTING_ERROR, error.message);
|
|
252
329
|
}
|
|
253
330
|
}
|
|
254
331
|
function translateFn(/** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */
|
|
@@ -257,7 +334,7 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
257
334
|
formats) {
|
|
258
335
|
const result = translateBaseFn(key, values, formats);
|
|
259
336
|
if (typeof result !== 'string') {
|
|
260
|
-
return getFallbackFromErrorAndNotify(key,
|
|
337
|
+
return getFallbackFromErrorAndNotify(key, IntlErrorCode.INVALID_MESSAGE, `The message \`${key}\` in ${namespace ? `namespace \`${namespace}\`` : 'messages'} didn't resolve to a string. If you want to format rich text, use \`t.rich\` instead.` );
|
|
261
338
|
}
|
|
262
339
|
return result;
|
|
263
340
|
}
|
|
@@ -269,13 +346,8 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
269
346
|
// @ts-expect-error -- `MarkupTranslationValues` is practically a sub type
|
|
270
347
|
// of `RichTranslationValues` but TypeScript isn't smart enough here.
|
|
271
348
|
values, formats);
|
|
272
|
-
|
|
273
|
-
// When only string chunks are provided to the parser, only
|
|
274
|
-
// strings should be returned here. Note that we need a runtime
|
|
275
|
-
// check for this since rich text values could be accidentally
|
|
276
|
-
// inherited from `defaultTranslationValues`.
|
|
277
349
|
if (typeof result !== 'string') {
|
|
278
|
-
const error = new
|
|
350
|
+
const error = new IntlError(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>`})");
|
|
279
351
|
onError(error);
|
|
280
352
|
return getMessageFallback({
|
|
281
353
|
error,
|
|
@@ -298,7 +370,7 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
298
370
|
try {
|
|
299
371
|
return resolvePath(locale, messages, key, namespace);
|
|
300
372
|
} catch (error) {
|
|
301
|
-
return getFallbackFromErrorAndNotify(key,
|
|
373
|
+
return getFallbackFromErrorAndNotify(key, IntlErrorCode.MISSING_MESSAGE, error.message);
|
|
302
374
|
}
|
|
303
375
|
};
|
|
304
376
|
translateFn.has = key => {
|
|
@@ -308,7 +380,7 @@ function createBaseTranslatorImpl(_ref) {
|
|
|
308
380
|
try {
|
|
309
381
|
resolvePath(locale, messagesOrError, key, namespace);
|
|
310
382
|
return true;
|
|
311
|
-
} catch
|
|
383
|
+
} catch {
|
|
312
384
|
return false;
|
|
313
385
|
}
|
|
314
386
|
};
|
|
@@ -371,26 +443,24 @@ function calculateRelativeTimeValue(seconds, unit) {
|
|
|
371
443
|
// will include fractions like '2.1 hours ago'.
|
|
372
444
|
return Math.round(seconds / UNIT_SECONDS[unit]);
|
|
373
445
|
}
|
|
374
|
-
function createFormatter(
|
|
375
|
-
|
|
376
|
-
_cache: cache =
|
|
377
|
-
_formatters: formatters =
|
|
446
|
+
function createFormatter(props) {
|
|
447
|
+
const {
|
|
448
|
+
_cache: cache = createCache(),
|
|
449
|
+
_formatters: formatters = createIntlFormatters(cache),
|
|
378
450
|
formats,
|
|
379
451
|
locale,
|
|
380
|
-
|
|
381
|
-
onError = initializeConfig.defaultOnError,
|
|
452
|
+
onError = defaultOnError,
|
|
382
453
|
timeZone: globalTimeZone
|
|
383
|
-
} =
|
|
454
|
+
} = props;
|
|
384
455
|
function applyTimeZone(options) {
|
|
385
|
-
|
|
386
|
-
if (!((_options = options) !== null && _options !== void 0 && _options.timeZone)) {
|
|
456
|
+
if (!options?.timeZone) {
|
|
387
457
|
if (globalTimeZone) {
|
|
388
458
|
options = {
|
|
389
459
|
...options,
|
|
390
460
|
timeZone: globalTimeZone
|
|
391
461
|
};
|
|
392
462
|
} else {
|
|
393
|
-
onError(new
|
|
463
|
+
onError(new IntlError(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-docs.vercel.app/docs/configuration#time-zone` ));
|
|
394
464
|
}
|
|
395
465
|
}
|
|
396
466
|
return options;
|
|
@@ -399,9 +469,9 @@ function createFormatter(_ref) {
|
|
|
399
469
|
let options;
|
|
400
470
|
if (typeof formatOrOptions === 'string') {
|
|
401
471
|
const formatName = formatOrOptions;
|
|
402
|
-
options = typeFormats
|
|
472
|
+
options = typeFormats?.[formatName];
|
|
403
473
|
if (!options) {
|
|
404
|
-
const error = new
|
|
474
|
+
const error = new IntlError(IntlErrorCode.MISSING_FORMAT, `Format \`${formatName}\` is not available. You can configure it on the provider or provide custom options.` );
|
|
405
475
|
onError(error);
|
|
406
476
|
throw error;
|
|
407
477
|
}
|
|
@@ -414,13 +484,13 @@ function createFormatter(_ref) {
|
|
|
414
484
|
let options;
|
|
415
485
|
try {
|
|
416
486
|
options = resolveFormatOrOptions(typeFormats, formatOrOptions);
|
|
417
|
-
} catch
|
|
487
|
+
} catch {
|
|
418
488
|
return getFallback();
|
|
419
489
|
}
|
|
420
490
|
try {
|
|
421
491
|
return formatter(options);
|
|
422
492
|
} catch (error) {
|
|
423
|
-
onError(new
|
|
493
|
+
onError(new IntlError(IntlErrorCode.FORMATTING_ERROR, error.message));
|
|
424
494
|
return getFallback();
|
|
425
495
|
}
|
|
426
496
|
}
|
|
@@ -429,7 +499,7 @@ function createFormatter(_ref) {
|
|
|
429
499
|
/** If a time zone is supplied, the `value` is converted to that time zone.
|
|
430
500
|
* Otherwise the user time zone will be used. */
|
|
431
501
|
formatOrOptions) {
|
|
432
|
-
return getFormattedValue(formatOrOptions, formats
|
|
502
|
+
return getFormattedValue(formatOrOptions, formats?.dateTime, options => {
|
|
433
503
|
options = applyTimeZone(options);
|
|
434
504
|
return formatters.getDateTimeFormat(locale, options).format(value);
|
|
435
505
|
}, () => String(value));
|
|
@@ -440,24 +510,26 @@ function createFormatter(_ref) {
|
|
|
440
510
|
/** If a time zone is supplied, the values are converted to that time zone.
|
|
441
511
|
* Otherwise the user time zone will be used. */
|
|
442
512
|
formatOrOptions) {
|
|
443
|
-
return getFormattedValue(formatOrOptions, formats
|
|
513
|
+
return getFormattedValue(formatOrOptions, formats?.dateTime, options => {
|
|
444
514
|
options = applyTimeZone(options);
|
|
445
515
|
return formatters.getDateTimeFormat(locale, options).formatRange(start, end);
|
|
446
516
|
}, () => [dateTime(start), dateTime(end)].join(' – '));
|
|
447
517
|
}
|
|
448
518
|
function number(value, formatOrOptions) {
|
|
449
|
-
return getFormattedValue(formatOrOptions, formats
|
|
519
|
+
return getFormattedValue(formatOrOptions, formats?.number, options => formatters.getNumberFormat(locale, options).format(value), () => String(value));
|
|
450
520
|
}
|
|
451
521
|
function getGlobalNow() {
|
|
452
|
-
|
|
453
|
-
|
|
522
|
+
// Only read when necessary to avoid triggering a `dynamicIO` error
|
|
523
|
+
// unnecessarily (`now` is only needed for `format.relativeTime`)
|
|
524
|
+
if (props.now) {
|
|
525
|
+
return props.now;
|
|
454
526
|
} else {
|
|
455
|
-
onError(new
|
|
527
|
+
onError(new IntlError(IntlErrorCode.ENVIRONMENT_FALLBACK, `The \`now\` parameter wasn't provided and there is no global default configured, therefore the current time will be used as a fallback. To avoid markup mismatches caused by environment differences, either provide the \`now\` parameter or configure a global default. Learn more: https://next-intl-docs.vercel.app/docs/configuration#now` ));
|
|
456
528
|
return new Date();
|
|
457
529
|
}
|
|
458
530
|
}
|
|
459
531
|
function relativeTime(/** The date time that needs to be formatted. */
|
|
460
|
-
date, /** The reference point in time to which `date` will be formatted in relation to.
|
|
532
|
+
date, /** The reference point in time to which `date` will be formatted in relation to. If this value is absent, a globally configured `now` value or alternatively the current time will be used. */
|
|
461
533
|
nowOrOptions) {
|
|
462
534
|
try {
|
|
463
535
|
let nowDate, unit;
|
|
@@ -496,7 +568,7 @@ function createFormatter(_ref) {
|
|
|
496
568
|
const value = calculateRelativeTimeValue(seconds, unit);
|
|
497
569
|
return formatters.getRelativeTimeFormat(locale, opts).format(value, unit);
|
|
498
570
|
} catch (error) {
|
|
499
|
-
onError(new
|
|
571
|
+
onError(new IntlError(IntlErrorCode.FORMATTING_ERROR, error.message));
|
|
500
572
|
return String(date);
|
|
501
573
|
}
|
|
502
574
|
}
|
|
@@ -519,7 +591,7 @@ function createFormatter(_ref) {
|
|
|
519
591
|
serializedValue.push(serializedItem);
|
|
520
592
|
index++;
|
|
521
593
|
}
|
|
522
|
-
return getFormattedValue(formatOrOptions, formats
|
|
594
|
+
return getFormattedValue(formatOrOptions, formats?.list,
|
|
523
595
|
// @ts-expect-error -- `richValues.size` is used to determine the return type, but TypeScript can't infer the meaning of this correctly
|
|
524
596
|
options => {
|
|
525
597
|
const result = formatters.getListFormat(locale, options).formatToParts(serializedValue).map(part => part.type === 'literal' ? part.value : richValues.get(part.value) || part.value);
|
|
@@ -539,6 +611,82 @@ function createFormatter(_ref) {
|
|
|
539
611
|
};
|
|
540
612
|
}
|
|
541
613
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
614
|
+
function validateMessagesSegment(messages, invalidKeyLabels, parentPath) {
|
|
615
|
+
Object.entries(messages).forEach(([key, messageOrMessages]) => {
|
|
616
|
+
if (key.includes('.')) {
|
|
617
|
+
let keyLabel = key;
|
|
618
|
+
if (parentPath) keyLabel += ` (at ${parentPath})`;
|
|
619
|
+
invalidKeyLabels.push(keyLabel);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
623
|
+
if (messageOrMessages != null && typeof messageOrMessages === 'object') {
|
|
624
|
+
validateMessagesSegment(messageOrMessages, invalidKeyLabels, joinPath(parentPath, key));
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
function validateMessages(messages, onError) {
|
|
629
|
+
const invalidKeyLabels = [];
|
|
630
|
+
validateMessagesSegment(messages, invalidKeyLabels);
|
|
631
|
+
if (invalidKeyLabels.length > 0) {
|
|
632
|
+
onError(new IntlError(IntlErrorCode.INVALID_KEY, `Namespace keys can not contain the character "." as this is used to express nesting. Please remove it or replace it with another character.
|
|
633
|
+
|
|
634
|
+
Invalid ${invalidKeyLabels.length === 1 ? 'key' : 'keys'}: ${invalidKeyLabels.join(', ')}
|
|
635
|
+
|
|
636
|
+
If you're migrating from a flat structure, you can convert your messages as follows:
|
|
637
|
+
|
|
638
|
+
import {set} from "lodash";
|
|
639
|
+
|
|
640
|
+
const input = {
|
|
641
|
+
"one.one": "1.1",
|
|
642
|
+
"one.two": "1.2",
|
|
643
|
+
"two.one.one": "2.1.1"
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
const output = Object.entries(input).reduce(
|
|
647
|
+
(acc, [key, value]) => set(acc, key, value),
|
|
648
|
+
{}
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
// Output:
|
|
652
|
+
//
|
|
653
|
+
// {
|
|
654
|
+
// "one": {
|
|
655
|
+
// "one": "1.1",
|
|
656
|
+
// "two": "1.2"
|
|
657
|
+
// },
|
|
658
|
+
// "two": {
|
|
659
|
+
// "one": {
|
|
660
|
+
// "one": "2.1.1"
|
|
661
|
+
// }
|
|
662
|
+
// }
|
|
663
|
+
// }
|
|
664
|
+
` ));
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Enhances the incoming props with defaults.
|
|
670
|
+
*/
|
|
671
|
+
function initializeConfig({
|
|
672
|
+
getMessageFallback,
|
|
673
|
+
messages,
|
|
674
|
+
onError,
|
|
675
|
+
...rest
|
|
676
|
+
}) {
|
|
677
|
+
const finalOnError = onError || defaultOnError;
|
|
678
|
+
const finalGetMessageFallback = getMessageFallback || defaultGetMessageFallback;
|
|
679
|
+
{
|
|
680
|
+
if (messages) {
|
|
681
|
+
validateMessages(messages, finalOnError);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
...rest,
|
|
686
|
+
messages,
|
|
687
|
+
onError: finalOnError,
|
|
688
|
+
getMessageFallback: finalGetMessageFallback
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export { IntlError as I, IntlErrorCode as a, createIntlFormatters as b, createFormatter as c, createCache as d, createBaseTranslator as e, defaultGetMessageFallback as f, defaultOnError as g, initializeConfig as i, resolveNamespace as r };
|