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