use-intl 2.7.6 → 2.8.0

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.
Files changed (124) hide show
  1. package/dist/{AbstractIntlMessages.d.ts → core/AbstractIntlMessages.d.ts} +0 -0
  2. package/dist/{DateTimeFormatOptions.d.ts → core/DateTimeFormatOptions.d.ts} +0 -0
  3. package/dist/{Formats.d.ts → core/Formats.d.ts} +0 -0
  4. package/dist/{IntlProvider.d.ts → core/IntlConfiguration.d.ts} +15 -16
  5. package/dist/{IntlError.d.ts → core/IntlError.d.ts} +0 -0
  6. package/dist/{IntlMessages.d.ts → core/IntlMessages.d.ts} +0 -0
  7. package/dist/{NumberFormatOptions.d.ts → core/NumberFormatOptions.d.ts} +0 -0
  8. package/dist/{TranslationValues.d.ts → core/TranslationValues.d.ts} +2 -2
  9. package/dist/{convertFormatsToIntlMessageFormat.d.ts → core/convertFormatsToIntlMessageFormat.d.ts} +0 -0
  10. package/dist/core/createBaseTranslator.d.ts +26 -0
  11. package/dist/{useIntl.d.ts → core/createIntl.d.ts} +11 -1
  12. package/dist/core/createTranslator.d.ts +48 -0
  13. package/dist/core/createTranslatorImpl.d.ts +14 -0
  14. package/dist/core/defaults.d.ts +11 -0
  15. package/dist/core/index.d.ts +8 -0
  16. package/dist/core/resolveNamespace.d.ts +5 -0
  17. package/dist/{use-intl.esm8.js → core/use-intl.esm.js} +2 -2
  18. package/dist/core/use-intl.esm.js.map +1 -0
  19. package/dist/core/use-intl.esm2.js +39 -0
  20. package/dist/core/use-intl.esm2.js.map +1 -0
  21. package/dist/{use-intl.esm4.js → core/use-intl.esm3.js} +12 -12
  22. package/dist/core/use-intl.esm3.js.map +1 -0
  23. package/dist/core/use-intl.esm4.js +59 -0
  24. package/dist/core/use-intl.esm4.js.map +1 -0
  25. package/dist/core/use-intl.esm5.js +15 -0
  26. package/dist/core/use-intl.esm5.js.map +1 -0
  27. package/dist/{use-intl.esm9.js → core/use-intl.esm6.js} +2 -2
  28. package/dist/core/use-intl.esm6.js.map +1 -0
  29. package/dist/core/use-intl.esm7.js +213 -0
  30. package/dist/core/use-intl.esm7.js.map +1 -0
  31. package/dist/core/use-intl.esm8.js +10 -0
  32. package/dist/core/use-intl.esm8.js.map +1 -0
  33. package/dist/{use-intl.esm13.js → core/use-intl.esm9.js} +2 -2
  34. package/dist/core/use-intl.esm9.js.map +1 -0
  35. package/dist/{utils → core/utils}/MessageKeys.d.ts +0 -0
  36. package/dist/{utils → core/utils}/NamespaceKeys.d.ts +0 -0
  37. package/dist/{utils → core/utils}/NestedKeyOf.d.ts +0 -0
  38. package/dist/{utils → core/utils}/NestedValueOf.d.ts +0 -0
  39. package/dist/{validateMessages.d.ts → core/validateMessages.d.ts} +0 -0
  40. package/dist/index.d.ts +2 -12
  41. package/dist/{IntlContext.d.ts → react/IntlContext.d.ts} +4 -4
  42. package/dist/react/IntlProvider.d.ts +16 -0
  43. package/dist/react/index.d.ts +6 -0
  44. package/dist/react/use-intl.esm.js +36 -0
  45. package/dist/react/use-intl.esm.js.map +1 -0
  46. package/dist/{use-intl.esm3.js → react/use-intl.esm2.js} +4 -4
  47. package/dist/react/use-intl.esm2.js.map +1 -0
  48. package/dist/react/use-intl.esm3.js +25 -0
  49. package/dist/react/use-intl.esm3.js.map +1 -0
  50. package/dist/{use-intl.esm5.js → react/use-intl.esm4.js} +2 -2
  51. package/dist/react/use-intl.esm4.js.map +1 -0
  52. package/dist/{use-intl.esm7.js → react/use-intl.esm5.js} +2 -2
  53. package/dist/react/use-intl.esm5.js.map +1 -0
  54. package/dist/{use-intl.esm6.js → react/use-intl.esm6.js} +1 -1
  55. package/dist/react/use-intl.esm6.js.map +1 -0
  56. package/dist/{use-intl.esm10.js → react/use-intl.esm7.js} +1 -1
  57. package/dist/react/use-intl.esm7.js.map +1 -0
  58. package/dist/{use-intl.esm11.js → react/use-intl.esm8.js} +2 -2
  59. package/dist/react/use-intl.esm8.js.map +1 -0
  60. package/dist/react/use-intl.esm9.js +44 -0
  61. package/dist/react/use-intl.esm9.js.map +1 -0
  62. package/dist/react/useIntl.d.ts +5 -0
  63. package/dist/{useIntlContext.d.ts → react/useIntlContext.d.ts} +0 -0
  64. package/dist/{useLocale.d.ts → react/useLocale.d.ts} +0 -0
  65. package/dist/{useNow.d.ts → react/useNow.d.ts} +0 -0
  66. package/dist/{useTimeZone.d.ts → react/useTimeZone.d.ts} +0 -0
  67. package/dist/{useTranslations.d.ts → react/useTranslations.d.ts} +6 -6
  68. package/dist/react/useTranslationsImpl.d.ts +8 -0
  69. package/dist/use-intl.cjs.development.js +381 -229
  70. package/dist/use-intl.cjs.development.js.map +1 -1
  71. package/dist/use-intl.cjs.production.min.js +1 -1
  72. package/dist/use-intl.cjs.production.min.js.map +1 -1
  73. package/dist/use-intl.esm.js +9 -7
  74. package/dist/use-intl.esm.js.map +1 -1
  75. package/package.json +3 -2
  76. package/src/{AbstractIntlMessages.tsx → core/AbstractIntlMessages.tsx} +0 -0
  77. package/src/{DateTimeFormatOptions.tsx → core/DateTimeFormatOptions.tsx} +0 -0
  78. package/src/{Formats.tsx → core/Formats.tsx} +0 -0
  79. package/src/{IntlProvider.tsx → core/IntlConfiguration.tsx} +16 -53
  80. package/src/{IntlError.tsx → core/IntlError.tsx} +0 -0
  81. package/src/{IntlMessages.tsx → core/IntlMessages.tsx} +0 -0
  82. package/src/{NumberFormatOptions.tsx → core/NumberFormatOptions.tsx} +0 -0
  83. package/src/core/TranslationValues.tsx +22 -0
  84. package/src/{convertFormatsToIntlMessageFormat.tsx → core/convertFormatsToIntlMessageFormat.tsx} +0 -0
  85. package/src/core/createBaseTranslator.tsx +316 -0
  86. package/src/{useIntl.tsx → core/createIntl.tsx} +17 -4
  87. package/src/core/createTranslator.tsx +112 -0
  88. package/src/core/createTranslatorImpl.tsx +85 -0
  89. package/src/core/defaults.tsx +18 -0
  90. package/src/core/index.tsx +11 -0
  91. package/src/core/resolveNamespace.tsx +12 -0
  92. package/src/{utils → core/utils}/MessageKeys.tsx +0 -0
  93. package/src/{utils → core/utils}/NamespaceKeys.tsx +0 -0
  94. package/src/{utils → core/utils}/NestedKeyOf.tsx +0 -0
  95. package/src/{utils → core/utils}/NestedValueOf.tsx +0 -0
  96. package/src/{validateMessages.tsx → core/validateMessages.tsx} +0 -0
  97. package/src/index.tsx +2 -15
  98. package/src/{IntlContext.tsx → react/IntlContext.tsx} +4 -4
  99. package/src/react/IntlProvider.tsx +43 -0
  100. package/src/react/index.tsx +6 -0
  101. package/src/react/useIntl.tsx +19 -0
  102. package/src/{useIntlContext.tsx → react/useIntlContext.tsx} +0 -0
  103. package/src/{useLocale.tsx → react/useLocale.tsx} +0 -0
  104. package/src/{useNow.tsx → react/useNow.tsx} +0 -0
  105. package/src/{useTimeZone.tsx → react/useTimeZone.tsx} +0 -0
  106. package/src/{useTranslations.tsx → react/useTranslations.tsx} +9 -7
  107. package/src/react/useTranslationsImpl.tsx +64 -0
  108. package/dist/use-intl.esm10.js.map +0 -1
  109. package/dist/use-intl.esm11.js.map +0 -1
  110. package/dist/use-intl.esm12.js +0 -213
  111. package/dist/use-intl.esm12.js.map +0 -1
  112. package/dist/use-intl.esm13.js.map +0 -1
  113. package/dist/use-intl.esm2.js +0 -48
  114. package/dist/use-intl.esm2.js.map +0 -1
  115. package/dist/use-intl.esm3.js.map +0 -1
  116. package/dist/use-intl.esm4.js.map +0 -1
  117. package/dist/use-intl.esm5.js.map +0 -1
  118. package/dist/use-intl.esm6.js.map +0 -1
  119. package/dist/use-intl.esm7.js.map +0 -1
  120. package/dist/use-intl.esm8.js.map +0 -1
  121. package/dist/use-intl.esm9.js.map +0 -1
  122. package/dist/useTranslationsImpl.d.ts +0 -12
  123. package/src/TranslationValues.tsx +0 -13
  124. package/src/useTranslationsImpl.tsx +0 -328
@@ -1,14 +1,11 @@
1
- import React, {ReactNode, useEffect} from 'react';
2
- import AbstractIntlMessages from './AbstractIntlMessages';
3
1
  import Formats from './Formats';
4
- import IntlContext from './IntlContext';
5
2
  import IntlError from './IntlError';
6
- import {RichTranslationValues} from './TranslationValues';
7
- import validateMessages from './validateMessages';
8
3
 
9
- type Props = {
10
- /** All messages that will be available in your components. */
11
- messages?: AbstractIntlMessages;
4
+ /**
5
+ * Should be used for entry points that configure the library.
6
+ */
7
+
8
+ type IntlConfiguration = {
12
9
  /** A valid Unicode locale tag (e.g. "en" or "en-GB"). */
13
10
  locale: string;
14
11
  /** Global formats can be provided to achieve consistent
@@ -25,12 +22,10 @@ type Props = {
25
22
  * an error. This defaults to `${namespace}.${key}` You can use this to
26
23
  * customize what will be rendered in this case. */
27
24
  getMessageFallback?(info: {
28
- namespace?: string;
29
- key: string;
30
25
  error: IntlError;
26
+ key: string;
27
+ namespace?: string;
31
28
  }): string;
32
- /** All components that use the provided hooks should be within this tree. */
33
- children: ReactNode;
34
29
  /**
35
30
  * Providing this value will have two effects:
36
31
  * 1. It will be used as the default for the `now` argument of
@@ -41,47 +36,15 @@ type Props = {
41
36
  * afterwards the current date will be returned continuously.
42
37
  */
43
38
  now?: Date;
44
- /** Global default values for translation values and rich text elements.
45
- * Can be used for consistent usage or styling of rich text elements.
46
- * Defaults will be overidden by locally provided values. */
47
- defaultTranslationValues?: RichTranslationValues;
48
39
  };
49
40
 
50
- function defaultGetMessageFallback({
51
- key,
52
- namespace
53
- }: {
54
- key: string;
55
- namespace?: string;
56
- }) {
57
- return [namespace, key].filter((part) => part != null).join('.');
58
- }
59
-
60
- function defaultOnError(error: IntlError) {
61
- console.error(error);
62
- }
63
-
64
- export default function IntlProvider({
65
- children,
66
- onError = defaultOnError,
67
- getMessageFallback = defaultGetMessageFallback,
68
- messages,
69
- ...contextValues
70
- }: Props) {
71
- if (__DEV__) {
72
- // eslint-disable-next-line react-hooks/rules-of-hooks
73
- useEffect(() => {
74
- if (messages) {
75
- validateMessages(messages, onError);
76
- }
77
- }, [messages, onError]);
78
- }
41
+ /**
42
+ * A stricter set of the configuration that should be used internally
43
+ * once defaults are assigned to `IntlConfiguration`.
44
+ */
45
+ export type InitializedIntlConfiguration = IntlConfiguration & {
46
+ onError: NonNullable<IntlConfiguration['onError']>;
47
+ getMessageFallback: NonNullable<IntlConfiguration['getMessageFallback']>;
48
+ };
79
49
 
80
- return (
81
- <IntlContext.Provider
82
- value={{...contextValues, messages, onError, getMessageFallback}}
83
- >
84
- {children}
85
- </IntlContext.Provider>
86
- );
87
- }
50
+ export default IntlConfiguration;
File without changes
File without changes
@@ -0,0 +1,22 @@
1
+ import {ReactNode} from 'react';
2
+
3
+ // From IntlMessageFormat#format
4
+ export type TranslationValue =
5
+ | string
6
+ | number
7
+ | boolean
8
+ | Date
9
+ | null
10
+ | undefined;
11
+
12
+ type TranslationValues = Record<string, TranslationValue>;
13
+
14
+ // We could consider renaming this to `ReactRichTranslationValues` and defining
15
+ // it in the `react` namespace if the core becomes useful to other frameworks.
16
+ // It would be a breaking change though, so let's wait for now.
17
+ export type RichTranslationValues = Record<
18
+ string,
19
+ TranslationValue | ((chunks: ReactNode) => ReactNode)
20
+ >;
21
+
22
+ export default TranslationValues;
@@ -0,0 +1,316 @@
1
+ import IntlMessageFormat from 'intl-messageformat';
2
+ import {
3
+ cloneElement,
4
+ isValidElement,
5
+ ReactElement,
6
+ ReactNode,
7
+ ReactNodeArray
8
+ } from 'react';
9
+ import AbstractIntlMessages from './AbstractIntlMessages';
10
+ import Formats from './Formats';
11
+ import {InitializedIntlConfiguration} from './IntlConfiguration';
12
+ import IntlError, {IntlErrorCode} from './IntlError';
13
+ import TranslationValues, {RichTranslationValues} from './TranslationValues';
14
+ import convertFormatsToIntlMessageFormat from './convertFormatsToIntlMessageFormat';
15
+ import {defaultGetMessageFallback, defaultOnError} from './defaults';
16
+ import MessageKeys from './utils/MessageKeys';
17
+ import NestedKeyOf from './utils/NestedKeyOf';
18
+ import NestedValueOf from './utils/NestedValueOf';
19
+
20
+ function resolvePath(
21
+ messages: AbstractIntlMessages | undefined,
22
+ key: string,
23
+ namespace?: string
24
+ ) {
25
+ if (!messages) {
26
+ throw new Error(
27
+ __DEV__ ? `No messages available at \`${namespace}\`.` : undefined
28
+ );
29
+ }
30
+
31
+ let message = messages;
32
+
33
+ key.split('.').forEach((part) => {
34
+ const next = (message as any)[part];
35
+
36
+ if (part == null || next == null) {
37
+ throw new Error(
38
+ __DEV__
39
+ ? `Could not resolve \`${key}\` in ${
40
+ namespace ? `\`${namespace}\`` : 'messages'
41
+ }.`
42
+ : undefined
43
+ );
44
+ }
45
+
46
+ message = next;
47
+ });
48
+
49
+ return message;
50
+ }
51
+
52
+ function prepareTranslationValues(values: RichTranslationValues) {
53
+ if (Object.keys(values).length === 0) return undefined;
54
+
55
+ // Workaround for https://github.com/formatjs/formatjs/issues/1467
56
+ const transformedValues: RichTranslationValues = {};
57
+ Object.keys(values).forEach((key) => {
58
+ let index = 0;
59
+ const value = values[key];
60
+
61
+ let transformed;
62
+ if (typeof value === 'function') {
63
+ transformed = (chunks: ReactNode) => {
64
+ const result = value(chunks);
65
+
66
+ return isValidElement(result)
67
+ ? cloneElement(result, {key: key + index++})
68
+ : result;
69
+ };
70
+ } else {
71
+ transformed = value;
72
+ }
73
+
74
+ transformedValues[key] = transformed;
75
+ });
76
+
77
+ return transformedValues;
78
+ }
79
+
80
+ export function getMessagesOrError<Messages extends AbstractIntlMessages>({
81
+ messages,
82
+ namespace,
83
+ onError = defaultOnError
84
+ }: {
85
+ messages: Messages;
86
+ namespace?: string;
87
+ onError?(error: IntlError): void;
88
+ }) {
89
+ try {
90
+ if (!messages) {
91
+ throw new Error(
92
+ __DEV__ ? `No messages were configured on the provider.` : undefined
93
+ );
94
+ }
95
+
96
+ const retrievedMessages = namespace
97
+ ? resolvePath(messages, namespace)
98
+ : messages;
99
+
100
+ if (!retrievedMessages) {
101
+ throw new Error(
102
+ __DEV__
103
+ ? `No messages for namespace \`${namespace}\` found.`
104
+ : undefined
105
+ );
106
+ }
107
+
108
+ return retrievedMessages;
109
+ } catch (error) {
110
+ const intlError = new IntlError(
111
+ IntlErrorCode.MISSING_MESSAGE,
112
+ (error as Error).message
113
+ );
114
+ onError(intlError);
115
+ return intlError;
116
+ }
117
+ }
118
+
119
+ export type CreateBaseTranslatorProps<Messages> =
120
+ InitializedIntlConfiguration & {
121
+ cachedFormatsByLocale?: Record<string, Record<string, IntlMessageFormat>>;
122
+ defaultTranslationValues?: RichTranslationValues;
123
+ namespace?: string;
124
+ messagesOrError: Messages | IntlError;
125
+ };
126
+
127
+ export default function createBaseTranslator<
128
+ Messages extends AbstractIntlMessages,
129
+ NestedKey extends NestedKeyOf<Messages>
130
+ >({
131
+ cachedFormatsByLocale,
132
+ defaultTranslationValues,
133
+ formats: globalFormats,
134
+ getMessageFallback = defaultGetMessageFallback,
135
+ locale,
136
+ messagesOrError,
137
+ namespace,
138
+ onError,
139
+ timeZone
140
+ }: CreateBaseTranslatorProps<Messages>) {
141
+ function getFallbackFromErrorAndNotify(
142
+ key: string,
143
+ code: IntlErrorCode,
144
+ message?: string
145
+ ) {
146
+ const error = new IntlError(code, message);
147
+ onError(error);
148
+ return getMessageFallback({error, key, namespace});
149
+ }
150
+
151
+ function translateBaseFn(
152
+ /** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */
153
+ key: string,
154
+ /** Key value pairs for values to interpolate into the message. */
155
+ values?: RichTranslationValues,
156
+ /** Provide custom formats for numbers, dates and times. */
157
+ formats?: Partial<Formats>
158
+ ): string | ReactElement | ReactNodeArray {
159
+ if (messagesOrError instanceof IntlError) {
160
+ // We have already warned about this during render
161
+ return getMessageFallback({
162
+ error: messagesOrError,
163
+ key,
164
+ namespace
165
+ });
166
+ }
167
+ const messages = messagesOrError;
168
+
169
+ const cacheKey = [namespace, key].filter((part) => part != null).join('.');
170
+
171
+ let messageFormat;
172
+ if (cachedFormatsByLocale?.[locale]?.[cacheKey]) {
173
+ messageFormat = cachedFormatsByLocale?.[locale][cacheKey];
174
+ } else {
175
+ let message;
176
+ try {
177
+ message = resolvePath(messages, key, namespace);
178
+ } catch (error) {
179
+ return getFallbackFromErrorAndNotify(
180
+ key,
181
+ IntlErrorCode.MISSING_MESSAGE,
182
+ (error as Error).message
183
+ );
184
+ }
185
+
186
+ if (typeof message === 'object') {
187
+ return getFallbackFromErrorAndNotify(
188
+ key,
189
+ IntlErrorCode.INSUFFICIENT_PATH,
190
+ __DEV__
191
+ ? `Insufficient path specified for \`${key}\` in \`${
192
+ namespace ? `\`${namespace}\`` : 'messages'
193
+ }\`.`
194
+ : undefined
195
+ );
196
+ }
197
+
198
+ try {
199
+ messageFormat = new IntlMessageFormat(
200
+ message,
201
+ locale,
202
+ convertFormatsToIntlMessageFormat(
203
+ {...globalFormats, ...formats},
204
+ timeZone
205
+ )
206
+ );
207
+ } catch (error) {
208
+ return getFallbackFromErrorAndNotify(
209
+ key,
210
+ IntlErrorCode.INVALID_MESSAGE,
211
+ (error as Error).message
212
+ );
213
+ }
214
+
215
+ if (cachedFormatsByLocale) {
216
+ if (!cachedFormatsByLocale[locale]) {
217
+ cachedFormatsByLocale[locale] = {};
218
+ }
219
+ cachedFormatsByLocale[locale][cacheKey] = messageFormat;
220
+ }
221
+ }
222
+
223
+ try {
224
+ const formattedMessage = messageFormat.format(
225
+ // @ts-ignore `intl-messageformat` expects a different format
226
+ // for rich text elements since a recent minor update. This
227
+ // needs to be evaluated in detail, possibly also in regards
228
+ // to be able to format to parts.
229
+ prepareTranslationValues({...defaultTranslationValues, ...values})
230
+ );
231
+
232
+ if (formattedMessage == null) {
233
+ throw new Error(
234
+ __DEV__
235
+ ? `Unable to format \`${key}\` in ${
236
+ namespace ? `namespace \`${namespace}\`` : 'messages'
237
+ }`
238
+ : undefined
239
+ );
240
+ }
241
+
242
+ // Limit the function signature to return strings or React elements
243
+ return isValidElement(formattedMessage) ||
244
+ // Arrays of React elements
245
+ Array.isArray(formattedMessage) ||
246
+ typeof formattedMessage === 'string'
247
+ ? formattedMessage
248
+ : String(formattedMessage);
249
+ } catch (error) {
250
+ return getFallbackFromErrorAndNotify(
251
+ key,
252
+ IntlErrorCode.FORMATTING_ERROR,
253
+ (error as Error).message
254
+ );
255
+ }
256
+ }
257
+
258
+ function translateFn<
259
+ TargetKey extends MessageKeys<
260
+ NestedValueOf<Messages, NestedKey>,
261
+ NestedKeyOf<NestedValueOf<Messages, NestedKey>>
262
+ >
263
+ >(
264
+ /** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */
265
+ key: TargetKey,
266
+ /** Key value pairs for values to interpolate into the message. */
267
+ values?: TranslationValues,
268
+ /** Provide custom formats for numbers, dates and times. */
269
+ formats?: Partial<Formats>
270
+ ): string {
271
+ const result = translateBaseFn(key, values, formats);
272
+
273
+ if (typeof result !== 'string') {
274
+ return getFallbackFromErrorAndNotify(
275
+ key,
276
+ IntlErrorCode.INVALID_MESSAGE,
277
+ __DEV__
278
+ ? `The message \`${key}\` in ${
279
+ namespace ? `namespace \`${namespace}\`` : 'messages'
280
+ } didn't resolve to a string. If you want to format rich text, use \`t.rich\` instead.`
281
+ : undefined
282
+ );
283
+ }
284
+
285
+ return result;
286
+ }
287
+
288
+ translateFn.rich = translateBaseFn;
289
+
290
+ translateFn.raw = (
291
+ /** Use a dot to indicate a level of nesting (e.g. `namespace.nestedLabel`). */
292
+ key: string
293
+ ): any => {
294
+ if (messagesOrError instanceof IntlError) {
295
+ // We have already warned about this during render
296
+ return getMessageFallback({
297
+ error: messagesOrError,
298
+ key,
299
+ namespace
300
+ });
301
+ }
302
+ const messages = messagesOrError;
303
+
304
+ try {
305
+ return resolvePath(messages, key, namespace);
306
+ } catch (error) {
307
+ return getFallbackFromErrorAndNotify(
308
+ key,
309
+ IntlErrorCode.MISSING_MESSAGE,
310
+ (error as Error).message
311
+ );
312
+ }
313
+ };
314
+
315
+ return translateFn;
316
+ }
@@ -1,7 +1,8 @@
1
1
  import DateTimeFormatOptions from './DateTimeFormatOptions';
2
+ import Formats from './Formats';
2
3
  import IntlError, {IntlErrorCode} from './IntlError';
3
4
  import NumberFormatOptions from './NumberFormatOptions';
4
- import useIntlContext from './useIntlContext';
5
+ import {defaultOnError} from './defaults';
5
6
 
6
7
  const MINUTE = 60;
7
8
  const HOUR = MINUTE * 60;
@@ -43,9 +44,21 @@ function getRelativeTimeFormatConfig(seconds: number) {
43
44
  return {value, unit};
44
45
  }
45
46
 
46
- export default function useIntl() {
47
- const {formats, locale, now: globalNow, onError, timeZone} = useIntlContext();
48
-
47
+ type Props = {
48
+ locale: string;
49
+ timeZone?: string;
50
+ onError?(error: IntlError): void;
51
+ formats?: Partial<Formats>;
52
+ now?: Date;
53
+ };
54
+
55
+ export default function createIntl({
56
+ formats,
57
+ locale,
58
+ now: globalNow,
59
+ onError = defaultOnError,
60
+ timeZone
61
+ }: Props) {
49
62
  function resolveFormatOrOptions<Options>(
50
63
  typeFormats: Record<string, Options> | undefined,
51
64
  formatOrOptions?: string | Options
@@ -0,0 +1,112 @@
1
+ import Formats from './Formats';
2
+ import IntlConfiguration from './IntlConfiguration';
3
+ import TranslationValues from './TranslationValues';
4
+ import createTranslatorImpl, {
5
+ CoreRichTranslationValues
6
+ } from './createTranslatorImpl';
7
+ import {defaultGetMessageFallback, defaultOnError} from './defaults';
8
+ import MessageKeys from './utils/MessageKeys';
9
+ import NamespaceKeys from './utils/NamespaceKeys';
10
+ import NestedKeyOf from './utils/NestedKeyOf';
11
+ import NestedValueOf from './utils/NestedValueOf';
12
+
13
+ /**
14
+ * Translates messages from the given namespace by using the ICU syntax.
15
+ * See https://formatjs.io/docs/core-concepts/icu-syntax.
16
+ *
17
+ * If no namespace is provided, all available messages are returned.
18
+ * The namespace can also indicate nesting by using a dot
19
+ * (e.g. `namespace.Component`).
20
+ */
21
+ export default function createTranslator<
22
+ NestedKey extends NamespaceKeys<
23
+ IntlMessages,
24
+ NestedKeyOf<IntlMessages>
25
+ > = never
26
+ >({
27
+ onError = defaultOnError,
28
+ getMessageFallback = defaultGetMessageFallback,
29
+ messages,
30
+ namespace,
31
+ ...rest
32
+ }: IntlConfiguration & {
33
+ messages: IntlMessages;
34
+ namespace?: NestedKey;
35
+ }): // Explicitly defining the return type is necessary as TypeScript would get it wrong
36
+ {
37
+ // Default invocation
38
+ <
39
+ TargetKey extends MessageKeys<
40
+ NestedValueOf<
41
+ {'!': IntlMessages},
42
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
43
+ >,
44
+ NestedKeyOf<
45
+ NestedValueOf<
46
+ {'!': IntlMessages},
47
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
48
+ >
49
+ >
50
+ >
51
+ >(
52
+ key: TargetKey,
53
+ values?: TranslationValues,
54
+ formats?: Partial<Formats>
55
+ ): string;
56
+
57
+ // `rich`
58
+ rich<
59
+ TargetKey extends MessageKeys<
60
+ NestedValueOf<
61
+ {'!': IntlMessages},
62
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
63
+ >,
64
+ NestedKeyOf<
65
+ NestedValueOf<
66
+ {'!': IntlMessages},
67
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
68
+ >
69
+ >
70
+ >
71
+ >(
72
+ key: TargetKey,
73
+ values?: CoreRichTranslationValues,
74
+ formats?: Partial<Formats>
75
+ ): string;
76
+
77
+ // `raw`
78
+ raw<
79
+ TargetKey extends MessageKeys<
80
+ NestedValueOf<
81
+ {'!': IntlMessages},
82
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
83
+ >,
84
+ NestedKeyOf<
85
+ NestedValueOf<
86
+ {'!': IntlMessages},
87
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
88
+ >
89
+ >
90
+ >
91
+ >(
92
+ key: TargetKey
93
+ ): any;
94
+ } {
95
+ // We have to wrap the actual function so the type inference for the optional
96
+ // namespace works correctly. See https://stackoverflow.com/a/71529575/343045
97
+ // The prefix ("!") is arbitrary.
98
+ return createTranslatorImpl<
99
+ {'!': IntlMessages},
100
+ [NestedKey] extends [never] ? '!' : `!.${NestedKey}`
101
+ >(
102
+ {
103
+ ...rest,
104
+ onError,
105
+ getMessageFallback,
106
+ messages: {'!': messages},
107
+ // @ts-ignore
108
+ namespace: namespace ? `!.${namespace}` : '!'
109
+ },
110
+ '!'
111
+ );
112
+ }
@@ -0,0 +1,85 @@
1
+ import AbstractIntlMessages from './AbstractIntlMessages';
2
+ import {InitializedIntlConfiguration} from './IntlConfiguration';
3
+ import IntlError, {IntlErrorCode} from './IntlError';
4
+ import {RichTranslationValues, TranslationValue} from './TranslationValues';
5
+ import createBaseTranslator, {getMessagesOrError} from './createBaseTranslator';
6
+ import resolveNamespace from './resolveNamespace';
7
+ import NestedKeyOf from './utils/NestedKeyOf';
8
+
9
+ export type CoreRichTranslationValues = Record<
10
+ string,
11
+ TranslationValue | ((chunks: string) => string)
12
+ >;
13
+
14
+ export type CreateTranslatorImplProps<Messages> =
15
+ InitializedIntlConfiguration & {
16
+ namespace: string;
17
+ messages: Messages;
18
+ };
19
+
20
+ export default function createTranslatorImpl<
21
+ Messages extends AbstractIntlMessages,
22
+ NestedKey extends NestedKeyOf<Messages>
23
+ >(
24
+ {
25
+ getMessageFallback,
26
+ messages,
27
+ namespace,
28
+ onError,
29
+ ...rest
30
+ }: CreateTranslatorImplProps<Messages>,
31
+ namespacePrefix: string
32
+ ) {
33
+ // The `namespacePrefix` is part of the type system.
34
+ // See the comment in the function invocation.
35
+ messages = messages[namespacePrefix] as Messages;
36
+ namespace = resolveNamespace(namespace, namespacePrefix) as NestedKey;
37
+
38
+ const translator = createBaseTranslator<Messages, NestedKey>({
39
+ ...rest,
40
+ onError,
41
+ getMessageFallback,
42
+ messagesOrError: getMessagesOrError({
43
+ messages,
44
+ namespace,
45
+ onError
46
+ }) as Messages | IntlError
47
+ });
48
+
49
+ const originalRich = translator.rich;
50
+
51
+ function base(...args: Parameters<typeof translator>) {
52
+ return translator(...args);
53
+ }
54
+
55
+ // Augment `t.rich` to return plain strings
56
+ base.rich = (
57
+ key: Parameters<typeof originalRich>[0],
58
+ /** Key value pairs for values to interpolate into the message. */
59
+ values: CoreRichTranslationValues,
60
+ formats?: Parameters<typeof originalRich>[2]
61
+ ): string => {
62
+ // `chunks` is returned as a string when no React element
63
+ // is used, therefore it's safe to cast this type.
64
+ const result = originalRich(key, values as RichTranslationValues, formats);
65
+
66
+ // When only string chunks are provided to the parser, only strings should be returned here.
67
+ if (typeof result !== 'string') {
68
+ const error = new IntlError(
69
+ IntlErrorCode.FORMATTING_ERROR,
70
+ __DEV__
71
+ ? "`createTranslator` only accepts functions for rich text formatting that receive and return strings.\n\nE.g. t.rich('rich', {b: (chunks) => `<b>${chunks}</b>`})"
72
+ : undefined
73
+ );
74
+
75
+ onError(error);
76
+ return getMessageFallback({error, key, namespace});
77
+ }
78
+
79
+ return result;
80
+ };
81
+
82
+ base.raw = translator.raw;
83
+
84
+ return base;
85
+ }
@@ -0,0 +1,18 @@
1
+ import IntlError from './IntlError';
2
+
3
+ /**
4
+ * Contains defaults that are used for all entry points into the core.
5
+ * See also `InitializedIntlConfiguration`.
6
+ */
7
+
8
+ export function defaultGetMessageFallback(props: {
9
+ error: IntlError;
10
+ key: string;
11
+ namespace?: string;
12
+ }) {
13
+ return [props.namespace, props.key].filter((part) => part != null).join('.');
14
+ }
15
+
16
+ export function defaultOnError(error: IntlError) {
17
+ console.error(error);
18
+ }
@@ -0,0 +1,11 @@
1
+ export {default as AbstractIntlMessages} from './AbstractIntlMessages';
2
+ export {
3
+ default as TranslationValues,
4
+ RichTranslationValues
5
+ } from './TranslationValues';
6
+ export {default as Formats} from './Formats';
7
+ export {default as DateTimeFormatOptions} from './DateTimeFormatOptions';
8
+ export {default as NumberFormatOptions} from './NumberFormatOptions';
9
+ export {default as IntlError, IntlErrorCode} from './IntlError';
10
+ export {default as createTranslator} from './createTranslator';
11
+ export {default as createIntl} from './createIntl';