Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Change numeric option from auto to always for format.relativeTime #765

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions docs/pages/docs/usage/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Depending on if you handle [internationalization in Client- or Server Components
`i18n.ts` can be used to provide configuration for **Server Components**.

```tsx filename="i18n.ts"
import {notFound} from "next/navigation";
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';

// Can be imported from a shared config
Expand Down Expand Up @@ -462,9 +462,12 @@ function getMessageFallback({namespace, key, error}) {
}
}

<NextIntlClientProvider onError={onError} getMessageFallback={getMessageFallback}>
<NextIntlClientProvider
onError={onError}
getMessageFallback={getMessageFallback}
>
...
</NextIntlClientProvider>
</NextIntlClientProvider>;
```

</Tab>
Expand Down Expand Up @@ -504,11 +507,12 @@ return (
As a convenience, there are a couple of hooks that allow you to read global configuration.

```tsx
import {useLocale, useTimeZone, useMessages} from 'next-intl';
import {useLocale, useTimeZone, useMessages, useNow} from 'next-intl';

function Component() {
const locale = useLocale();
const timeZone = useTimeZone();
const messages = useMessages();
const now = useNow();
}
```
55 changes: 35 additions & 20 deletions docs/pages/docs/usage/dates-times.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import PartnerContentLink from 'components/PartnerContentLink';

# Date and time formatting

The formatting of dates and times varies greatly between locales (e.g. "Apr 24, 2023" in `en-US` vs. "24 квіт. 2023 р." in `uk-UA`). By using the formatting capabilities of `next-intl`, you can handle all i18n differences in your Next.js app automatically.
The formatting of dates and times varies greatly between locales (e.g. "Apr 24, 2023" in `en-US` vs. "24 квіт. 2023 р." in `uk-UA`). By using the formatting capabilities of `next-intl`, you can handle i18n differences in your Next.js app automatically.

## Formatting dates and times

You can format plain dates that are not part of a message with the `useFormatter` hook:
You can format plain dates that are not part of a message with the `dateTime` function that is returned from the `useFormatter` hook:

```js
import {useFormatter} from 'next-intl';
Expand Down Expand Up @@ -51,24 +51,26 @@ const twoDaysAgo = subDays(date, 2);

## Formatting relative time

Relative time durations can be formatted with a separate function:
You can format plain dates that are not part of a message with the `relativeTime` function:

```js
import {useFormatter} from 'next-intl';

function Component() {
const format = useFormatter();
const dateTime = new Date('2020-11-20T08:30:00.000Z');
const now = new Date('2020-11-20T10:36:00.000Z');

// Renders "2 hours ago"
format.relativeTime(dateTime, now);
// At 2020-11-20T10:36:00.000Z,
// this will render "2 hours ago"
format.relativeTime(dateTime);
}
```

Note that values are rounded, so e.g. if 100 seconds have passed, "2 minutes ago" will be returned.
Note that values are rounded, so e.g. if 126 minutes have passed, "2 hours ago" will be returned.

If you want to use a specific unit, you can pass options with the second argument:
### Supplying `now`

By default, `relativeTime` will use [the global value for `now`](/docs/usage/configuration#now). If you want to use a different value, you can explicitly pass this as the second parameter.

```js
import {useFormatter} from 'next-intl';
Expand All @@ -78,29 +80,23 @@ function Component() {
const dateTime = new Date('2020-11-20T08:30:00.000Z');
const now = new Date('2020-11-20T10:36:00.000Z');

// Renders "today"
format.relativeTime(dateTime, { now, unit: 'day' });
// Renders "2 hours ago"
format.relativeTime(dateTime, now);
}
```

Supplying `now` is necessary for the function to return consistent results. If you have [configured a global value for `now`](/docs/usage/configuration#now), you can omit the second argument:

```js
format.relativeTime(dateTime);
```

### `useNow`

If you want the relative time value to update over time, you can do so with the `useNow` hook:
If you want the relative time value to update over time, you can do so with [the `useNow` hook](/docs/usage/configuration#retrieve-global-configuration):

```js
import {useNow, useFormatter} from 'next-intl';

function Component() {
// Use the global now value initially …
const now = useNow({
// Update every 10 seconds
// … and update it every 10 seconds
updateInterval: 1000 * 10
});

const format = useFormatter();
const dateTime = new Date('2020-11-20T10:36:01.516Z');

Expand All @@ -109,6 +105,25 @@ function Component() {
}
```

### Customizing the unit

By default, `relativeTime` will pick a unit based on the difference between the passed date and `now` (e.g. 3 seconds, 40 minutes, 4 days, etc.).

If you want to use a specific unit, you can provide options via the second argument:

```js
import {useFormatter} from 'next-intl';

function Component() {
const format = useFormatter();
const dateTime = new Date('2020-03-20T08:30:00.000Z');
const now = new Date('2020-11-22T10:36:00.000Z');

// Renders "247 days ago"
format.relativeTime(dateTime, {now, unit: 'day'});
}
```

## Dates and times within messages

Dates and times can be embedded within messages by using the ICU syntax.
Expand Down
2 changes: 1 addition & 1 deletion examples/example-app-router-playground/tests/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ it('can use `getMessageFallback`', async ({page}) => {
it('can use the core library', async ({page}) => {
await page.goto('/en');
const element = page.getByTestId('CoreLibrary');
await expect(element).toHaveText('Relative time: tomorrow');
await expect(element).toHaveText('Relative time: in 1 day');
});

it('can use `Link` on the server', async ({page}) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"size-limit": [
{
"path": "dist/production/index.react-client.js",
"limit": "12.841 KB"
"limit": "12.855 KB"
},
{
"path": "dist/production/index.react-server.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/use-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"size-limit": [
{
"path": "dist/production/index.js",
"limit": "12.385 kB"
"limit": "12.4 kB"
}
]
}
10 changes: 9 additions & 1 deletion packages/use-intl/src/core/createFormatter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,15 @@ export default function createFormatter({
const value = calculateRelativeTimeValue(seconds, unit);

return new Intl.RelativeTimeFormat(locale, {
numeric: 'auto'
// `numeric: 'auto'` can theoretically produce output like "yesterday",
// but it only works with integers. E.g. -1 day will produce "yesterday",
// but -1.1 days will produce "-1.1 days". Rounding before formatting is
// not desired, as the given dates might cross a threshold were the
// output isn't correct anymore. Example: 2024-01-08T23:00:00.000Z and
// 2024-01-08T01:00:00.000Z would produce "yesterday", which is not the
// case. By using `always` we can ensure correct output. The only exception
// is the formatting of times <1 second as "now".
numeric: unit === 'second' ? 'auto' : 'always'
}).format(value, unit);
} catch (error) {
onError(
Expand Down
Loading
Loading