Skip to content

Commit

Permalink
fix: Correctly parse date skeleton EEEE to a long weekday like "Tue…
Browse files Browse the repository at this point in the history
…sday" (upgrades to `intl-messageformat@10` internally) (#1039)
  • Loading branch information
amannn authored May 2, 2024
1 parent 6d84093 commit d6b5fd2
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 29 deletions.
2 changes: 1 addition & 1 deletion docs/pages/docs/getting-started/pages-router.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Tab, Tabs} from 'nextra-theme-docs';
import Callout from 'components/Callout';

# Next.js internationalization (i18n) with the Pages Router
# Next.js Pages Router internationalization (i18n)

While it's recommended to [use `next-intl` with the App Router](/docs/getting-started/app-router), the Pages Router is still fully supported.

Expand Down
37 changes: 18 additions & 19 deletions docs/pages/docs/usage/dates-times.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -179,32 +179,31 @@ You can customize the formatting by using date skeletons:

```json filename="en.json"
{
"ordered": "Ordered on {orderDate, date, ::yyyyMd}"
// Renders e.g. "Ordered on Jul 9, 2024"
"ordered": "Ordered on {orderDate, date, ::yyyyMMMd}"
}
```

Note the leading `::` that is used to indicate that a skeleton should be used.

**These formats from ICU are supported:**

| Symbol | Meaning |
| :----: | :---------------------------- |
| G | Era designator |
| y | Year |
| M | Month in year |
| L | Stand-alone month in year |
| d | Day in month |
| E | Day of week |
| e | Local day of week |
| c | Stand-alone local day of week |
| a | AM/PM marker |
| h | Hour [1-12] |
| H | Hour [0-23] |
| K | Hour [0-11] |
| k | Hour [1-24] |
| m | Minute |
| s | Second |
| z | Time zone |
| Symbol | Meaning | Pattern | Example |
| :----: | :------------------------------------- | ---------------------------------------- | --------------------------------------------------- |
| G | Era designator (includes the date) | G<br/>GGGG<br/>GGGGG | 7/9/2024 AD<br/>7/9/2024 Anno Domini<br/>7/9/2024 A |
| y | Year | y<br/>yy<br/>yyyy | 2024<br/>24<br/>2024 |
| M | Month in year | M<br/>MM<br/>MMM<br/>MMMM<br/>MMMMM<br/> | 7<br/>07<br/>Jul<br/>July<br/>J |
| d | Day in month | d<br/>dd | 9<br/>09 |
| E | Day of week | E<br/>EEEE<br/>EEEEE | Tue<br/>Tuesday<br/>T |
| h | Hour (1-12) | h<br/>hh | 9 AM<br/>09 AM |
| K | Hour (0-11) | K<br/>KK | 0 AM (12 AM with `h`)<br/>00 AM |
| H | Hour (0-23) | HH | 09 |
| k | Hour (1-24) | kk | 24 (00 with `H`) |
| m | Minute (2 digits if used with seconds) | m<br/>mmss | 6<br/>06:03 |
| s | Second (2 digits if used with minutes) | s<br/>mmss | 3<br/>06:03 |
| z | Time zone | z<br/>zzzz | GMT+2<br/>Central European Summer Time |

Patterns can be combined with each other, therefore e.g. `yyyyMMMd` would return "Jul 9, 2024".

### Custom date and time formats

Expand Down
6 changes: 3 additions & 3 deletions packages/next-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@
"size-limit": [
{
"path": "dist/production/index.react-client.js",
"limit": "13.055 KB"
"limit": "15.735 KB"
},
{
"path": "dist/production/index.react-server.js",
"limit": "13.765 KB"
"limit": "16.5 KB"
},
{
"path": "dist/production/navigation.react-client.js",
Expand All @@ -134,7 +134,7 @@
},
{
"path": "dist/production/server.react-server.js",
"limit": "13.05 KB"
"limit": "15.645 KB"
},
{
"path": "dist/production/middleware.js",
Expand Down
4 changes: 2 additions & 2 deletions packages/use-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
],
"dependencies": {
"@formatjs/ecma402-abstract": "^1.11.4",
"intl-messageformat": "^9.3.18"
"intl-messageformat": "^10.5.11"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
Expand All @@ -90,7 +90,7 @@
"size-limit": [
{
"path": "dist/production/index.js",
"limit": "12.575 kB"
"limit": "15.235 kB"
}
]
}
6 changes: 5 additions & 1 deletion packages/use-intl/src/core/createBaseTranslator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,11 @@ function createBaseTranslatorImpl<
{
formatters: {
getNumberFormat(locales, options) {
return new Intl.NumberFormat(locales, options);
return new Intl.NumberFormat(
locales,
// `useGrouping` was changed from a boolean later to a string enum or boolean, the type definition is outdated (https://tc39.es/proposal-intl-numberformat-v3/#grouping-enum-ecma-402-367)
options as Intl.NumberFormatOptions
);
},
getDateTimeFormat(locales, options) {
// Workaround for https://github.com/formatjs/formatjs/issues/4279
Expand Down
99 changes: 99 additions & 0 deletions packages/use-intl/test/core/createTranslator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,105 @@ it('throws an error for non-alphanumeric value names', () => {
expect(error.code).toBe('INVALID_MESSAGE');
});

describe('dates in messages', () => {
it.each([
['G', '7/9/2024 AD'], // 🤔 Includes date
['GG', '7/9/2024 AD'], // 🤔 Includes date
['GGGG', '7/9/2024 Anno Domini'], // 🤔 Includes date
['GGGGG', '7/9/2024 A'], // 🤔 Includes date

['y', '2024'],
['yy', '24'],
['yyyy', '2024'],

['M', '7'],
['MM', '07'],
['MMM', 'Jul'],
['MMMM', 'July'],
['MMMMM', 'J'],

// Same as M
['L', '7'],
['LL', '07'],
['LLL', 'Jul'],
['LLLL', 'July'],
['LLLLL', 'J'],

['d', '9'],
['dd', '09'],

['E', 'Tue'],
['EE', 'Tue'],
['EEE', 'Tue'],
['EEEE', 'Tuesday'],
['EEEEE', 'T'],
// ['e', '7'] // 🤔 Not supported
// ['ee', '07'] // 🤔 Not supported
// ['eee', 'Jul'] // 🤔 Not supported

// ['eeee', 'Tuesday'], // ❌ "Tue"
// ['eeeee', 'T'], // ❌ "Tuesday"
// ['eeeeee', 'Tu'] // ❌ "T"

// ['c', '7'] // 🤔 Not supported
// ['cc', '07'], // 🤔 Not supported
// ['ccc', 'Jul'] // 🤔 Not supported

// ['cccc', 'Tuesday'] // ❌ "Tue"
// ['ccccc', 'T'], // ❌ "Tuesday"
// ['cccccc', 'Tu'] // ❌ "T"

// 🤔 Only in combination with time?
// ['a', 'PM'] // ❌ "7/9/2024"
// ['aa', 'PM'] // ❌ "7/9/2024"
// ['aaa', 'PM'] // ❌ "7/9/2024"
// ['aaaa', 'PM'] // ❌ "7/9/2024"
// ['aaaaa', 'PM'] // ❌ "7/9/2024"

['h', '12 AM', '2024-07-09T22:00:00.000Z'],
['h', '9 AM'],
['hh', '09 AM'],

// ['H', '9'], // ❌ "09"
['HH', '09'],
['HH', '00', '2024-07-09T22:00:00.000Z'],

['K', '0 AM', '2024-07-09T22:00:00.000Z'],
['KK', '00 AM', '2024-07-09T22:00:00.000Z'],
['K', '9 AM'],
['KK', '09 AM'],

// ['k', '9'], // ❌ "09"
['kk', '09'],
['kk', '24', '2024-07-09T22:00:00.000Z'],

['m', '6'],
// ['mm', '06'] // ❌ "6"

['s', '3'],
// ['ss', '03'], // ❌ "3"
['mmss', '06:03'],

['z', '7/9/2024, GMT+2'], // 🤔 Includes date
['zz', '7/9/2024, GMT+2'], // 🤔 Includes date
['zzz', '7/9/2024, GMT+2'], // 🤔 Includes date
['zzzz', '7/9/2024, Central European Summer Time'], // 🤔 Includes date

['yyyyMMMd', 'Jul 9, 2024'],
[
'GGGGyyyyMMMMEdhmszzzz',
'Tue, July 9, 2024 Anno Domini at 9:06:03 AM Central European Summer Time'
]
])('%s: %s', (value, expected, dateString = '2024-07-09T07:06:03.320Z') => {
const date = new Date(dateString);
const t = createTranslator({
locale: 'en',
messages: {date: `{date, date, ::${value}}`}
});
expect(t('date', {date})).toBe(expected);
});
});

describe('t.rich', () => {
it('can translate a message to a ReactNode', () => {
const t = createTranslator({
Expand Down
2 changes: 1 addition & 1 deletion packages/use-intl/test/react/useTranslations.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ describe('error handling', () => {

const error: IntlError = onError.mock.calls[0][0];
expect(error.message).toBe(
'INVALID_MESSAGE: Expected "date", "number", "plural", "select", "selectordinal", or "time" but "c" found.'
'INVALID_MESSAGE: INVALID_ARGUMENT_TYPE ({value, currency})'
);
expect(error.code).toBe(IntlErrorCode.INVALID_MESSAGE);
screen.getByText('price');
Expand Down
47 changes: 45 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d6b5fd2

Please sign in to comment.