API
@k8ordo/i18nがexportする関数と型の一覧です。入口は@k8ordo/i18nの1つだけで、Server Componentのモジュールからも、Client Componentのモジュールからもimportできます。
このページの内容
defineLocales
import元 @k8ordo/i18n
ロケールの集合を定義します。ロケールの一覧に依存するものは、どれもこの戻り値から読みます。
defineLocales(
definitions: Record<string, LocaleDefinition>,
options?: LocalesOptions<D>,
): Locales<L, D>引数
definitionsRecord<string, LocaleDefinition>- ロケールのタグをキーに、そのロケールの
timeZoneとdirを値にしたオブジェクト。タグはBCP 47で書き、書いた順がallの順になります。 optionsLocalesOptions<D>defaultで既定のロケールを選びます。省くと、先頭に書いたロケールが既定です。
戻り値
Locales<L, D> — ロケールの一覧と、一覧から作った道具をまとめた集合。
注意
- ロケールが1つも無い定義と、一覧に無い
defaultは、呼んだ時点でTypeErrorを投げます。BCP 47ではないタグと、実行環境が知らないタイムゾーンも同じです。欠けたtimeZoneと、ltrかrtlではないdirもエラーになります。 - 定義した集合は
globalThisに登録され、messageはそこから既定のロケールを読みます。後から定義した集合が勝つので、アプリで1回だけ呼びます。 - キーはリテラル型のまま推論されるので、
as constは要りません。
i18n.tsexport const locales = defineLocales(
{
ja: { timeZone: 'Asia/Tokyo', dir: 'ltr' },
en: { timeZone: 'America/New_York', dir: 'ltr' },
},
{ default: 'en' },
);Locales
import元 @k8ordo/i18n
defineLocalesが返す集合の型です。Lはタグの和集合、Dは既定のロケールのタグです。
type Locales<L extends string, D extends L> =
IntlFormats & {
all: readonly L[];
definitions: Readonly<Record<L, LocaleDefinition>>;
default: D;
is: (value: unknown) => value is L;
negotiate: (requested: Iterable<string>) => L;
negotiateRequest: (
request: Request,
options?: NegotiateRequestOptions,
) => L;
localize: (pathname: string, locale: L) => string;
delocalize: (pathname: string) => Delocalized<L>;
paths: (patterns: readonly string[]) => string[];
paramsSchema: LocaleParamsSchema<L>;
getLocale: () => L;
run: <T>(locale: L, fn: () => T) => T;
};フィールド
allreadonly L[]- 書いた順に並んだタグ。
definitionsReadonly<Record<L, LocaleDefinition>>- ロケールごとの
{ timeZone, dir }。defineLocalesに渡したオブジェクトそのものです。 defaultD- 既定のロケール。何もロケールを指名していないときと、交渉でどのロケールも一致しなかったときに使います。
is(value: unknown) => value is L- 一覧に含まれるかを確かめる型ガード。大文字と小文字を区別するので、
is('EN')はfalseです。 negotiate(requested) => L- 希望の並びから、集合のロケールを1つ選びます。タグごとに同じタグ、同じ言語の順に探し、最後まで無ければ既定のロケールを返します。
negotiateRequest(request, options?) => LRequestからロケールを選びます。options.cookieで名前を渡したCookieを先に読み、次にAccept-Languageを読みます。localize(pathname, locale) => string- pathnameの前にロケールの区間を付けます。
'/ui'は'/en/ui'に、'/'は'/en'になります。 delocalize(pathname) => Delocalized<L>- 先頭のロケールの区間を外します。先頭の区間がロケールでなければ、
localeはnullです。 paths(patterns) => string[]@k8ordo/staticのpathsオプション。/:localeの区間を持つパターンを、ロケールの数だけ展開します。paramsSchemaLocaleParamsSchema<L>[locale]の区間のスキーマ。サーバーでは、受け付けたロケールがそのページの描画のロケールになります。getLocale() => L- 描画中のロケール。サーバーでは受け付けたロケール、ブラウザではURLの先頭の区間で、どちらも無ければ既定のロケールです。
run(locale, fn) => Tfnと、そこから始まる処理をlocaleで動かし、fnの戻り値を返します。サーバーでだけ使えます。dateTimeFormat …IntlFormats- 今のロケールの
Intlのオブジェクトを返すメンバー。下のIntlFormatsを見てください。
注意
- どのメンバーも
thisを使わないので、export const { getLocale } = localesのように取り出して使えます。 runをブラウザで呼ぶと、locales.run: in the browser the URL is the locale — navigate insteadを投げます。AsyncLocalStorageを取り出せないサーバーのランタイムでは、runと、ロケールを受け付けるときのparamsSchemaがエラーを投げます。
IntlFormats
import元 @k8ordo/i18n
集合のうち、今のロケールのIntlのオブジェクトを返すメンバーです。返すのはIntlのオブジェクトそのものです。
type IntlFormats = {
dateTimeFormat: (
options?: LocaleDateTimeFormatOptions,
) => Intl.DateTimeFormat;
numberFormat: (
options?: Intl.NumberFormatOptions,
) => Intl.NumberFormat;
relativeTimeFormat: (
options?: Intl.RelativeTimeFormatOptions,
) => Intl.RelativeTimeFormat;
pluralRules: (
options?: Intl.PluralRulesOptions,
) => Intl.PluralRules;
listFormat: (options?: Intl.ListFormatOptions) => Intl.ListFormat;
};フィールド
dateTimeFormat(options?) => Intl.DateTimeFormat- 今のロケールと、そのロケールの
timeZoneで書くIntl.DateTimeFormat。 numberFormat(options?) => Intl.NumberFormat- 今のロケールの
Intl.NumberFormat。 relativeTimeFormat(options?) => Intl.RelativeTimeFormat- 今のロケールの
Intl.RelativeTimeFormat。 pluralRules(options?) => Intl.PluralRules- 今のロケールの
Intl.PluralRules。 listFormat(options?) => Intl.ListFormat- 今のロケールの
Intl.ListFormat。
注意
- ロケールとオプションの組ごとに1つだけ作り、次からは同じものを返します。オプションはJSONにして見分けます。
- どれもフックではないので、Server ComponentでもClient Componentでも、文言の関数の中でも呼べます。
message
import元 @k8ordo/i18n
1つの文言を、すべてのロケールの文とともに宣言します。返す関数は、呼ばれた場所のロケールの文を返します。
function message(variants: Variants<string>): Message;
function message<A extends readonly unknown[]>(
variants: Variants<(...args: A) => string>,
): Message<A>;引数
variantsVariants<string> | Variants<(...args: A) => string>- ロケールごとの文。すべてのロケールを文で書くか、すべてを同じ引数の関数で書きます。
戻り値
Message<A> — 引数を受け取り、今のロケールの文を返す関数。
注意
Registerを登録すると、ロケールが欠けた宣言は型エラーになります。型をすり抜けた欠けは、読んだ時点でmessage: no text for "en" in ["ja"]のようなTypeErrorを投げます。- 何もロケールを指名していないときは、既定のロケールの文を返します。この環境で集合がまだ定義されていなければ、既定のロケールの代わりに最初に書いた文を返します。
- 引数の型は、注釈を書いたロケールから決まります。どこにも書かないと
unknownになるので、少なくとも1つに書くか、message<[name: string]>({ … })のように型引数で渡します。 - 宣言には副作用が無いので、どこからも呼ばれない文言はバンドラが取り除きます。
messages/nav.tsexport const home = message({ ja: 'ホーム', en: 'Home' });
export const greeting = message({
ja: (name: string) => `こんにちは、${name}さん`,
en: (name) => `Hello, ${name}`,
});Message
import元 @k8ordo/i18n
messageが返す関数の型です。文言をpropsで受け取るコンポーネントは、この型で受け取ります。
type Message<A extends readonly unknown[] = []> = (
...args: A
) => string;注意
- 関数なので、Server ComponentからClient Componentへpropsで渡せません。そこでは、呼んだ結果の文字列を渡します。
parseAcceptLanguage
import元 @k8ordo/i18n
Accept-Languageヘッダーを、negotiateに渡せる希望の並びにします。
parseAcceptLanguage(header: string | null): string[]引数
headerstring | null- ヘッダーの値。
request.headers.get('accept-language')をそのまま渡せます。
戻り値
string[] — 重みの大きい順に並べたタグ。ヘッダーが無いか空白だけなら、空の配列。
注意
- 重みが同じタグは、ヘッダーの順を保ちます。重みが0以下のタグと
*は落とします。 - タグそのものは確かめません。BCP 47ではないタグは、
negotiateが飛ばします。
parseAcceptLanguage('en-US;q=0.8, ja, en;q=0.9');
// ['ja', 'en', 'en-US']currentLocale
import元 @k8ordo/i18n
アプリの集合が決める、今のロケールを返します。アプリのことを知らないライブラリが、アプリのロケールに合わせるために使います。
currentLocale(): RegisteredLocale | null戻り値
RegisteredLocale | null — 指名されたロケール。無ければ集合の既定のロケールで、この環境で集合が定義されていなければnull。
注意
@k8ordo/uiのコンポーネントは、自分で描く文言のロケールをこれで決めています。- アプリのコードでは、集合の型が付く
locales.getLocale()を使います。
Register
import元 @k8ordo/i18n
アプリのロケールの型を、パッケージに伝えるための宣言です。アプリで1回だけマージします。
declare module '@k8ordo/i18n' {
interface Register {
locale: LocaleOf<typeof locales>;
}
}注意
- マージされるための型なので、
typeではなくinterfaceで書きます。 - 登録する前は
RegisteredLocaleがstringなので、ロケールの欠けは型エラーになりません。
RegisteredLocale
import元 @k8ordo/i18n
Registerに登録したロケールの和集合です。登録する前はstringです。
type RegisteredLocale = Register extends {
locale: infer L extends string;
}
? L
: string;Variants
import元 @k8ordo/i18n
Registerに登録したロケールごとに、1つずつ値を持つオブジェクトの型です。messageが受け取るのもこの型です。
type Variants<V> = Readonly<Record<RegisteredLocale, V>>;注意
- 言語の切り替えに並べる言語名のように、文言以外でもロケールごとに漏れなくそろえたい値に使えます。
LocaleOf
import元 @k8ordo/i18n
集合のタグの和集合を取り出します。Registerのlocaleにも、この型を書きます。
type LocaleOf<Ls> =
Ls extends Locales<infer L, infer _D> ? L : never;LocaleDefinition
import元 @k8ordo/i18n
1つのロケールに添える情報です。どちらも実行環境から導かずに書きます。
type LocaleDefinition = {
timeZone: string;
dir: 'ltr' | 'rtl';
};フィールド
timeZonestring- そのロケールで日付を表示するIANAのタイムゾーン。サーバーでもブラウザでも同じタイムゾーンで書くので、日付がずれません。
dir'ltr' | 'rtl'- そのロケールの文字の向き。
<html dir>に使います。
LocalesOptions
import元 @k8ordo/i18n
defineLocalesの第2引数の型です。
type LocalesOptions<D extends string> = { default?: D };フィールド
defaultD- 既定のロケール。一覧のタグでなければならず、省くと先頭に書いたロケールです。
NegotiateRequestOptions
import元 @k8ordo/i18n
negotiateRequestの第2引数の型です。
type NegotiateRequestOptions = { cookie?: string };フィールド
cookiestring- 訪問者が選んだ言語を持つCookieの名前。省くと
Accept-Languageだけを読みます。
Delocalized
import元 @k8ordo/i18n
delocalizeが返す値の型です。
type Delocalized<L extends string> = {
locale: L | null;
pathname: string;
};フィールド
localeL | null- 先頭の区間が示すロケール。先頭の区間がロケールでなければ
null。 pathnamestring- 区間を外した残りのpathname。ロケールの区間しか無いときは
/です。
LocaleParamsSchema
import元 @k8ordo/i18n
paramsSchemaの型です。スキーマのライブラリに頼らないよう、Standard Schemaの形をこのパッケージ自身が宣言しています。
type LocaleParamsSchema<L extends string> = {
'~standard': {
version: 1;
vendor: '@k8ordo/i18n';
validate: (value: unknown) =>
| { value: { locale: L } }
| { issues: { message: string; path: ['locale'] }[] };
};
};注意
- 検証は同期的です。一覧に無いロケールには、
path: ['locale']のissueを返します。 - 受け付けた値は
{ locale }だけです。ほかのパラメータは、後に続くスキーマのために文字列のまま残ります。
LocaleDateTimeFormatOptions
import元 @k8ordo/i18n
dateTimeFormatのオプションの型です。Intl.DateTimeFormatOptionsのうち、timeZoneだけを受け付けません。
type LocaleDateTimeFormatOptions = Intl.DateTimeFormatOptions & {
timeZone?: never;
};注意
asでtimeZoneを通しても、ロケールのtimeZoneで上書きします。