use-intl 0.0.0-canary-255c5c2 → 0.0.0-canary-fdaddc5
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 +70 -0
- package/dist/esm/development/index.js +7 -0
- package/dist/{development/createFormatter-QqAaZwGD.js → esm/development/initializeConfig-CRD6euuK.js} +292 -147
- 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-zfMDfl5R.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 +15 -19
- 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 +6 -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 +43 -0
- package/dist/types/core/createTranslator.d.ts +55 -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 +7 -0
- package/dist/types/core/index.d.ts +20 -0
- package/dist/types/core/initializeConfig.d.ts +14 -0
- package/dist/types/core/types.d.ts +6 -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-BK3jOeaA.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-7W3qm3h_.js +0 -1
- package/dist/esm/_useLocale.js +0 -1
- package/dist/esm/core.js +0 -1
- package/dist/esm/createFormatter-D4OO35MB.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/initializeConfig.d.ts +0 -13
- 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
|
|
50
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
|
+
};
|
|
113
|
+
}
|
|
114
|
+
};
|
|
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
|
|
51
139
|
};
|
|
52
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.` );
|
|
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.dev/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.dev/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,94 +443,89 @@ 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.dev/docs/configuration#time-zone` ));
|
|
394
464
|
}
|
|
395
465
|
}
|
|
396
466
|
return options;
|
|
397
467
|
}
|
|
398
|
-
function resolveFormatOrOptions(typeFormats, formatOrOptions) {
|
|
468
|
+
function resolveFormatOrOptions(typeFormats, formatOrOptions, overrides) {
|
|
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.` );
|
|
405
475
|
onError(error);
|
|
406
476
|
throw error;
|
|
407
477
|
}
|
|
408
478
|
} else {
|
|
409
479
|
options = formatOrOptions;
|
|
410
480
|
}
|
|
481
|
+
if (overrides) {
|
|
482
|
+
options = {
|
|
483
|
+
...options,
|
|
484
|
+
...overrides
|
|
485
|
+
};
|
|
486
|
+
}
|
|
411
487
|
return options;
|
|
412
488
|
}
|
|
413
|
-
function getFormattedValue(formatOrOptions, typeFormats, formatter, getFallback) {
|
|
489
|
+
function getFormattedValue(formatOrOptions, overrides, typeFormats, formatter, getFallback) {
|
|
414
490
|
let options;
|
|
415
491
|
try {
|
|
416
|
-
options = resolveFormatOrOptions(typeFormats, formatOrOptions);
|
|
417
|
-
} catch
|
|
492
|
+
options = resolveFormatOrOptions(typeFormats, formatOrOptions, overrides);
|
|
493
|
+
} catch {
|
|
418
494
|
return getFallback();
|
|
419
495
|
}
|
|
420
496
|
try {
|
|
421
497
|
return formatter(options);
|
|
422
498
|
} catch (error) {
|
|
423
|
-
onError(new
|
|
499
|
+
onError(new IntlError(IntlErrorCode.FORMATTING_ERROR, error.message));
|
|
424
500
|
return getFallback();
|
|
425
501
|
}
|
|
426
502
|
}
|
|
427
|
-
function dateTime(
|
|
428
|
-
|
|
429
|
-
/** If a time zone is supplied, the `value` is converted to that time zone.
|
|
430
|
-
* Otherwise the user time zone will be used. */
|
|
431
|
-
formatOrOptions) {
|
|
432
|
-
return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.dateTime, options => {
|
|
503
|
+
function dateTime(value, formatOrOptions, overrides) {
|
|
504
|
+
return getFormattedValue(formatOrOptions, overrides, formats?.dateTime, options => {
|
|
433
505
|
options = applyTimeZone(options);
|
|
434
506
|
return formatters.getDateTimeFormat(locale, options).format(value);
|
|
435
507
|
}, () => String(value));
|
|
436
508
|
}
|
|
437
|
-
function dateTimeRange(
|
|
438
|
-
|
|
439
|
-
end,
|
|
440
|
-
/** If a time zone is supplied, the values are converted to that time zone.
|
|
441
|
-
* Otherwise the user time zone will be used. */
|
|
442
|
-
formatOrOptions) {
|
|
443
|
-
return getFormattedValue(formatOrOptions, formats === null || formats === void 0 ? void 0 : formats.dateTime, options => {
|
|
509
|
+
function dateTimeRange(start, end, formatOrOptions, overrides) {
|
|
510
|
+
return getFormattedValue(formatOrOptions, overrides, formats?.dateTime, options => {
|
|
444
511
|
options = applyTimeZone(options);
|
|
445
512
|
return formatters.getDateTimeFormat(locale, options).formatRange(start, end);
|
|
446
513
|
}, () => [dateTime(start), dateTime(end)].join(' – '));
|
|
447
514
|
}
|
|
448
|
-
function number(value, formatOrOptions) {
|
|
449
|
-
return getFormattedValue(formatOrOptions,
|
|
515
|
+
function number(value, formatOrOptions, overrides) {
|
|
516
|
+
return getFormattedValue(formatOrOptions, overrides, formats?.number, options => formatters.getNumberFormat(locale, options).format(value), () => String(value));
|
|
450
517
|
}
|
|
451
518
|
function getGlobalNow() {
|
|
452
|
-
|
|
453
|
-
|
|
519
|
+
// Only read when necessary to avoid triggering a `dynamicIO` error
|
|
520
|
+
// unnecessarily (`now` is only needed for `format.relativeTime`)
|
|
521
|
+
if (props.now) {
|
|
522
|
+
return props.now;
|
|
454
523
|
} else {
|
|
455
|
-
onError(new
|
|
524
|
+
onError(new IntlError(IntlErrorCode.ENVIRONMENT_FALLBACK, `The \`now\` parameter wasn't provided to \`relativeTime\` and there is no global default configured, therefore the current time will be used as a fallback. See https://next-intl.dev/docs/usage/dates-times#relative-times-usenow` ));
|
|
456
525
|
return new Date();
|
|
457
526
|
}
|
|
458
527
|
}
|
|
459
|
-
function relativeTime(
|
|
460
|
-
date, /** The reference point in time to which `date` will be formatted in relation to. */
|
|
461
|
-
nowOrOptions) {
|
|
528
|
+
function relativeTime(date, nowOrOptions) {
|
|
462
529
|
try {
|
|
463
530
|
let nowDate, unit;
|
|
464
531
|
const opts = {};
|
|
@@ -496,11 +563,11 @@ function createFormatter(_ref) {
|
|
|
496
563
|
const value = calculateRelativeTimeValue(seconds, unit);
|
|
497
564
|
return formatters.getRelativeTimeFormat(locale, opts).format(value, unit);
|
|
498
565
|
} catch (error) {
|
|
499
|
-
onError(new
|
|
566
|
+
onError(new IntlError(IntlErrorCode.FORMATTING_ERROR, error.message));
|
|
500
567
|
return String(date);
|
|
501
568
|
}
|
|
502
569
|
}
|
|
503
|
-
function list(value, formatOrOptions) {
|
|
570
|
+
function list(value, formatOrOptions, overrides) {
|
|
504
571
|
const serializedValue = [];
|
|
505
572
|
const richValues = new Map();
|
|
506
573
|
|
|
@@ -519,7 +586,7 @@ function createFormatter(_ref) {
|
|
|
519
586
|
serializedValue.push(serializedItem);
|
|
520
587
|
index++;
|
|
521
588
|
}
|
|
522
|
-
return getFormattedValue(formatOrOptions,
|
|
589
|
+
return getFormattedValue(formatOrOptions, overrides, formats?.list,
|
|
523
590
|
// @ts-expect-error -- `richValues.size` is used to determine the return type, but TypeScript can't infer the meaning of this correctly
|
|
524
591
|
options => {
|
|
525
592
|
const result = formatters.getListFormat(locale, options).formatToParts(serializedValue).map(part => part.type === 'literal' ? part.value : richValues.get(part.value) || part.value);
|
|
@@ -539,6 +606,84 @@ function createFormatter(_ref) {
|
|
|
539
606
|
};
|
|
540
607
|
}
|
|
541
608
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
609
|
+
function validateMessagesSegment(messages, invalidKeyLabels, parentPath) {
|
|
610
|
+
Object.entries(messages).forEach(([key, messageOrMessages]) => {
|
|
611
|
+
if (key.includes('.')) {
|
|
612
|
+
let keyLabel = key;
|
|
613
|
+
if (parentPath) keyLabel += ` (at ${parentPath})`;
|
|
614
|
+
invalidKeyLabels.push(keyLabel);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
618
|
+
if (messageOrMessages != null && typeof messageOrMessages === 'object') {
|
|
619
|
+
validateMessagesSegment(messageOrMessages, invalidKeyLabels, joinPath(parentPath, key));
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
function validateMessages(messages, onError) {
|
|
624
|
+
const invalidKeyLabels = [];
|
|
625
|
+
validateMessagesSegment(messages, invalidKeyLabels);
|
|
626
|
+
if (invalidKeyLabels.length > 0) {
|
|
627
|
+
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.
|
|
628
|
+
|
|
629
|
+
Invalid ${invalidKeyLabels.length === 1 ? 'key' : 'keys'}: ${invalidKeyLabels.join(', ')}
|
|
630
|
+
|
|
631
|
+
If you're migrating from a flat structure, you can convert your messages as follows:
|
|
632
|
+
|
|
633
|
+
import {set} from "lodash";
|
|
634
|
+
|
|
635
|
+
const input = {
|
|
636
|
+
"one.one": "1.1",
|
|
637
|
+
"one.two": "1.2",
|
|
638
|
+
"two.one.one": "2.1.1"
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const output = Object.entries(input).reduce(
|
|
642
|
+
(acc, [key, value]) => set(acc, key, value),
|
|
643
|
+
{}
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
// Output:
|
|
647
|
+
//
|
|
648
|
+
// {
|
|
649
|
+
// "one": {
|
|
650
|
+
// "one": "1.1",
|
|
651
|
+
// "two": "1.2"
|
|
652
|
+
// },
|
|
653
|
+
// "two": {
|
|
654
|
+
// "one": {
|
|
655
|
+
// "one": "2.1.1"
|
|
656
|
+
// }
|
|
657
|
+
// }
|
|
658
|
+
// }
|
|
659
|
+
` ));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Enhances the incoming props with defaults.
|
|
665
|
+
*/
|
|
666
|
+
function initializeConfig({
|
|
667
|
+
formats,
|
|
668
|
+
getMessageFallback,
|
|
669
|
+
messages,
|
|
670
|
+
onError,
|
|
671
|
+
...rest
|
|
672
|
+
}) {
|
|
673
|
+
const finalOnError = onError || defaultOnError;
|
|
674
|
+
const finalGetMessageFallback = getMessageFallback || defaultGetMessageFallback;
|
|
675
|
+
{
|
|
676
|
+
if (messages) {
|
|
677
|
+
validateMessages(messages, finalOnError);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return {
|
|
681
|
+
...rest,
|
|
682
|
+
formats: formats || undefined,
|
|
683
|
+
messages: messages || undefined,
|
|
684
|
+
onError: finalOnError,
|
|
685
|
+
getMessageFallback: finalGetMessageFallback
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
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 };
|