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

feat: Typesafe Global Formats #1346

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions examples/example-app-router/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import en from './messages/en.json';
import {formats} from './src/i18n/request';

type Messages = typeof en;
type Formats = typeof formats;

declare global {
// Use type safe message keys with `next-intl`
interface IntlMessages extends Messages {}
interface IntlFormats extends Formats {}
}
25 changes: 24 additions & 1 deletion examples/example-app-router/src/i18n/request.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import {notFound} from 'next/navigation';
import {Formats} from 'next-intl';
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';

export const formats = {
dateTime: {
short: {
day: 'numeric',
month: 'short',
year: 'numeric'
}
},
number: {
precise: {
maximumFractionDigits: 5
}
},
list: {
enumeration: {
style: 'long',
type: 'conjunction'
}
}
} as const satisfies Partial<Formats>;

export default getRequestConfig(async ({locale}) => {
// Validate that the incoming `locale` parameter is valid
if (!routing.locales.includes(locale as any)) notFound();
Expand All @@ -12,6 +34,7 @@ export default getRequestConfig(async ({locale}) => {
? // When using Turbopack, this will enable HMR for `en`
import('../../messages/en.json')
: import(`../../messages/${locale}.json`))
).default
).default,
formats
};
});
24 changes: 20 additions & 4 deletions packages/use-intl/src/core/createFormatter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,11 @@ export default function createFormatter({
value: Date | number,
/** If a time zone is supplied, the `value` is converted to that time zone.
* Otherwise the user time zone will be used. */
formatOrOptions?: string | DateTimeFormatOptions
formatOrOptions?:
| (keyof IntlFormats['dateTime'] extends string
? IntlFormats['dateTime']
: string)
| DateTimeFormatOptions
) {
return getFormattedValue(
formatOrOptions,
Expand All @@ -183,7 +187,11 @@ export default function createFormatter({
end: Date | number,
/** If a time zone is supplied, the values are converted to that time zone.
* Otherwise the user time zone will be used. */
formatOrOptions?: string | DateTimeFormatOptions
formatOrOptions?:
| (keyof IntlFormats['dateTime'] extends string
? IntlFormats['dateTime']
: string)
| DateTimeFormatOptions
) {
return getFormattedValue(
formatOrOptions,
Expand All @@ -200,7 +208,11 @@ export default function createFormatter({

function number(
value: number | bigint,
formatOrOptions?: string | NumberFormatOptions
formatOrOptions?:
| (keyof IntlFormats['number'] extends string
? IntlFormats['number']
: string)
| NumberFormatOptions
) {
return getFormattedValue(
formatOrOptions,
Expand Down Expand Up @@ -284,7 +296,11 @@ export default function createFormatter({
type FormattableListValue = string | ReactElement;
function list<Value extends FormattableListValue>(
value: Iterable<Value>,
formatOrOptions?: string | Intl.ListFormatOptions
formatOrOptions?:
| (keyof IntlFormats['list'] extends string
dBianchii marked this conversation as resolved.
Show resolved Hide resolved
? IntlFormats['list']
: string)
| Intl.ListFormatOptions
): Value extends string ? string : Iterable<ReactElement> {
const serializedValue: Array<string> = [];
const richValues = new Map<string, Value>();
Expand Down
10 changes: 9 additions & 1 deletion packages/use-intl/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// This type is intended to be overridden
// by the consumer for optional type safety
// by the consumer for optional type safety of messages
declare interface IntlMessages extends Record<string, any> {}

// This type is intended to be overridden
// by the consumer for optional type safety of formats
declare interface IntlFormats {
dateTime: any;
number: any;
list: any;
}

// Temporarly copied here until the "es2020.intl" lib is published.

declare namespace Intl {
Expand Down
Loading
Loading