👶

私の知らなかったLocaleの世界

に公開

はじめに

こんにちは、ふるしょうです。
DRESS CODEの開発で、JavaScriptの国際化対応 APIに関するMDNを読み進めていたときに、localeが「country」ではなく「region」として扱われている点が気になり、ECMAScriptの仕様や関連する国際標準を掘り下げました。
その過程で、localeの背後にある複雑な構造と、WEB標準の設計思想に触れることができました。
本記事では、その調査で得た知見を共有しつつ、i18n対応に取り組むエンジニアが直面する課題の具体的な視点を紹介します。

i18nとは?

i18nは、多様な言語や地域、文化に適応させるための設計プロセスです。
このプロセスは、単にテキストを翻訳するだけに留まらず、以下のような要素を包括的に扱います

  • 書記体系: ユーザーが母語でコンテンツを理解できるようにする。

  • 日付と時刻: 日本なら「2023年10月25日」、米国なら「October 25, 2023」のように、地域特有のフォーマットを提供。

  • 測定単位:「¥1,234」や「$1,234.56」など、地域ごとの記号や桁区切りを適用。

私が初めてi18nに取り組んだとき、翻訳ファイルを作れば終わりだと考えていました。しかし、実際にはユーザーが「自然に使える」体験を提供するには、これらの要素を丁寧に設計する必要がありました。
i18nのゴールは、グローバルなユーザー誰もが自然と直感的に利用できる感覚を与えることだと捉えています。

localeとは?

localeとは、ユーザーの言語や地域、文化的な設定を表す識別子です。例えば、「ja-JP」は日本語(日本)を、「en-US」は英語(アメリカ)を示します。localeは、言語コード(ISO 639-1)と地域コード(ISO 3166-1 alpha-2)の組み合わせで構成されることが一般的で、必要に応じてスクリプトやカレンダーなどの追加情報が含まれる場合もあります。
WEBアプリケーションでは、localeを活用することで、ユーザーに適した言語でメッセージを表示したり、地域に応じた日付や通貨のフォーマットを提供したりできます。

ECMAScriptにおける国際化の基盤

ECMAScriptの国際化機能は、ECMA-402で標準化されています。この仕様では、IntlオブジェクトとLocaleオブジェクトが中心的な役割を果たします。以下では、これらのオブジェクトの役割と違いを詳しく見ていきます。

https://ecma-international.org/publications-and-standards/standards/ecma-402/

Intlオブジェクト

Intlオブジェクトは、JavaScriptのグローバルオブジェクトであり、国際化に関する一連の機能を提供します。主に以下のコンストラクタを通じて、localeに応じたフォーマットや比較を実現します。

  • Intl.DateTimeFormat: 日付や時刻のフォーマット
  • Intl.NumberFormat: 数値や通貨のフォーマット
  • Intl.Collator: 文字列の比較とソート
  • Intl.RelativeTimeFormat: 相対的な時間のフォーマット(例: 「2日前」)
  • Intl.PluralRules: 複数形のルール処理

これらのコンストラクタは、localeを引数として受け取り、その設定に基づいた結果を返します。たとえば、以下はIntl.DateTimeFormatを使った例です。

const date = new Date();
const japaneseFormatter = new Intl.DateTimeFormat("ja-JP");
console.log(japaneseFormatter.format(date)); // 日本式の日付フォーマット

const usFormatter = new Intl.DateTimeFormat("en-US");
console.log(usFormatter.format(date)); // "10/10/2023"

このように、Intlオブジェクトはlocaleに応じた柔軟なフォーマットを提供し、国際化の基盤となります。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl

Intl.Localeオブジェクト

Intl.Localeは、特定のlocaleをオブジェクトとして表現するためのコンストラクタです。Intlオブジェクトが機能を提供するのに対し、Localeオブジェクトはlocaleの詳細な情報を保持し、開発者がそれを操作・参照できるようにします。

Localeオブジェクトの基本的な使い方

以下は、Localeオブジェクトを作成し、そのプロパティにアクセスする例です。

const locale = new Intl.Locale("ja-JP");
console.log(locale.language); // "ja"(言語コード)
console.log(locale.region); // "JP"(地域コード)
console.log(locale.toString()); // "ja-JP"(locale全体)

主なプロパティ
Localeオブジェクトは、以下のようなプロパティを持ちます。

  • language: 言語コード(例: "ja")
  • region: 地域コード(例: "JP")
  • script: スクリプトコード(例: "Latn")
  • baseName: 基本的なlocale名(例: "ja-JP")
  • calendar: 使用するカレンダー(例: "gregory")
  • numberingSystem: 数値システム(例: "latn")

これらのプロパティは、localeの構成要素を分解してアクセスする際に役立ちます。
私が驚いたのは、Localeが単なるデータホルダーではなく、拡張性を持った設計だということです。
たとえば、カレンダーを指定する-u-ca-のようなオプションを扱えるのは、Intl単体では難しい柔軟性です。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale

IntlとLocaleの違いと連携

IntlとLocaleは、それぞれ異なる役割を持ちながら、連携して国際化を実現します。

  • Intl: 機能を提供する枠組み。locale情報を引数として受け取り、フォーマットや比較を実行。
  • Locale: localeそのものを表現するオブジェクト。詳細な情報を保持し、必要に応じてIntlに渡される。

たとえば、LocaleオブジェクトをIntlコンストラクタに渡して使うことができます。

const locale = new Intl.Locale("fr-FR");
const formatter = new Intl.DateTimeFormat(locale);
console.log(formatter.format(new Date())); // "10/10/2023"(フランス式)

このように、Localeで定義したlocaleをIntlで活用することで、柔軟かつ正確な国際化が可能になります。

region vs country: なぜ「region」なのか?

私がi18nに取り組む中で最も引っかかったのは、IntlやLocaleが「country」ではなく「region」という単語を使う点です。
直感的には「国=country」で良いと感じたんですが、ECMAScriptやWEB標準を調べていくと、この選択には深い理由がありました。

BCP 47

Intlの設計は、BCP 47(RFC 5646 Tags for Identifying Languages)に準拠しています。BCP 47は、言語タグの構造を定義する標準で、localeを「言語+地域+スクリプト」などのサブタグで表現します。RFC 5646の原文を見てみましょう:

Section 2.2.4 Region Subtag
Region subtags are used to indicate linguistic variations associated
with or appropriate to a specific country, territory, or region.
Typically, a region subtag is used to indicate variations such as
regional dialects or usage, or region-specific spelling conventions.
It can also be used to indicate that content is expressed in a way
that is appropriate for use throughout a region, for instance,
Spanish content tailored to be useful throughout Latin America.

引用元
訳すと、「地域サブタグは、特定の国、領土、または地域に関連する、または適切な言語のバリエーションを示すために使用されます。通常、地域サブタグは地域の方言や使用法、地域特有の綴り規則などのバリエーションを示すために使用されます。また、コンテンツが地域全体で使用するのに適切な方法で表現されていることを示すためにも使用できます。たとえば、ラテンアメリカ全体で有用となるように調整されたスペイン語のコンテンツなどです。」

これらが国に限定されない点がポイントです。たとえばes-419の「419」は特定の国ではなく、ラテンアメリカ全体を指します。原文の「a specific country, territory, or region」という柔軟性が、countryではなくregionを採用した理由なんですね。

ISO 3166−1とUN M.49

Regionサブタグは、具体的にISO 3166−1とUN M.49の規格に基づいています。

ISO 3166−1は、国やその行政区画(例:州や県)を表すための国際的に標準化されたコード体系を定めた国際規格です。

ISO 3166−1
Country Codes
The International Standard for country codes and codes for their subdivisions
The purpose of ISO 3166 is to define internationally recognized codes of letters and/or numbers that we can use when we refer to countries and their subdivisions. However, it does not define the names of countries – this information comes from United Nations sources (Terminology Bulletin Country Names and the Country and Region Codes for Statistical Use maintained by the United Nations Statistics Divisions).
Using codes saves time and avoids errors as instead of using a country’s name (which will change depending on the language being used), we can use a combination of letters and/or numbers that are understood all over the world.
For example, all national postal organizations throughout the world exchange international mail in containers identified with the relevant country code. Internet domain name systems use the codes to define top-level domain names such as “.fr” for France, “.au” for Australia. In addition, in machine-readable passports, the codes are used to determine the nationality of the user and, when we send money from one bank to another, the country codes are a way to identify where the bank is based.

引用元

国や地域を一意に識別するための短いコード(文字や数字の組み合わせ) を提供することで、言語や地域による国の名称の違い(例:日本は英語でJapan、フランス語でJaponなど)を気にせず、誰でも同じコードを使って正確に国を参照できます。
国の正式名称は国連の資料(国連統計部の「国名および地域コード」や国連の「国名用語集」)に基づいており、ISO 3166は、あくまでこれらの名称に対応するコードを提供することを目的としています。

UN M.49(Standard Country or Area Codes for Statistical Use)は国連統計部(UNSD)が開発・維持するコード体系で、国、地理的地域、経済的グループ、その他のグループを一意に識別するために使用されます。

UN M.49
This is the online version of the United Nations publication "Standard Country or Area Codes for Statistical Use" originally published as Series M, No. 49 and now commonly referred to as the M49 standard. The print version of the standard was issued last in 1999 and previously in 1996 , 1982 , 1975 and 1970 . M49 is prepared by the Statistics Division of the United Nations Secretariat primarily for use in its publications and databases.
The assignment of countries or areas to specific groupings is for statistical convenience and does not imply any assumption regarding political or other affiliation of countries or territories by the United Nations.

引用元

UN M.49は、国連統計部が統計用途のために国や地域を3桁のコードで識別する規格で、1970年から始まり、印刷版の最終発行は1999年です。現在はオンライン版として維持され、国連の出版物やデータベースで使用されます。グループ分けは統計の便宜上行われ、政治的意味合いは一切持たないことが強調されています。

私が「es-419」を初めて見たとき、「国じゃないの?」と混乱しましたが、UN M.49の存在を知って納得しました。たとえば、ラテンアメリカ向けのスペイン語コンテンツを一括で提供する際、「419」は非常に便利です。

Unicode拡張

さらに、Unicode Technical Standard #35では、localeの拡張について説明されています。

Unicode Locale Identifier
A Unicode locale identifier is composed of a Unicode language identifier plus (optional) locale extensions. It has the following structure. The semantics of the U and T extensions are explained in Unicode BCP 47 U Extension and Unicode BCP 47 T Extension. Other extensions and private use extensions are supported for pass-through. The following table defines syntactically well-formed identifiers: they are not necessarily valid identifiers. For additional validity criteria, see the links on the right.

引用元
localeの拡張は、Unicode locale識別子のオプションのコンポーネントであり、基本的な言語識別子(言語、文字体系、地域、バリアント)に加えて、追加のカスタマイズ可能なデータを指定します。これにより詳細な好みや設定を反映できます。

カレンダーシステムの指定を例示すると、
このように、「-u-」でカレンダーや数値体系を指定できる仕組みが該当します。

const locale = new Intl.Locale("ja-JP-u-ca-japanese");
console.log(locale.calendar); // "japanese"(和暦)

これをIntlで使うと

const formatter = new Intl.DateTimeFormat("ja-JP-u-ca-japanese");
console.log(formatter.format(new Date(2025, 4, 15))); // "R7/5/15"

「region」が採用されたのは、こうした拡張性と互換性を確保するため。和暦対応を求められるようなケースでは、この仕様が役に立ちます。

ECMAScriptでの明記

ECMAScriptの国際化API(ECMA-402)ではECMAScriptがBCP 47のRegionサブタグを採用し、処理・返却することで地域ごとの設定が正確に扱われるようにしていることを明記しています。

6.2 Language Tags
This specification identifies locales using Unicode BCP 47 locale identifiers as defined by Unicode Technical Standard #35 Part 1 Core, Section 3.3 BCP 47 Conformance, and its algorithms refer to Unicode locale nonterminals defined in the grammars of Section 3 Unicode Language and Locale Identifiers. Each such identifier can also be referred to as a language tag, and is in fact a valid language tag as that term is used in BCP 47. A locale identifier in canonical form as specified in Unicode Technical Standard #35 Part 1 Core, Section 3.2.1 Canonical Unicode Locale Identifiers is referred to as a "Unicode canonicalized locale identifier".

6.2 Language Tagsでは、localeはUnicode BCP 47 locale idを使用して識別されると述べています。

ECMAScriptでRegionサブタグの使用例

locale識別子 言語 Regionサブタグ 対象
en-US 英語 US Tアメリカ英語(ISO 3166-1)
es-419 スペイン語 419 ラテンアメリカ向けスペイン語(UN M.49)
de-DE ドイツ語 DE ドイツのドイツ語(ISO 3166-1))

WEBアプリケーションでのlocaleの取り扱い

WEB開発では、localeを正しく取得し、それに基づいてコンテンツやフォーマットを動的に調整することが求められます。以下に、実践的な手法を紹介します。

  1. ブラウザからlocaleを取得
    ユーザーのlocaleは、ブラウザの設定から取得できます。JavaScriptでは以下のプロパティが利用可能です。
  • navigator.language: ユーザーの優先言語(例: "ja-JP")
  • navigator.languages: 優先言語のリスト(例: ["ja-JP", "en-US"])
const userLocale = navigator.language;
console.log(userLocale); // "ja-JP"(環境依存)
  1. Network層での国際化
    Webアプリケーションでは、ネットワーク層でのlocale情報も重要です。ユーザーの言語や地域の好みは、HTTPのAccept-Languageヘッダーでサーバーに伝わります。

Accept-Languageの仕様

RFC 7231(HTTP/1.1)1に基づくAccept-Languageの例を見てみましょう

Accept-Language: ja-JP, en-US;q=0.9, zh-CN;q=0.8

Section 5.3.5 Accept-Language
The "Accept-Language" header field can be used by user agents to indicate the set of natural languages that are preferred in the response.

「ユーザーの好みを表す品質値(q値)」がポイントで、サーバーはこれを基に最適なコンテンツを選びます。

import express from 'express';
const app = express();

app.use((req, res, next) => {
  const locale = req.headers['accept-language'] || 'en-US';
  console.log(`User locale: ${locale}`);
  next();
});

app.listen(3000);

DRESS CODEの場合は、それぞれのレイヤーで言語の優位性を判定するロジックを国際化対応基盤として用意し、フロントエンドアプリケーションとバックエンドサーバーが通信を行うことで、ユーザーが求める言語体でコンテンツをブラウザに描画するよう設計しています。

  1. 国際化ライブラリの活用
    localeに応じた翻訳やフォーマットを効率的に行うには、ライブラリが便利です。以下に、i18nextを使った例を示します。
import i18next from 'i18next';

i18next.init({
  lng: navigator.language,
  resources: {
    'en-US': { translation: { welcome: 'Welcome!' } },
    'ja-JP': { translation: { welcome: 'ようこそ!' } }
  }
});

console.log(i18next.t('welcome')); // "ようこそ!"(日本語の場合)
  1. Intlを使ったフォーマットの実践
    Intlオブジェクトを活用して、localeに応じたフォーマットを実装できます。
const number = 123456.789;

// アメリカ英語 (ドル表記・小数点)
const usFormat = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
}).format(number);

// 日本語 (円表記・小数点なし)
const jpFormat = new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY",
  minimumFractionDigits: 0,
}).format(number);

// ドイツ語 (ユーロ表記・カンマとピリオドの位置が逆)
const deFormat = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR",
  minimumFractionDigits: 2,
}).format(number);

console.log(usFormat); // "$123,456.79"
console.log(jpFormat); // "¥123,457"
console.log(deFormat); // "123.456,79 €"

まとめ

この記事では、i18n対応におけるIntlとLocaleの重要性と活用方法について調査しました。

  • i18nの本質: 単なる翻訳を超え、日付・通貨・数値形式・ソート順・文化的配慮などを包括的に扱う設計プロセス
  • IntlとLocaleの関係: Intlが機能を提供する枠組みであるのに対し、Localeはlocale情報そのものを表現・操作するためのオブジェクト
  • 「region」採用の理由: 国(country)だけでなく、地域や広域(例:ラテンアメリカ全体の「419」)をカバーするための柔軟な設計思想
  • 標準規格の重要性: BCP 47、ISO 3166-1、UN M.49など、複数の国際標準が連携して多様な地域・文化への対応を可能に

localeの世界は単なる技術的な仕様を超えて、多様な言語や文化を尊重するWEBの設計思想そのものを反映しています。グローバルなユーザーに「自然と直感的に利用できる」体験を提供するためには、これらの仕様や標準への理解を深め、適切に実装することが不可欠だと改めて気づくきっかけになりました。

参考文献

GitHubで編集を提案
DRESS CODE TECH BLOG

Discussion