@havelaer/msgs 0.0.6 → 0.0.8
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/README.md +96 -14
- package/dist/functions.d.ts +43 -78
- package/dist/functions.js +77 -81
- package/dist/{index-DqWXabHs.d.ts → index-ChgITPMW.d.ts} +14 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -8
- package/dist/react.d.ts +8 -11
- package/dist/react.js +53 -19
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
# Msgs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Msgs is a library for internationalization of TypeScript/JavaScript applications. It is built on top of [MessageFormat 2 (MF2)](https://messageformat.unicode.org) and provides a set of functions to format numbers (currency, percentage, unit, etc.), date and time, and other types.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Uses [MessageFormat 2 (MF2)](https://messageformat.unicode.org) for message formatting.
|
|
6
|
+
- TypeScript support.
|
|
7
|
+
- Formatting functions for [numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat), [date and time](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat), [relative time](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat), [list](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat), and more.
|
|
8
|
+
- Optional React hook for React applications.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
- Optional Vite plugin for precompilation.
|
|
9
|
-
- React hook for using the messages.
|
|
10
|
-
- Typesafe messages.
|
|
10
|
+
## React Example
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
```ts
|
|
13
|
+
// formatter.ts
|
|
14
|
+
import { createFormatter } from "@havelaer/msgs";
|
|
15
|
+
import * as functions from "@havelaer/msgs/functions";
|
|
16
|
+
|
|
17
|
+
export default createFormatter({
|
|
18
|
+
locales: {
|
|
19
|
+
"en-US": {},
|
|
20
|
+
"nl-NL": {},
|
|
21
|
+
"fr-FR": {},
|
|
22
|
+
},
|
|
23
|
+
defaultLocale: "en-US",
|
|
24
|
+
options: {
|
|
25
|
+
functions, // several Intl formatters
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
13
29
|
|
|
14
30
|
```ts
|
|
15
31
|
// Greeting.msgs.ts
|
|
16
|
-
import
|
|
32
|
+
import formatter from "~/formatter";
|
|
17
33
|
|
|
18
|
-
export default
|
|
19
|
-
|
|
34
|
+
export default formatter.parse({
|
|
35
|
+
hello: {
|
|
20
36
|
"en-US": "Hello {$name}",
|
|
21
37
|
"nl-NL": "Hallo {$name}",
|
|
22
38
|
"fr-FR": "Bonjour {$name}",
|
|
@@ -32,20 +48,86 @@ import msgs from "./Greeting.msgs";
|
|
|
32
48
|
export function Greeting() {
|
|
33
49
|
const t = useTranslator();
|
|
34
50
|
|
|
35
|
-
return <h1>{t(msgs.
|
|
51
|
+
return <h1>{t(msgs.hello, { name: "John" })}</h1>;
|
|
36
52
|
}
|
|
37
53
|
```
|
|
38
54
|
|
|
39
55
|
```tsx
|
|
40
56
|
// App.tsx
|
|
41
57
|
import { MsgsProvider } from "@havelaer/msgs/react";
|
|
42
|
-
import
|
|
43
|
-
import { Greeting } from "
|
|
58
|
+
import formatter from "~/formatter";
|
|
59
|
+
import { Greeting } from "~/components/Greeting";
|
|
44
60
|
|
|
45
61
|
export default function App() {
|
|
62
|
+
const locale = formatter.resolveLocale(navigator.languages);
|
|
63
|
+
|
|
46
64
|
return (
|
|
47
|
-
<MsgsProvider
|
|
65
|
+
<MsgsProvider formatter={formatter} locale={locale}>
|
|
48
66
|
<Greeting />
|
|
49
67
|
</MsgsProvider>
|
|
50
68
|
);
|
|
51
69
|
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Formatters
|
|
73
|
+
|
|
74
|
+
### `:number`
|
|
75
|
+
|
|
76
|
+
See [NumberFormat documentation (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) for more details.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import formatter from "~/formatter";
|
|
80
|
+
|
|
81
|
+
export default formatter.parse({
|
|
82
|
+
decimalExample: {
|
|
83
|
+
"en-US": "Decimal: {$amount :number style=decimal}", // decimal is the default
|
|
84
|
+
// Example output: "Decimal: 1,234.56"
|
|
85
|
+
},
|
|
86
|
+
currencyExample: {
|
|
87
|
+
"en-US": "Currency: {$amount :number style=currency currency=USD}",
|
|
88
|
+
// Example output: "Currency: $1,234.56"
|
|
89
|
+
},
|
|
90
|
+
percentExample: {
|
|
91
|
+
"en-US": "Percentage: {$amount :number style=percent}",
|
|
92
|
+
// Example output: "Percentage: 12%"
|
|
93
|
+
},
|
|
94
|
+
unitExample: {
|
|
95
|
+
"en-US": "Unit: {$amount :number style=unit unit=liter}",
|
|
96
|
+
// Example output: "Unit: 1.2L"
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `:datetime`
|
|
102
|
+
|
|
103
|
+
See [DateTimeFormat documentation (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) for more details.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import formatter from "~/formatter";
|
|
107
|
+
|
|
108
|
+
export default formatter.parse({
|
|
109
|
+
dateExample: {
|
|
110
|
+
"en-US": "Date: {$eventDate :datetime dateStyle=long}",
|
|
111
|
+
// Example output: "Date: January 15, 2024"
|
|
112
|
+
},
|
|
113
|
+
timeExample: {
|
|
114
|
+
"en-US": "Date: {$eventDate :datetime timeStyle=long}",
|
|
115
|
+
// Example output: "Date: 2:30:00 PM"
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `:relativeTime`
|
|
121
|
+
|
|
122
|
+
See [RelativeTimeFormat documentation (MDN)](https://developer.mozilla.org/en-US/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) for more details.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import formatter from "~/formatter";
|
|
126
|
+
|
|
127
|
+
export default formatter.parse({
|
|
128
|
+
relativeTimeExample: {
|
|
129
|
+
"en-US": "Relative Time: {$days :relativeTime unit=day}",
|
|
130
|
+
// Example output: "Relative Time: 1 day ago"
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
package/dist/functions.d.ts
CHANGED
|
@@ -3,116 +3,81 @@ import { MessageFunction } from "messageformat/functions";
|
|
|
3
3
|
//#region src/functions/index.d.ts
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Date and/or time formatting function for MessageFormat.
|
|
7
|
+
* It's a wrapper around the `Intl.DateTimeFormat` API.
|
|
8
8
|
*
|
|
9
|
-
* @
|
|
9
|
+
* @param options - The Intl.DateTimeFormatOptions object.
|
|
10
|
+
* @param arg - Date string
|
|
10
11
|
*
|
|
11
|
-
* @
|
|
12
|
-
* ```ts
|
|
13
|
-
* "Price: {$price :currency currency=USD}"
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
declare const currency: MessageFunction<any, any>;
|
|
17
|
-
/**
|
|
18
|
-
* Date formatting function for MessageFormat.
|
|
19
|
-
* Formats dates according to locale conventions.
|
|
20
|
-
*
|
|
21
|
-
* @beta
|
|
12
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
|
|
22
13
|
*
|
|
23
14
|
* @example
|
|
24
15
|
* ```ts
|
|
25
|
-
* "
|
|
16
|
+
* "{$date :datetime dateStyle=short timeStyle=short}" { date: new Date("2024-01-15T14:30:00") };
|
|
17
|
+
* // Expected output: "Jan 15, 2024, 2:30 PM"
|
|
26
18
|
* ```
|
|
27
19
|
*/
|
|
28
|
-
declare const
|
|
20
|
+
declare const datetime: MessageFunction<any, any>;
|
|
29
21
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
22
|
+
* Number formatting function for MessageFormat.
|
|
23
|
+
* It's a wrapper around the `Intl.NumberFormat` API.
|
|
32
24
|
*
|
|
33
|
-
* @
|
|
25
|
+
* @param options - The Intl.NumberFormatOptions object.
|
|
26
|
+
* @param arg - An number or a string that can be parsed as a number.
|
|
27
|
+
*
|
|
28
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
|
|
34
29
|
*
|
|
35
30
|
* @example
|
|
36
31
|
* ```ts
|
|
37
|
-
* "
|
|
38
|
-
*
|
|
39
|
-
*/
|
|
40
|
-
declare const datetime: MessageFunction<any, any>;
|
|
41
|
-
/**
|
|
42
|
-
* Percentage formatting function for MessageFormat.
|
|
43
|
-
* Formats numbers as percentages.
|
|
32
|
+
* "{$amount :number style=decimal}" { amount: 0.1234 };
|
|
33
|
+
* // Expected output: "0.1234"
|
|
44
34
|
*
|
|
45
|
-
*
|
|
35
|
+
* "{$amount :number style=currency currency=USD}" { amount: 0.1234 };
|
|
36
|
+
* // Expected output: "$0.12"
|
|
46
37
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* "Progress: {$progress :percent}"
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
declare const percent: MessageFunction<any, any>;
|
|
53
|
-
/**
|
|
54
|
-
* Number formatting function for MessageFormat.
|
|
55
|
-
* Formats numbers with locale-specific formatting.
|
|
38
|
+
* "{$amount :number style=percentage}" { amount: 0.1234 };
|
|
39
|
+
* // Expected output: "12%"
|
|
56
40
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* "Count: {$count :number}"
|
|
41
|
+
* "{$amount :number style=unit unit=liter}" { amount: 0.1234 };
|
|
42
|
+
* // Expected output: "0.12L"
|
|
60
43
|
* ```
|
|
61
44
|
*/
|
|
62
45
|
declare const number: MessageFunction<any, any>;
|
|
63
46
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* @beta
|
|
47
|
+
* Relative time formatting function for MessageFormat.
|
|
48
|
+
* It's a wrapper around the `Intl.RelativeTimeFormat` API.
|
|
68
49
|
*
|
|
69
|
-
* @
|
|
70
|
-
*
|
|
71
|
-
* "Time: {$time :time timeStyle=short}"
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
declare const time: MessageFunction<any, any>;
|
|
75
|
-
/**
|
|
76
|
-
* Unit formatting function for MessageFormat.
|
|
77
|
-
* Formats numbers with units (length, weight, etc.).
|
|
50
|
+
* @param options - The Intl.RelativeTimeFormatOptions object.
|
|
51
|
+
* @param arg - An number or a string that can be parsed as a number.
|
|
78
52
|
*
|
|
79
|
-
* @
|
|
53
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
|
|
80
54
|
*
|
|
81
55
|
* @example
|
|
82
56
|
* ```ts
|
|
83
|
-
* "
|
|
57
|
+
* "{$days :relativeTime unit=day}" { days: -1 };
|
|
58
|
+
* // Expected output: "1 day ago"
|
|
84
59
|
* ```
|
|
85
60
|
*/
|
|
86
|
-
declare const
|
|
61
|
+
declare const relativeTime: MessageFunction<any, any>;
|
|
87
62
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
63
|
+
* List formatting function for MessageFormat.
|
|
64
|
+
* It's a wrapper around the `Intl.ListFormat` API.
|
|
90
65
|
*
|
|
91
|
-
* @
|
|
92
|
-
*
|
|
93
|
-
* "Items: {$count :integer}"
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
declare const integer: MessageFunction<any, any>;
|
|
97
|
-
/**
|
|
98
|
-
* Offset formatting function for MessageFormat.
|
|
99
|
-
* Formats timezone offsets.
|
|
66
|
+
* @remarks
|
|
67
|
+
* Needs tsconfig.json/compilerOptions.lib to include "ES2021" or higher.
|
|
100
68
|
*
|
|
101
|
-
* @
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*/
|
|
106
|
-
declare const offset: MessageFunction<any, any>;
|
|
107
|
-
/**
|
|
108
|
-
* String formatting function for MessageFormat.
|
|
109
|
-
* Formats values as strings with optional transformations.
|
|
69
|
+
* @param options - The Intl.ListFormatOptions object.
|
|
70
|
+
* @param arg - An number
|
|
71
|
+
*
|
|
72
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat
|
|
110
73
|
*
|
|
111
74
|
* @example
|
|
112
75
|
* ```ts
|
|
113
|
-
* "
|
|
76
|
+
* "{$vehicles :list style=long type=conjunction}"
|
|
77
|
+
* { vehicles: ["Motorcycle", "Bus", "Car"] }
|
|
78
|
+
* // Expected output: "Motorcycle, Bus, and Car"
|
|
114
79
|
* ```
|
|
115
80
|
*/
|
|
116
|
-
declare const
|
|
81
|
+
declare const list: MessageFunction<any, any>;
|
|
117
82
|
//#endregion
|
|
118
|
-
export {
|
|
83
|
+
export { datetime, list, number, relativeTime };
|
package/dist/functions.js
CHANGED
|
@@ -1,118 +1,114 @@
|
|
|
1
|
-
import { DefaultFunctions, DraftFunctions } from "messageformat/functions";
|
|
2
|
-
|
|
3
1
|
//#region src/functions/index.ts
|
|
4
2
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @beta
|
|
3
|
+
* Date and/or time formatting function for MessageFormat.
|
|
4
|
+
* It's a wrapper around the `Intl.DateTimeFormat` API.
|
|
9
5
|
*
|
|
10
|
-
* @
|
|
11
|
-
*
|
|
12
|
-
* "Price: {$price :currency currency=USD}"
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
const currency = DraftFunctions.currency;
|
|
16
|
-
/**
|
|
17
|
-
* Date formatting function for MessageFormat.
|
|
18
|
-
* Formats dates according to locale conventions.
|
|
6
|
+
* @param options - The Intl.DateTimeFormatOptions object.
|
|
7
|
+
* @param arg - Date string
|
|
19
8
|
*
|
|
20
|
-
* @
|
|
9
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
|
|
21
10
|
*
|
|
22
11
|
* @example
|
|
23
12
|
* ```ts
|
|
24
|
-
* "
|
|
13
|
+
* "{$date :datetime dateStyle=short timeStyle=short}" { date: new Date("2024-01-15T14:30:00") };
|
|
14
|
+
* // Expected output: "Jan 15, 2024, 2:30 PM"
|
|
25
15
|
* ```
|
|
26
16
|
*/
|
|
27
|
-
const
|
|
17
|
+
const datetime = (ctx, options, arg) => {
|
|
18
|
+
const formatter = new Intl.DateTimeFormat(ctx.locales, options);
|
|
19
|
+
const date = new Date(arg);
|
|
20
|
+
return {
|
|
21
|
+
type: "datetime",
|
|
22
|
+
toString: () => formatter.format(date),
|
|
23
|
+
toParts: () => formatter.formatToParts(date)
|
|
24
|
+
};
|
|
25
|
+
};
|
|
28
26
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* Number formatting function for MessageFormat.
|
|
28
|
+
* It's a wrapper around the `Intl.NumberFormat` API.
|
|
31
29
|
*
|
|
32
|
-
* @
|
|
30
|
+
* @param options - The Intl.NumberFormatOptions object.
|
|
31
|
+
* @param arg - An number or a string that can be parsed as a number.
|
|
33
32
|
*
|
|
34
|
-
* @
|
|
35
|
-
* ```ts
|
|
36
|
-
* "Event at {$datetime :datetime dateStyle=short timeStyle=short}"
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
const datetime = DraftFunctions.datetime;
|
|
40
|
-
/**
|
|
41
|
-
* Percentage formatting function for MessageFormat.
|
|
42
|
-
* Formats numbers as percentages.
|
|
43
|
-
*
|
|
44
|
-
* @beta
|
|
33
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
|
|
45
34
|
*
|
|
46
35
|
* @example
|
|
47
36
|
* ```ts
|
|
48
|
-
* "
|
|
49
|
-
*
|
|
50
|
-
*/
|
|
51
|
-
const percent = DraftFunctions.percent;
|
|
52
|
-
/**
|
|
53
|
-
* Number formatting function for MessageFormat.
|
|
54
|
-
* Formats numbers with locale-specific formatting.
|
|
37
|
+
* "{$amount :number style=decimal}" { amount: 0.1234 };
|
|
38
|
+
* // Expected output: "0.1234"
|
|
55
39
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* "Count: {$count :number}"
|
|
59
|
-
* ```
|
|
60
|
-
*/
|
|
61
|
-
const number = DefaultFunctions.number;
|
|
62
|
-
/**
|
|
63
|
-
* Time formatting function for MessageFormat.
|
|
64
|
-
* Formats time values according to locale conventions.
|
|
40
|
+
* "{$amount :number style=currency currency=USD}" { amount: 0.1234 };
|
|
41
|
+
* // Expected output: "$0.12"
|
|
65
42
|
*
|
|
66
|
-
*
|
|
43
|
+
* "{$amount :number style=percentage}" { amount: 0.1234 };
|
|
44
|
+
* // Expected output: "12%"
|
|
67
45
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* "Time: {$time :time timeStyle=short}"
|
|
46
|
+
* "{$amount :number style=unit unit=liter}" { amount: 0.1234 };
|
|
47
|
+
* // Expected output: "0.12L"
|
|
71
48
|
* ```
|
|
72
49
|
*/
|
|
73
|
-
const
|
|
50
|
+
const number = (ctx, options, arg) => {
|
|
51
|
+
const formatter = new Intl.NumberFormat(ctx.locales, options);
|
|
52
|
+
const number$1 = Number.parseFloat(arg);
|
|
53
|
+
return {
|
|
54
|
+
type: "number",
|
|
55
|
+
toString: () => formatter.format(number$1),
|
|
56
|
+
toParts: () => formatter.formatToParts(number$1)
|
|
57
|
+
};
|
|
58
|
+
};
|
|
74
59
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
60
|
+
* Relative time formatting function for MessageFormat.
|
|
61
|
+
* It's a wrapper around the `Intl.RelativeTimeFormat` API.
|
|
77
62
|
*
|
|
78
|
-
* @
|
|
63
|
+
* @param options - The Intl.RelativeTimeFormatOptions object.
|
|
64
|
+
* @param arg - An number or a string that can be parsed as a number.
|
|
79
65
|
*
|
|
80
|
-
* @
|
|
81
|
-
* ```ts
|
|
82
|
-
* "Distance: {$distance :unit unit=mile}"
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
const unit = DraftFunctions.unit;
|
|
86
|
-
/**
|
|
87
|
-
* Integer formatting function for MessageFormat.
|
|
88
|
-
* Formats numbers as integers.
|
|
66
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
|
|
89
67
|
*
|
|
90
68
|
* @example
|
|
91
69
|
* ```ts
|
|
92
|
-
* "
|
|
70
|
+
* "{$days :relativeTime unit=day}" { days: -1 };
|
|
71
|
+
* // Expected output: "1 day ago"
|
|
93
72
|
* ```
|
|
94
73
|
*/
|
|
95
|
-
const
|
|
74
|
+
const relativeTime = (ctx, options, arg) => {
|
|
75
|
+
const formatter = new Intl.RelativeTimeFormat(ctx.locales, options);
|
|
76
|
+
const number$1 = Number.parseInt(arg);
|
|
77
|
+
if (!options.unit) throw new Error(":relativeTime requires a unit parameter");
|
|
78
|
+
return {
|
|
79
|
+
type: "relativeTime",
|
|
80
|
+
toString: () => formatter.format(number$1, options.unit),
|
|
81
|
+
toParts: () => formatter.formatToParts(number$1, options.unit)
|
|
82
|
+
};
|
|
83
|
+
};
|
|
96
84
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
85
|
+
* List formatting function for MessageFormat.
|
|
86
|
+
* It's a wrapper around the `Intl.ListFormat` API.
|
|
99
87
|
*
|
|
100
|
-
* @
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
* String formatting function for MessageFormat.
|
|
108
|
-
* Formats values as strings with optional transformations.
|
|
88
|
+
* @remarks
|
|
89
|
+
* Needs tsconfig.json/compilerOptions.lib to include "ES2021" or higher.
|
|
90
|
+
*
|
|
91
|
+
* @param options - The Intl.ListFormatOptions object.
|
|
92
|
+
* @param arg - An number
|
|
93
|
+
*
|
|
94
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat
|
|
109
95
|
*
|
|
110
96
|
* @example
|
|
111
97
|
* ```ts
|
|
112
|
-
* "
|
|
98
|
+
* "{$vehicles :list style=long type=conjunction}"
|
|
99
|
+
* { vehicles: ["Motorcycle", "Bus", "Car"] }
|
|
100
|
+
* // Expected output: "Motorcycle, Bus, and Car"
|
|
113
101
|
* ```
|
|
114
102
|
*/
|
|
115
|
-
const
|
|
103
|
+
const list = (ctx, options, arg) => {
|
|
104
|
+
const formatter = new Intl.ListFormat(ctx.locales, options);
|
|
105
|
+
const list$1 = typeof arg === "string" ? arg.split(",") : Array.isArray(arg) ? arg : [];
|
|
106
|
+
return {
|
|
107
|
+
type: "list",
|
|
108
|
+
toString: () => formatter.format(list$1),
|
|
109
|
+
toParts: () => formatter.formatToParts(list$1)
|
|
110
|
+
};
|
|
111
|
+
};
|
|
116
112
|
|
|
117
113
|
//#endregion
|
|
118
|
-
export {
|
|
114
|
+
export { datetime, list, number, relativeTime };
|
|
@@ -2,6 +2,10 @@ import { MessageFormatOptions, MessagePart, Model } from "messageformat";
|
|
|
2
2
|
|
|
3
3
|
//#region src/index.d.ts
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Valid argument values for message formatting.
|
|
7
|
+
*/
|
|
8
|
+
type ArgValue = string | number | boolean | null | undefined;
|
|
5
9
|
/**
|
|
6
10
|
* Represents a nested structure of messages for different locales.
|
|
7
11
|
* Can be either a nested object with string keys or a flat object with locale keys.
|
|
@@ -40,7 +44,7 @@ type Messages<TLocales extends string> = {
|
|
|
40
44
|
* const formatted = config.format("en-US", parsed.greeting, { name: "John" });
|
|
41
45
|
* ```
|
|
42
46
|
*/
|
|
43
|
-
type
|
|
47
|
+
type Formatter<TLocales extends string> = {
|
|
44
48
|
/** Parse message strings into MessageFormat objects */
|
|
45
49
|
parse: <T extends string = TLocales, const U extends Messages<T> = Messages<T>>(messages: U) => U;
|
|
46
50
|
/** Format a message to a string */
|
|
@@ -51,7 +55,7 @@ type MsgsConfig<TLocales extends string> = {
|
|
|
51
55
|
resolveLocale: (userLocales: string[] | readonly string[]) => TLocales;
|
|
52
56
|
};
|
|
53
57
|
/**
|
|
54
|
-
* Creates a message
|
|
58
|
+
* Creates a message formatter object for internationalization.
|
|
55
59
|
*
|
|
56
60
|
* @template TLocales - The supported locale strings
|
|
57
61
|
* @template TDefaultLocale - The default locale (must be one of TLocales)
|
|
@@ -61,24 +65,24 @@ type MsgsConfig<TLocales extends string> = {
|
|
|
61
65
|
* @param config.locales - Locale-specific MessageFormat options
|
|
62
66
|
* @param config.options - Global MessageFormat options applied to all locales
|
|
63
67
|
*
|
|
64
|
-
* @returns A
|
|
68
|
+
* @returns A Formatter object with parsing, formatting, and locale resolution methods
|
|
65
69
|
*
|
|
66
70
|
* @example
|
|
67
71
|
* ```ts
|
|
68
|
-
* const
|
|
72
|
+
* const formatter = createFormatter({
|
|
69
73
|
* defaultLocale: "en-US",
|
|
70
74
|
* locales: {
|
|
71
|
-
* "en-US": {
|
|
72
|
-
* "nl-NL": {
|
|
75
|
+
* "en-US": { --locale specific MessageFormatOptions-- },
|
|
76
|
+
* "nl-NL": { --locale specific MessageFormatOptions-- }
|
|
73
77
|
* },
|
|
74
|
-
* options: {
|
|
78
|
+
* options: { --global MessageFormatOptions-- }
|
|
75
79
|
* });
|
|
76
80
|
* ```
|
|
77
81
|
*/
|
|
78
|
-
declare function
|
|
82
|
+
declare function createFormatter<TLocales extends string, TDefaultLocale extends TLocales>(config: {
|
|
79
83
|
defaultLocale: TDefaultLocale;
|
|
80
84
|
locales: Record<TLocales, MessageFormatOptions<any, any>>;
|
|
81
85
|
options?: MessageFormatOptions<any, any>;
|
|
82
|
-
}):
|
|
86
|
+
}): Formatter<TLocales>;
|
|
83
87
|
//#endregion
|
|
84
|
-
export {
|
|
88
|
+
export { ArgValue, Formatter, Messages, createFormatter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { ArgValue, Formatter, Messages, createFormatter } from "./index-ChgITPMW.js";
|
|
2
|
+
export { ArgValue, Formatter, Messages, createFormatter };
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ function walkAndParse(node, target) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* Creates a message
|
|
63
|
+
* Creates a message formatter object for internationalization.
|
|
64
64
|
*
|
|
65
65
|
* @template TLocales - The supported locale strings
|
|
66
66
|
* @template TDefaultLocale - The default locale (must be one of TLocales)
|
|
@@ -70,21 +70,21 @@ function walkAndParse(node, target) {
|
|
|
70
70
|
* @param config.locales - Locale-specific MessageFormat options
|
|
71
71
|
* @param config.options - Global MessageFormat options applied to all locales
|
|
72
72
|
*
|
|
73
|
-
* @returns A
|
|
73
|
+
* @returns A Formatter object with parsing, formatting, and locale resolution methods
|
|
74
74
|
*
|
|
75
75
|
* @example
|
|
76
76
|
* ```ts
|
|
77
|
-
* const
|
|
77
|
+
* const formatter = createFormatter({
|
|
78
78
|
* defaultLocale: "en-US",
|
|
79
79
|
* locales: {
|
|
80
|
-
* "en-US": {
|
|
81
|
-
* "nl-NL": {
|
|
80
|
+
* "en-US": { --locale specific MessageFormatOptions-- },
|
|
81
|
+
* "nl-NL": { --locale specific MessageFormatOptions-- }
|
|
82
82
|
* },
|
|
83
|
-
* options: {
|
|
83
|
+
* options: { --global MessageFormatOptions-- }
|
|
84
84
|
* });
|
|
85
85
|
* ```
|
|
86
86
|
*/
|
|
87
|
-
function
|
|
87
|
+
function createFormatter(config) {
|
|
88
88
|
function parse(messages) {
|
|
89
89
|
const parsed = {};
|
|
90
90
|
walkAndParse(messages, parsed);
|
|
@@ -118,4 +118,4 @@ function defineConfig(config) {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
//#endregion
|
|
121
|
-
export {
|
|
121
|
+
export { createFormatter };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArgValue, Formatter } from "./index-ChgITPMW.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/index.d.ts
|
|
6
|
-
|
|
7
|
-
* Valid argument values for message formatting.
|
|
8
|
-
*/
|
|
9
|
-
type ArgValue = string | number | boolean | null | undefined;
|
|
6
|
+
|
|
10
7
|
/**
|
|
11
8
|
* Translator object returned by useTranslator hook.
|
|
12
9
|
* Provides methods for formatting messages as strings or JSX.
|
|
@@ -49,20 +46,20 @@ declare function useTranslator(): Translator;
|
|
|
49
46
|
* @template TLocales - The supported locale strings
|
|
50
47
|
*/
|
|
51
48
|
interface MsgsProviderProps<TLocales extends string> {
|
|
52
|
-
/** The message
|
|
53
|
-
|
|
49
|
+
/** The message formatter object */
|
|
50
|
+
formatter: Formatter<TLocales>;
|
|
54
51
|
/** The current locale */
|
|
55
52
|
locale: TLocales;
|
|
56
53
|
/** Child components */
|
|
57
54
|
children: ReactNode;
|
|
58
55
|
}
|
|
59
56
|
/**
|
|
60
|
-
* React provider component that makes message
|
|
57
|
+
* React provider component that makes message formatter and localeavailable to child components.
|
|
61
58
|
*
|
|
62
59
|
* @template TLocales - The supported locale strings
|
|
63
60
|
*
|
|
64
61
|
* @param props - Component props
|
|
65
|
-
* @param props.
|
|
62
|
+
* @param props.formatter - The message formatter object
|
|
66
63
|
* @param props.locale - The current locale
|
|
67
64
|
* @param props.children - Child components
|
|
68
65
|
*
|
|
@@ -70,7 +67,7 @@ interface MsgsProviderProps<TLocales extends string> {
|
|
|
70
67
|
* ```tsx
|
|
71
68
|
* function App() {
|
|
72
69
|
* return (
|
|
73
|
-
* <MsgsProvider
|
|
70
|
+
* <MsgsProvider formatter={Formatter} locale="en-US">
|
|
74
71
|
* <MyComponent />
|
|
75
72
|
* </MsgsProvider>
|
|
76
73
|
* );
|
|
@@ -78,7 +75,7 @@ interface MsgsProviderProps<TLocales extends string> {
|
|
|
78
75
|
* ```
|
|
79
76
|
*/
|
|
80
77
|
declare function MsgsProvider<TLocales extends string>({
|
|
81
|
-
|
|
78
|
+
formatter,
|
|
82
79
|
locale,
|
|
83
80
|
children
|
|
84
81
|
}: MsgsProviderProps<TLocales>): react_jsx_runtime0.JSX.Element;
|
package/dist/react.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { Fragment, createContext, useContext } from "react";
|
|
2
|
-
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/messageParts.ts
|
|
5
|
+
function isTextPart(part) {
|
|
6
|
+
return part.type === "text";
|
|
7
|
+
}
|
|
8
|
+
function isStringPart(part) {
|
|
9
|
+
return part.type === "string";
|
|
10
|
+
}
|
|
5
11
|
function isMarkupPart(part) {
|
|
6
12
|
return part.type === "markup";
|
|
7
13
|
}
|
|
14
|
+
function isBiDiIsolationPart(part) {
|
|
15
|
+
return part.type === "bidiIsolation";
|
|
16
|
+
}
|
|
17
|
+
function isNumberPart(part) {
|
|
18
|
+
return part.type === "number";
|
|
19
|
+
}
|
|
20
|
+
function isDateTimePart(part) {
|
|
21
|
+
return part.type === "datetime";
|
|
22
|
+
}
|
|
23
|
+
function isFallbackPart(part) {
|
|
24
|
+
return part.type === "fallback";
|
|
25
|
+
}
|
|
8
26
|
|
|
9
27
|
//#endregion
|
|
10
28
|
//#region src/react/partsToJSX.tsx
|
|
@@ -30,13 +48,20 @@ function partsToJSX(parts, args, start = 0, stop) {
|
|
|
30
48
|
let i = start;
|
|
31
49
|
while (i < (stop ?? parts.length)) {
|
|
32
50
|
const part = parts[i];
|
|
33
|
-
if (
|
|
51
|
+
if (isTextPart(part)) {
|
|
52
|
+
result.push(/* @__PURE__ */ jsx(Fragment, { children: part.value }, createKey(part, i)));
|
|
53
|
+
i++;
|
|
54
|
+
} else if (isBiDiIsolationPart(part)) i++;
|
|
55
|
+
else if (isStringPart(part)) {
|
|
56
|
+
result.push(/* @__PURE__ */ jsx(Fragment, { children: part.value }, createKey(part, i)));
|
|
57
|
+
i++;
|
|
58
|
+
} else if (isMarkupPart(part) && part.kind === "open") {
|
|
34
59
|
const tagName = part.name;
|
|
35
60
|
const Tag = args?.[tagName] ?? tagName;
|
|
36
61
|
const jsxProps = part.options ?? {};
|
|
37
62
|
let depth = 1;
|
|
38
63
|
let j = i + 1;
|
|
39
|
-
while (j < parts.length) {
|
|
64
|
+
while (j < (stop ?? parts.length)) {
|
|
40
65
|
const p = parts[j];
|
|
41
66
|
if (isMarkupPart(p) && p.name === tagName) {
|
|
42
67
|
if (p.kind === "open") depth++;
|
|
@@ -52,17 +77,27 @@ function partsToJSX(parts, args, start = 0, stop) {
|
|
|
52
77
|
}, createKey(part, i)));
|
|
53
78
|
i = j + 1;
|
|
54
79
|
} else if (isMarkupPart(part) && part.kind === "close") break;
|
|
55
|
-
else if (
|
|
80
|
+
else if (isNumberPart(part) && part.parts) {
|
|
56
81
|
result.push(/* @__PURE__ */ jsx(Fragment, { children: partsToJSX(part.parts, args) }, createKey(part, i)));
|
|
57
82
|
i++;
|
|
58
|
-
} else if (
|
|
59
|
-
result.push(/* @__PURE__ */ jsx(Fragment, { children: part.
|
|
83
|
+
} else if (isDateTimePart(part) && part.parts) {
|
|
84
|
+
result.push(/* @__PURE__ */ jsx(Fragment, { children: partsToJSX(part.parts, args) }, createKey(part, i)));
|
|
60
85
|
i++;
|
|
61
|
-
} else {
|
|
86
|
+
} else if (isFallbackPart(part)) {
|
|
62
87
|
console.warn("Unsupported MessagePart", part);
|
|
63
|
-
result.push(
|
|
88
|
+
result.push(/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
89
|
+
"[",
|
|
90
|
+
part.source,
|
|
91
|
+
"]"
|
|
92
|
+
] }, createKey(part, i)));
|
|
93
|
+
i++;
|
|
94
|
+
} else if ("type" in part && "parts" in part && Array.isArray(part.parts)) {
|
|
95
|
+
result.push(/* @__PURE__ */ jsx(Fragment, { children: partsToJSX(part.parts, args) }, createKey(part, i)));
|
|
96
|
+
i++;
|
|
97
|
+
} else if ("type" in part && "value" in part && typeof part.value === "string") {
|
|
98
|
+
result.push(/* @__PURE__ */ jsx(Fragment, { children: part.value }, createKey(part, i)));
|
|
64
99
|
i++;
|
|
65
|
-
}
|
|
100
|
+
} else throw new Error(`Unhandled MessagePart ${JSON.stringify(part)}`);
|
|
66
101
|
}
|
|
67
102
|
return result;
|
|
68
103
|
}
|
|
@@ -70,7 +105,7 @@ function partsToJSX(parts, args, start = 0, stop) {
|
|
|
70
105
|
//#endregion
|
|
71
106
|
//#region src/react/index.tsx
|
|
72
107
|
const localeContext = createContext(null);
|
|
73
|
-
const
|
|
108
|
+
const formatterContext = createContext(null);
|
|
74
109
|
/**
|
|
75
110
|
* React hook for accessing the message translator.
|
|
76
111
|
* Must be used within a MsgsProvider context.
|
|
@@ -95,10 +130,9 @@ const configContext = createContext(null);
|
|
|
95
130
|
* ```
|
|
96
131
|
*/
|
|
97
132
|
function useTranslator() {
|
|
98
|
-
const config = useContext(
|
|
133
|
+
const config = useContext(formatterContext);
|
|
99
134
|
const locale = useContext(localeContext);
|
|
100
|
-
if (!config) throw new Error("config not set by a MsgsProvider");
|
|
101
|
-
if (typeof locale !== "string") throw new Error("locale not set by a LocaleProvider");
|
|
135
|
+
if (!config) throw new Error("msgs config not set by a MsgsProvider");
|
|
102
136
|
const translator = function translate(msg, args) {
|
|
103
137
|
return config.format(locale, msg, args);
|
|
104
138
|
};
|
|
@@ -110,12 +144,12 @@ function useTranslator() {
|
|
|
110
144
|
return translator;
|
|
111
145
|
}
|
|
112
146
|
/**
|
|
113
|
-
* React provider component that makes message
|
|
147
|
+
* React provider component that makes message formatter and localeavailable to child components.
|
|
114
148
|
*
|
|
115
149
|
* @template TLocales - The supported locale strings
|
|
116
150
|
*
|
|
117
151
|
* @param props - Component props
|
|
118
|
-
* @param props.
|
|
152
|
+
* @param props.formatter - The message formatter object
|
|
119
153
|
* @param props.locale - The current locale
|
|
120
154
|
* @param props.children - Child components
|
|
121
155
|
*
|
|
@@ -123,16 +157,16 @@ function useTranslator() {
|
|
|
123
157
|
* ```tsx
|
|
124
158
|
* function App() {
|
|
125
159
|
* return (
|
|
126
|
-
* <MsgsProvider
|
|
160
|
+
* <MsgsProvider formatter={Formatter} locale="en-US">
|
|
127
161
|
* <MyComponent />
|
|
128
162
|
* </MsgsProvider>
|
|
129
163
|
* );
|
|
130
164
|
* }
|
|
131
165
|
* ```
|
|
132
166
|
*/
|
|
133
|
-
function MsgsProvider({
|
|
134
|
-
return /* @__PURE__ */ jsx(
|
|
135
|
-
value:
|
|
167
|
+
function MsgsProvider({ formatter, locale, children }) {
|
|
168
|
+
return /* @__PURE__ */ jsx(formatterContext.Provider, {
|
|
169
|
+
value: formatter,
|
|
136
170
|
children: /* @__PURE__ */ jsx(localeContext.Provider, {
|
|
137
171
|
value: locale,
|
|
138
172
|
children
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@havelaer/msgs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/havelaer/msgs.git"
|
|
25
|
+
},
|
|
22
26
|
"keywords": [
|
|
23
27
|
"messageformat",
|
|
24
28
|
"MessageFormat2",
|
|
@@ -31,7 +35,9 @@
|
|
|
31
35
|
"scripts": {
|
|
32
36
|
"check": "biome check .",
|
|
33
37
|
"prepublishOnly": "npm run check && npm run build && npm version patch -m 'chore: publishing version %s'",
|
|
34
|
-
"build": "tsdown"
|
|
38
|
+
"build": "tsdown",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest watch"
|
|
35
41
|
},
|
|
36
42
|
"files": [
|
|
37
43
|
"dist"
|
|
@@ -42,12 +48,18 @@
|
|
|
42
48
|
},
|
|
43
49
|
"devDependencies": {
|
|
44
50
|
"@biomejs/biome": "^1.9.4",
|
|
51
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
52
|
+
"@testing-library/react": "^16.3.0",
|
|
45
53
|
"@types/node": "^22.13.9",
|
|
46
54
|
"@types/react": "^19.0.10",
|
|
47
55
|
"@types/react-dom": "^19.0.4",
|
|
56
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
57
|
+
"happy-dom": "^18.0.1",
|
|
48
58
|
"react": "^19.0.0",
|
|
49
59
|
"react-dom": "^19.0.0",
|
|
50
|
-
"
|
|
60
|
+
"tsdown": "^0.15.0",
|
|
61
|
+
"typescript": "~5.7.2",
|
|
62
|
+
"vitest": "^3.2.4"
|
|
51
63
|
},
|
|
52
64
|
"workspaces": [
|
|
53
65
|
".",
|