@formatjs/intl 2.3.2 → 2.4.1

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 (104) hide show
  1. package/index.d.ts +17 -0
  2. package/index.d.ts.map +1 -0
  3. package/index.js +40 -0
  4. package/lib/index.d.ts +17 -0
  5. package/lib/index.d.ts.map +1 -0
  6. package/lib/index.js +17 -0
  7. package/lib/src/create-intl.d.ts +11 -0
  8. package/lib/src/create-intl.d.ts.map +1 -0
  9. package/lib/src/create-intl.js +55 -0
  10. package/lib/src/dateTime.d.ts +38 -0
  11. package/lib/src/dateTime.d.ts.map +1 -0
  12. package/lib/src/dateTime.js +117 -0
  13. package/lib/src/displayName.d.ts +6 -0
  14. package/lib/src/displayName.d.ts.map +1 -0
  15. package/lib/src/displayName.js +22 -0
  16. package/lib/src/error.d.ts +34 -0
  17. package/lib/src/error.d.ts.map +1 -0
  18. package/lib/src/error.js +88 -0
  19. package/lib/src/list.d.ts +11 -0
  20. package/lib/src/list.d.ts.map +1 -0
  21. package/lib/src/list.js +61 -0
  22. package/lib/src/message.d.ts +16 -0
  23. package/lib/src/message.d.ts.map +1 -0
  24. package/lib/src/message.js +103 -0
  25. package/lib/src/number.d.ts +17 -0
  26. package/lib/src/number.d.ts.map +1 -0
  27. package/lib/src/number.js +54 -0
  28. package/lib/src/plural.d.ts +7 -0
  29. package/lib/src/plural.d.ts.map +1 -0
  30. package/lib/src/plural.js +19 -0
  31. package/lib/src/relativeTime.d.ts +7 -0
  32. package/lib/src/relativeTime.d.ts.map +1 -0
  33. package/lib/src/relativeTime.js +29 -0
  34. package/lib/src/types.d.ts +103 -0
  35. package/lib/src/types.d.ts.map +1 -0
  36. package/lib/src/types.js +1 -0
  37. package/lib/src/utils.d.ts +12 -0
  38. package/lib/src/utils.d.ts.map +1 -0
  39. package/lib/src/utils.js +162 -0
  40. package/package.json +10 -10
  41. package/src/create-intl.d.ts +11 -0
  42. package/src/create-intl.d.ts.map +1 -0
  43. package/src/create-intl.js +59 -0
  44. package/src/dateTime.d.ts +38 -0
  45. package/src/dateTime.d.ts.map +1 -0
  46. package/src/dateTime.js +126 -0
  47. package/src/displayName.d.ts +6 -0
  48. package/src/displayName.d.ts.map +1 -0
  49. package/src/displayName.js +26 -0
  50. package/src/error.d.ts +34 -0
  51. package/src/error.d.ts.map +1 -0
  52. package/src/error.js +91 -0
  53. package/src/list.d.ts +11 -0
  54. package/src/list.d.ts.map +1 -0
  55. package/src/list.js +66 -0
  56. package/src/message.d.ts +16 -0
  57. package/src/message.d.ts.map +1 -0
  58. package/src/message.js +107 -0
  59. package/src/number.d.ts +17 -0
  60. package/src/number.d.ts.map +1 -0
  61. package/src/number.js +60 -0
  62. package/src/plural.d.ts +7 -0
  63. package/src/plural.d.ts.map +1 -0
  64. package/src/plural.js +23 -0
  65. package/src/relativeTime.d.ts +7 -0
  66. package/src/relativeTime.d.ts.map +1 -0
  67. package/src/relativeTime.js +33 -0
  68. package/src/types.d.ts +103 -0
  69. package/src/types.d.ts.map +1 -0
  70. package/src/types.js +2 -0
  71. package/src/utils.d.ts +12 -0
  72. package/src/utils.d.ts.map +1 -0
  73. package/src/utils.js +169 -0
  74. package/BUILD +0 -79
  75. package/CHANGELOG.md +0 -537
  76. package/index.ts +0 -38
  77. package/src/create-intl.ts +0 -159
  78. package/src/dateTime.ts +0 -204
  79. package/src/displayName.ts +0 -54
  80. package/src/error.ts +0 -106
  81. package/src/list.ts +0 -119
  82. package/src/message.ts +0 -246
  83. package/src/number.ts +0 -101
  84. package/src/plural.ts +0 -45
  85. package/src/relativeTime.ts +0 -74
  86. package/src/types.ts +0 -234
  87. package/src/utils.ts +0 -177
  88. package/tests/__snapshots__/formatDate.test.ts.snap +0 -13
  89. package/tests/__snapshots__/formatMessage.test.ts.snap +0 -45
  90. package/tests/__snapshots__/formatNumber.test.ts.snap +0 -13
  91. package/tests/__snapshots__/formatRelativeTime.test.ts.snap +0 -19
  92. package/tests/__snapshots__/formatTime.test.ts.snap +0 -13
  93. package/tests/create-intl.test.ts +0 -60
  94. package/tests/error.test.ts +0 -25
  95. package/tests/formatDate.test.ts +0 -167
  96. package/tests/formatDisplayNames.test.ts +0 -63
  97. package/tests/formatList.test.ts +0 -51
  98. package/tests/formatMessage.test.ts +0 -495
  99. package/tests/formatNumber.test.ts +0 -172
  100. package/tests/formatPlural.test.ts +0 -119
  101. package/tests/formatRelativeTime.test.ts +0 -145
  102. package/tests/formatTime.test.ts +0 -234
  103. package/tests/global.d.ts +0 -7
  104. package/tsconfig.json +0 -5
@@ -1,159 +0,0 @@
1
- import {IntlCache, IntlShape, IntlConfig, ResolvedIntlConfig} from './types'
2
- import {createFormatters, DEFAULT_INTL_CONFIG} from './utils'
3
- import {InvalidConfigError, MissingDataError} from './error'
4
- import {formatNumber, formatNumberToParts} from './number'
5
- import {formatRelativeTime} from './relativeTime'
6
- import {
7
- formatDate,
8
- formatDateToParts,
9
- formatTime,
10
- formatTimeToParts,
11
- formatDateTimeRange,
12
- } from './dateTime'
13
- import {formatPlural} from './plural'
14
- import {formatMessage} from './message'
15
- import {formatList, formatListToParts} from './list'
16
- import {formatDisplayName} from './displayName'
17
- import {MessageFormatElement} from '@formatjs/icu-messageformat-parser'
18
-
19
- export interface CreateIntlFn<
20
- T = string,
21
- C extends IntlConfig<T> = IntlConfig<T>,
22
- S extends IntlShape<T> = IntlShape<T>
23
- > {
24
- (config: C, cache?: IntlCache): S
25
- }
26
-
27
- function messagesContainString(
28
- messages: Record<string, string> | Record<string, MessageFormatElement[]>
29
- ): messages is Record<string, MessageFormatElement[]> {
30
- const firstMessage = messages ? messages[Object.keys(messages)[0]] : undefined
31
-
32
- return typeof firstMessage === 'string'
33
- }
34
-
35
- function verifyConfigMessages<T = string>(config: IntlConfig<T>) {
36
- if (
37
- config.onWarn &&
38
- config.defaultRichTextElements &&
39
- messagesContainString(config.messages || {})
40
- ) {
41
- config.onWarn(`[@formatjs/intl] "defaultRichTextElements" was specified but "message" was not pre-compiled.
42
- Please consider using "@formatjs/cli" to pre-compile your messages for performance.
43
- For more details see https://formatjs.io/docs/getting-started/message-distribution`)
44
- }
45
- }
46
-
47
- /**
48
- * Create intl object
49
- * @param config intl config
50
- * @param cache cache for formatter instances to prevent memory leak
51
- */
52
- export function createIntl<T = string>(
53
- config: IntlConfig<T>,
54
- cache?: IntlCache
55
- ): IntlShape<T> {
56
- const formatters = createFormatters(cache)
57
- const resolvedConfig: ResolvedIntlConfig<T> = {
58
- ...DEFAULT_INTL_CONFIG,
59
- ...config,
60
- }
61
-
62
- const {locale, defaultLocale, onError} = resolvedConfig
63
- if (!locale) {
64
- if (onError) {
65
- onError(
66
- new InvalidConfigError(
67
- `"locale" was not configured, using "${defaultLocale}" as fallback. See https://formatjs.io/docs/react-intl/api#intlshape for more details`
68
- )
69
- )
70
- }
71
- // Since there's no registered locale data for `locale`, this will
72
- // fallback to the `defaultLocale` to make sure things can render.
73
- // The `messages` are overridden to the `defaultProps` empty object
74
- // to maintain referential equality across re-renders. It's assumed
75
- // each <FormattedMessage> contains a `defaultMessage` prop.
76
- resolvedConfig.locale = resolvedConfig.defaultLocale || 'en'
77
- } else if (!Intl.NumberFormat.supportedLocalesOf(locale).length && onError) {
78
- onError(
79
- new MissingDataError(
80
- `Missing locale data for locale: "${locale}" in Intl.NumberFormat. Using default locale: "${defaultLocale}" as fallback. See https://formatjs.io/docs/react-intl#runtime-requirements for more details`
81
- )
82
- )
83
- } else if (
84
- !Intl.DateTimeFormat.supportedLocalesOf(locale).length &&
85
- onError
86
- ) {
87
- onError(
88
- new MissingDataError(
89
- `Missing locale data for locale: "${locale}" in Intl.DateTimeFormat. Using default locale: "${defaultLocale}" as fallback. See https://formatjs.io/docs/react-intl#runtime-requirements for more details`
90
- )
91
- )
92
- }
93
-
94
- verifyConfigMessages(resolvedConfig)
95
- return {
96
- ...resolvedConfig,
97
- formatters,
98
- formatNumber: formatNumber.bind(
99
- null,
100
- resolvedConfig,
101
- formatters.getNumberFormat
102
- ),
103
- formatNumberToParts: formatNumberToParts.bind(
104
- null,
105
- resolvedConfig,
106
- formatters.getNumberFormat
107
- ),
108
- formatRelativeTime: formatRelativeTime.bind(
109
- null,
110
- resolvedConfig,
111
- formatters.getRelativeTimeFormat
112
- ),
113
- formatDate: formatDate.bind(
114
- null,
115
- resolvedConfig,
116
- formatters.getDateTimeFormat
117
- ),
118
- formatDateToParts: formatDateToParts.bind(
119
- null,
120
- resolvedConfig,
121
- formatters.getDateTimeFormat
122
- ),
123
- formatTime: formatTime.bind(
124
- null,
125
- resolvedConfig,
126
- formatters.getDateTimeFormat
127
- ),
128
- formatDateTimeRange: formatDateTimeRange.bind(
129
- null,
130
- resolvedConfig,
131
- formatters.getDateTimeFormat
132
- ),
133
- formatTimeToParts: formatTimeToParts.bind(
134
- null,
135
- resolvedConfig,
136
- formatters.getDateTimeFormat
137
- ),
138
- formatPlural: formatPlural.bind(
139
- null,
140
- resolvedConfig,
141
- formatters.getPluralRules
142
- ),
143
- // @ts-expect-error TODO: will get to this later
144
- formatMessage: formatMessage.bind(null, resolvedConfig, formatters),
145
- // @ts-expect-error TODO: will get to this later
146
- $t: formatMessage.bind(null, resolvedConfig, formatters),
147
- formatList: formatList.bind(null, resolvedConfig, formatters.getListFormat),
148
- formatListToParts: formatListToParts.bind(
149
- null,
150
- resolvedConfig,
151
- formatters.getListFormat
152
- ),
153
- formatDisplayName: formatDisplayName.bind(
154
- null,
155
- resolvedConfig,
156
- formatters.getDisplayNames
157
- ),
158
- }
159
- }
package/src/dateTime.ts DELETED
@@ -1,204 +0,0 @@
1
- import {Formatters, IntlFormatters, CustomFormats, OnErrorFn} from './types'
2
-
3
- import {filterProps, getNamedFormat} from './utils'
4
- import {IntlError, IntlErrorCode} from './error'
5
- import {DateTimeFormat} from '@formatjs/ecma402-abstract'
6
-
7
- const DATE_TIME_FORMAT_OPTIONS: Array<keyof Intl.DateTimeFormatOptions> = [
8
- 'formatMatcher',
9
-
10
- 'timeZone',
11
- 'hour12',
12
-
13
- 'weekday',
14
- 'era',
15
- 'year',
16
- 'month',
17
- 'day',
18
- 'hour',
19
- 'minute',
20
- 'second',
21
- 'timeZoneName',
22
- 'hourCycle',
23
- 'dateStyle',
24
- 'timeStyle',
25
- 'calendar',
26
- // 'dayPeriod',
27
- 'numberingSystem',
28
- 'fractionalSecondDigits',
29
- ]
30
-
31
- export function getFormatter(
32
- {
33
- locale,
34
- formats,
35
- onError,
36
- timeZone,
37
- }: {
38
- locale: string
39
- timeZone?: string
40
- formats: CustomFormats
41
- onError: OnErrorFn
42
- },
43
- type: 'date' | 'time',
44
- getDateTimeFormat: Formatters['getDateTimeFormat'],
45
- options: Parameters<IntlFormatters['formatDate']>[1] = {}
46
- ): DateTimeFormat {
47
- const {format} = options
48
- const defaults = {
49
- ...(timeZone && {timeZone}),
50
- ...(format && getNamedFormat(formats!, type, format, onError)),
51
- }
52
-
53
- let filteredOptions = filterProps(
54
- options,
55
- DATE_TIME_FORMAT_OPTIONS,
56
- defaults
57
- ) as Intl.DateTimeFormatOptions
58
-
59
- if (
60
- type === 'time' &&
61
- !filteredOptions.hour &&
62
- !filteredOptions.minute &&
63
- !filteredOptions.second &&
64
- !filteredOptions.timeStyle &&
65
- !filteredOptions.dateStyle
66
- ) {
67
- // Add default formatting options if hour, minute, or second isn't defined.
68
- filteredOptions = {...filteredOptions, hour: 'numeric', minute: 'numeric'}
69
- }
70
-
71
- return getDateTimeFormat(locale, filteredOptions)
72
- }
73
-
74
- export function formatDate(
75
- config: {
76
- locale: string
77
- timeZone?: string
78
- formats: CustomFormats
79
- onError: OnErrorFn
80
- },
81
- getDateTimeFormat: Formatters['getDateTimeFormat'],
82
- ...[value, options = {}]: Parameters<IntlFormatters['formatDate']>
83
- ): string {
84
- const date = typeof value === 'string' ? new Date(value || 0) : value
85
- try {
86
- return getFormatter(config, 'date', getDateTimeFormat, options).format(date)
87
- } catch (e) {
88
- config.onError(
89
- new IntlError(IntlErrorCode.FORMAT_ERROR, 'Error formatting date.', e)
90
- )
91
- }
92
-
93
- return String(date)
94
- }
95
-
96
- export function formatTime(
97
- config: {
98
- locale: string
99
- timeZone?: string
100
- formats: CustomFormats
101
- onError: OnErrorFn
102
- },
103
- getDateTimeFormat: Formatters['getDateTimeFormat'],
104
- ...[value, options = {}]: Parameters<IntlFormatters['formatTime']>
105
- ): string {
106
- const date = typeof value === 'string' ? new Date(value || 0) : value
107
-
108
- try {
109
- return getFormatter(config, 'time', getDateTimeFormat, options).format(date)
110
- } catch (e) {
111
- config.onError(
112
- new IntlError(IntlErrorCode.FORMAT_ERROR, 'Error formatting time.', e)
113
- )
114
- }
115
-
116
- return String(date)
117
- }
118
-
119
- export function formatDateTimeRange(
120
- config: {
121
- locale: string
122
- timeZone?: string
123
- onError: OnErrorFn
124
- },
125
- getDateTimeFormat: Formatters['getDateTimeFormat'],
126
- ...[from, to, options = {}]: Parameters<IntlFormatters['formatDateTimeRange']>
127
- ): string {
128
- const {timeZone, locale, onError} = config
129
-
130
- const filteredOptions = filterProps(
131
- options,
132
- DATE_TIME_FORMAT_OPTIONS,
133
- timeZone ? {timeZone} : {}
134
- ) as Intl.DateTimeFormatOptions
135
-
136
- try {
137
- return getDateTimeFormat(locale, filteredOptions).formatRange(from, to)
138
- } catch (e) {
139
- onError(
140
- new IntlError(
141
- IntlErrorCode.FORMAT_ERROR,
142
- 'Error formatting date time range.',
143
- e
144
- )
145
- )
146
- }
147
-
148
- return String(from)
149
- }
150
-
151
- export function formatDateToParts(
152
- config: {
153
- locale: string
154
- timeZone?: string
155
- formats: CustomFormats
156
- onError: OnErrorFn
157
- },
158
- getDateTimeFormat: Formatters['getDateTimeFormat'],
159
- ...[value, options = {}]: Parameters<IntlFormatters['formatDate']>
160
- ): Intl.DateTimeFormatPart[] {
161
- const date = typeof value === 'string' ? new Date(value || 0) : value
162
- try {
163
- return getFormatter(
164
- config,
165
- 'date',
166
- getDateTimeFormat,
167
- options
168
- ).formatToParts(date)
169
- } catch (e) {
170
- config.onError(
171
- new IntlError(IntlErrorCode.FORMAT_ERROR, 'Error formatting date.', e)
172
- )
173
- }
174
-
175
- return []
176
- }
177
-
178
- export function formatTimeToParts(
179
- config: {
180
- locale: string
181
- timeZone?: string
182
- formats: CustomFormats
183
- onError: OnErrorFn
184
- },
185
- getDateTimeFormat: Formatters['getDateTimeFormat'],
186
- ...[value, options = {}]: Parameters<IntlFormatters['formatTimeToParts']>
187
- ): Intl.DateTimeFormatPart[] {
188
- const date = typeof value === 'string' ? new Date(value || 0) : value
189
-
190
- try {
191
- return getFormatter(
192
- config,
193
- 'time',
194
- getDateTimeFormat,
195
- options
196
- ).formatToParts(date)
197
- } catch (e) {
198
- config.onError(
199
- new IntlError(IntlErrorCode.FORMAT_ERROR, 'Error formatting time.', e)
200
- )
201
- }
202
-
203
- return []
204
- }
@@ -1,54 +0,0 @@
1
- import {Formatters, IntlFormatters, OnErrorFn} from './types'
2
- import {filterProps} from './utils'
3
- import {
4
- DisplayNamesOptions,
5
- DisplayNames as IntlDisplayNames,
6
- } from '@formatjs/intl-displaynames'
7
- import {FormatError, ErrorCode} from 'intl-messageformat'
8
- import {IntlErrorCode, IntlError} from './error'
9
-
10
- const DISPLAY_NAMES_OPTONS: Array<keyof DisplayNamesOptions> = [
11
- 'style',
12
- 'type',
13
- 'fallback',
14
- ]
15
-
16
- export function formatDisplayName(
17
- {
18
- locale,
19
- onError,
20
- }: {
21
- locale: string
22
- onError: OnErrorFn
23
- },
24
- getDisplayNames: Formatters['getDisplayNames'],
25
- value: Parameters<IntlFormatters['formatDisplayName']>[0],
26
- options: Parameters<IntlFormatters['formatDisplayName']>[1]
27
- ): string | undefined {
28
- const DisplayNames: typeof IntlDisplayNames = (Intl as any).DisplayNames
29
- if (!DisplayNames) {
30
- onError(
31
- new FormatError(
32
- `Intl.DisplayNames is not available in this environment.
33
- Try polyfilling it using "@formatjs/intl-displaynames"
34
- `,
35
- ErrorCode.MISSING_INTL_API
36
- )
37
- )
38
- }
39
- const filteredOptions = filterProps(
40
- options,
41
- DISPLAY_NAMES_OPTONS
42
- ) as DisplayNamesOptions
43
- try {
44
- return getDisplayNames(locale, filteredOptions).of(value)
45
- } catch (e) {
46
- onError(
47
- new IntlError(
48
- IntlErrorCode.FORMAT_ERROR,
49
- 'Error formatting display name.',
50
- e
51
- )
52
- )
53
- }
54
- }
package/src/error.ts DELETED
@@ -1,106 +0,0 @@
1
- import {MessageDescriptor} from './types'
2
-
3
- export enum IntlErrorCode {
4
- FORMAT_ERROR = 'FORMAT_ERROR',
5
- UNSUPPORTED_FORMATTER = 'UNSUPPORTED_FORMATTER',
6
- INVALID_CONFIG = 'INVALID_CONFIG',
7
- MISSING_DATA = 'MISSING_DATA',
8
- MISSING_TRANSLATION = 'MISSING_TRANSLATION',
9
- }
10
-
11
- export class IntlError<
12
- T extends IntlErrorCode = IntlErrorCode.FORMAT_ERROR
13
- > extends Error {
14
- public readonly code: T
15
-
16
- constructor(code: T, message: string, exception?: Error | unknown) {
17
- const err = exception
18
- ? exception instanceof Error
19
- ? exception
20
- : new Error(String(exception))
21
- : undefined
22
- super(
23
- `[@formatjs/intl Error ${code}] ${message}
24
- ${err ? `\n${err.message}\n${err.stack}` : ''}`
25
- )
26
- this.code = code
27
- // @ts-ignore just so we don't need to declare dep on @types/node
28
- if (typeof Error.captureStackTrace === 'function') {
29
- // @ts-ignore just so we don't need to declare dep on @types/node
30
- Error.captureStackTrace(this, IntlError)
31
- }
32
- }
33
- }
34
-
35
- export class UnsupportedFormatterError extends IntlError<IntlErrorCode.UNSUPPORTED_FORMATTER> {
36
- constructor(message: string, exception?: Error | unknown) {
37
- super(IntlErrorCode.UNSUPPORTED_FORMATTER, message, exception)
38
- }
39
- }
40
-
41
- export class InvalidConfigError extends IntlError<IntlErrorCode.INVALID_CONFIG> {
42
- constructor(message: string, exception?: Error | unknown) {
43
- super(IntlErrorCode.INVALID_CONFIG, message, exception)
44
- }
45
- }
46
-
47
- export class MissingDataError extends IntlError<IntlErrorCode.MISSING_DATA> {
48
- constructor(message: string, exception?: Error | unknown) {
49
- super(IntlErrorCode.MISSING_DATA, message, exception)
50
- }
51
- }
52
-
53
- export class IntlFormatError extends IntlError<IntlErrorCode.FORMAT_ERROR> {
54
- public readonly descriptor?: MessageDescriptor
55
- constructor(message: string, locale: string, exception?: Error | unknown) {
56
- super(
57
- IntlErrorCode.FORMAT_ERROR,
58
- `${message}
59
- Locale: ${locale}
60
- `,
61
- exception
62
- )
63
- }
64
- }
65
-
66
- export class MessageFormatError extends IntlFormatError {
67
- public readonly descriptor?: MessageDescriptor
68
- constructor(
69
- message: string,
70
- locale: string,
71
- descriptor?: MessageDescriptor,
72
- exception?: Error | unknown
73
- ) {
74
- super(
75
- `${message}
76
- MessageID: ${descriptor?.id}
77
- Default Message: ${descriptor?.defaultMessage}
78
- Description: ${descriptor?.description}
79
- `,
80
- locale,
81
- exception
82
- )
83
- this.descriptor = descriptor
84
- }
85
- }
86
-
87
- export class MissingTranslationError extends IntlError<IntlErrorCode.MISSING_TRANSLATION> {
88
- public readonly descriptor?: MessageDescriptor
89
- constructor(descriptor: MessageDescriptor, locale: string) {
90
- super(
91
- IntlErrorCode.MISSING_TRANSLATION,
92
- `Missing message: "${descriptor.id}" for locale "${locale}", using ${
93
- descriptor.defaultMessage
94
- ? `default message (${
95
- typeof descriptor.defaultMessage === 'string'
96
- ? descriptor.defaultMessage
97
- : descriptor.defaultMessage
98
- .map((e: any) => e.value ?? JSON.stringify(e))
99
- .join()
100
- })`
101
- : 'id'
102
- } as fallback.`
103
- )
104
- this.descriptor = descriptor
105
- }
106
- }
package/src/list.ts DELETED
@@ -1,119 +0,0 @@
1
- import {Formatters, IntlFormatters, OnErrorFn} from './types'
2
- import {filterProps} from './utils'
3
- import type IntlListFormat from '@formatjs/intl-listformat'
4
- import type {Part} from '@formatjs/intl-listformat'
5
- import type {IntlListFormatOptions} from '@formatjs/intl-listformat'
6
- import {FormatError, ErrorCode} from 'intl-messageformat'
7
- import {IntlError, IntlErrorCode} from './error'
8
-
9
- const LIST_FORMAT_OPTIONS: Array<keyof IntlListFormatOptions> = [
10
- 'type',
11
- 'style',
12
- ]
13
-
14
- const now = Date.now()
15
-
16
- function generateToken(i: number): string {
17
- return `${now}_${i}_${now}`
18
- }
19
-
20
- export function formatList(
21
- opts: {
22
- locale: string
23
- onError: OnErrorFn
24
- },
25
- getListFormat: Formatters['getListFormat'],
26
- values: ReadonlyArray<string>,
27
- options: Parameters<IntlFormatters['formatList']>[1]
28
- ): string
29
- export function formatList<T>(
30
- opts: {
31
- locale: string
32
- onError: OnErrorFn
33
- },
34
- getListFormat: Formatters['getListFormat'],
35
- values: Parameters<IntlFormatters['formatList']>[0],
36
- options: Parameters<IntlFormatters['formatList']>[1] = {}
37
- ): Array<T | string> | T | string {
38
- const results = formatListToParts(
39
- opts,
40
- getListFormat,
41
- values,
42
- options
43
- ).reduce((all: Array<string | T>, el) => {
44
- const val = el.value
45
- if (typeof val !== 'string') {
46
- all.push(val)
47
- } else if (typeof all[all.length - 1] === 'string') {
48
- all[all.length - 1] += val
49
- } else {
50
- all.push(val)
51
- }
52
- return all
53
- }, [])
54
- return results.length === 1 ? results[0] : results
55
- }
56
-
57
- export function formatListToParts<T>(
58
- opts: {
59
- locale: string
60
- onError: OnErrorFn
61
- },
62
- getListFormat: Formatters['getListFormat'],
63
- values: ReadonlyArray<string>,
64
- options: Parameters<IntlFormatters['formatList']>[1]
65
- ): Part[]
66
- export function formatListToParts<T>(
67
- {
68
- locale,
69
- onError,
70
- }: {
71
- locale: string
72
- onError: OnErrorFn
73
- },
74
- getListFormat: Formatters['getListFormat'],
75
- values: Parameters<IntlFormatters['formatList']>[0],
76
- options: Parameters<IntlFormatters['formatList']>[1] = {}
77
- ): Part<T | string>[] {
78
- const ListFormat: typeof IntlListFormat = (Intl as any).ListFormat
79
- if (!ListFormat) {
80
- onError(
81
- new FormatError(
82
- `Intl.ListFormat is not available in this environment.
83
- Try polyfilling it using "@formatjs/intl-listformat"
84
- `,
85
- ErrorCode.MISSING_INTL_API
86
- )
87
- )
88
- }
89
- const filteredOptions = filterProps(
90
- options,
91
- LIST_FORMAT_OPTIONS
92
- ) as IntlListFormatOptions
93
-
94
- try {
95
- const richValues: Record<string, T> = {}
96
- const serializedValues = values.map((v, i) => {
97
- if (typeof v === 'object') {
98
- const id = generateToken(i)
99
- richValues[id] = v as any
100
- return id
101
- }
102
- return String(v)
103
- })
104
- return getListFormat(locale, filteredOptions)
105
- .formatToParts(serializedValues)
106
- .map(part =>
107
- part.type === 'literal'
108
- ? part
109
- : {...part, value: richValues[part.value] || part.value}
110
- )
111
- } catch (e) {
112
- onError(
113
- new IntlError(IntlErrorCode.FORMAT_ERROR, 'Error formatting list.', e)
114
- )
115
- }
116
-
117
- // @ts-ignore
118
- return values
119
- }