オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

プログラミング

(プログラマのための)
いまさら聞けない標準規格の話

第4回 国際化編
オージス総研 技術部 アドバンストテクノロジセンター
伊藤 喜一
2022年3月17日

プログラマがシステム開発において共通で必要となる、技術と業務の狭間の共通知識を解説します。連載第4回は国際化編です。

0. 今回の概要

システム開発で必要となる標準規格の話、第4回は国際化編です。
グローバルな環境で利用される、あるいはグローバルな情報を扱うシステムを構築する際に参考になりそうな話題を取り上げます。

1. 国際化と地域化

ソフトウェアを特定の言語や地域に適合させる工程を「地域化 (localization, L10N)」と言います。しかし、多くの言語や地域に対して個別に地域化を行うと、開発や保守に多くの時間と費用がかかるため、ソフトウェア本体に変更を加えることなく多様な言語や地域に適合できるように予め設計しておく手法が生まれました。これを「国際化 (internatioalization, i18n)」と言います。
L10N、i18n は最初と最後の文字とその間の文字数を使った略語です。L10N が大文字、i18n が小文字なのは、小文字の l (エル)、大文字の I (アイ)、数字の 1 が紛らわしいためです。

国際化と地域化

国際化では一般に以下の項目が対象となります。

  • テキスト処理
    • 文字コード (文字集合、符号化方式)
    • テキスト出力 (方向性、文言、フォント)
    • テキスト入力
  • 日付・時刻
    • タイムゾーン
    • 日付・時刻の書式
  • 数値
    • 数値の書式
  • 通貨情報

今回は言語・地域の識別、テキスト出力を主に扱います。文字コードについては、連載第1回、第2回を参照してください。日付・時刻、数値については、長くなりそうなので次回へ回します。

2. ロケール

ソフトウェアの国際化・地域化にあたって、対象となる言語・地域の単位を定義する必要があります。これを「ロケール (ロカール, locale)」といいます。

2-1. IETF 言語タグ

ロケールの識別子として、インターネット関連や Java 7 以降では、IETF (Internet Engineering Task Force) の BCP 47 という仕様が使われます。現在、BCP 47 は RFC 5646「Tags for Identifying Languages」、RFC 4647「Matching of Language Tags」の2つの文書で構成されています。RFC は更新時に新たな番号が振られるため、番号が変わらない BCP 47 を一連の RFC を取りまとめる名前として使用します。BCP は RFC の分類の1つで、Best Current Practice (現時点における最良の方法) を意味します。Language Tag を「言語タグ」と訳します。

制定年 規格 概要
1995年 RFC 1766 ISO 639-1 とサブタグで構成。RFC 3066 により廃止。
2001年 RFC 3066 ISO 639-2 を追加、数字が利用可能に。RFC 4646 により廃止。
2006年 RFC 4646 全面改訂。サブタグレジストリを整備。RFC 5646 により廃止。
RFC 4647 マッチングの仕様を分離。
2009年 RFC 5646 拡張言語を定義。RFC 3066 以前の基準外のタグを列挙。

BCP 47

言語タグは当初 en-US のように language - subtag の最大2つの部分からなる構成でしたが、現在の RFC 5646 では language - script - region - variant - extension - privateuse の形式で書くことができます。このうち、language のみ必須です。language、script、region の各サブタグの値は、次章で説明する国際規格に基づいています。

サブタグ 説明 派生元規格
language 言語 ISO 639-1、ISO 639-2、ISO 639-3、ISO 639-5
script 文字体系 (用字系) ISO 15924
region 地域 ISO 3166-1 alpha-2、UN M.49
variant 異体 (変種)
extension 拡張
privateuse 私用

利用可能なサブタグは IANA (Internet Assigned Numbers Authority) の Language Subtag Registry で管理されています。

言語タグの例を以下に挙げます。

言語タグ 説明 形式
en 英語 language
en-US 英語 (米国) language-region
en-GB 英語 (英国) language-region
es-005 スペイン語 (南アメリカ) language-region
ja 日本語 language
ja-Latn 日本語 (ローマ字) language-script
ja-Latn-hepburn 日本語 (ローマ字、ヘボン式) language-script-variant
jp-JP 日本語 (日本) language-region
ja-JP-u-ca-japanese 日本語 (日本、和暦) language-region-extension
zh 中国語 language
zh-Hans 中国語 (簡体字) language-script
zh-Hans-CN 中国語 (簡体字、中国) language-script-region
zh-CN 中国語 (中国) language-region
zh-Hant 中国語 (繁体字) language-script
zh-Hant-TW 中国語 (繁体字、台湾) language-script-region
zh-TW 中国語 (台湾) language-region

大文字・小文字は区別されませんが、2番目以降の2文字のサブタグ (region など) はすべて大文字、2番目以降の4文字のサブタグ (script など) は先頭だけ大文字、それ以外のサブタグはすべて小文字が推奨されています。

言語タグの使用例としては、HTML 文書の属性で lang="ja" のように使われたりします。

extension の例として、ja-JP-u-ca-japanese (和暦) を挙げていますが、詳細は「4-1. Unicode ロケール識別子」で説明します。

2-2. POSIX ロケール

Linux などの OS で指定する識別子です。language _ territory . codeset @ modifier の形式で表すことができます。

サブコード 説明 派生元規格
language 言語 ISO 639-1、ISO 639-2
territory 地域 ISO 3166-1 alpha-2
codeset 文字コード
modifier 修飾子

POSIX ロケールの例を以下に挙げます。

ロケール 説明
C デフォルト、英語 (米国、ASCII)
POSIX C と同じ
en_US 英語 (米国、ISO-8859-1)
en_US.UTF-8 英語 (米国、UTF-8)
fr_FR@euro フランス語 (フランス、ユーロ、ISO-8859-15)
ja_JP.EUC-JP 日本語 (日本、EUC-JP)
ja_JP.UTF-8 日本語 (日本、UTF-8)

POSIX ロケールは、以下の環境変数に設定してシステムの動作を変更します。

環境変数 説明
LANG 一般的な言語設定
LANGUAGE 翻訳の優先順位 (複数指定可)
LC_CTYPE 文字の種類、比較・分類
LC_COLLATE 文字の照合や整列
LC_MESSAGES メッセージの表示
LC_MONETARY 通貨、金額の表示
LC_NUMERIC 数値の表示
LC_TIME 日付・時刻の表示
LC_ALL すべての設定を上書き

LANG より LC_*、LC_* より LC_ALL の設定が優先されます。

3. 国際化に関する国際規格

本章では、国際化に関する国際規格を紹介します。
ロケールの定義以外にも、グローバルな事象を扱うシステムで、データの保存・交換を行う際のキーの候補として検討するとよいと思います。

3-1. 言語

言語は ISO-639 シリーズで規定されており、日本では JIS X 0412 が対応します。
最初に2文字コードが規定されましたが、より多くの言語を表現するため3文字コードが追加されました。

制定年 規格 説明
2002年 ISO 639-1 (JIS X 0412-1) 2文字コード、ISO 639:1988 を改訂
1998年 ISO 639-2 (JIS X 0412-2) 3文字コード (T: 用語学用、B: 書誌用)
2007年 ISO 639-3 3文字コードの拡張 (国際SIL)
2008年 ISO 639-5 3文字コード (言語グループ)
2009年 ISO 639-6 4文字コード (言語変種)、2014年廃止

規定されているコードの例を以下に挙げます。
ISO 639-2 は用途によって一部異なったコードが振られています (太字)。また、mis (その他) や und (不明) のように特殊なコードが定義されています。日本関連では、ISO 639-2 でアイヌ語が、ISO 639-3 で沖縄語などの琉球諸語が追加されています。

(表は横にスクロールしてご覧ください)

言語 639-1 639-2/T 639-2/B 639-3 639-5 639-6 Scope Type
英語 en eng eng eng engs Individual Living
フランス語 fr fra fre fra fras Individual Living
ドイツ語 de deu ger deu deus Individual Living
イタリア語 it ita ita ita itas Individual Living
スペイン語 es spa spa spa Individual Living
ポルトガル語 pt por por por Individual Living
ギリシア語 el ell gre ell ells Individual Living
ロシア語 ru rus rus rus Individual Living
インド・ヨーロッパ語族 ine ine ine Collective
アラビア語 ar ara ara ara Macrolanguage Living
アフロ・アジア語族 afa afa afa Collective
アイヌ語 ain ain ain Individual Living
沖縄語 ryu Individual Living
日本語 ja jpn jpn jpn Individual Living
日本手話 jsl Individual Living
中古日本語 ojp Individual Historical
日琉語族 jpx Collective
韓国語 ko kor kor kor Individual Living
中国語 zh zho chi zho Macrolanguage Living
シナ・チベット語族 sit sit sit Collective
その他の言語 mis mis mis Special
複数言語 mul mul mul Special
不明言語 und und und Special
言語ではない zxx zxx zxx Special

3-2. 文字体系

文字体系は ISO 15924 で規定されています。
4文字のアルファベットと3桁の数字が定義されています。

規定されているコードの例を以下に挙げます。

文字 alpha-4 numeric Property Value Alias
ラテン文字 Latn 215 Latin
ギリシア文字 Grek 200 Greek
キリル文字 Cyrl 220 Cyrillic
アラビア文字 Arab 160 Arabic
平仮名 Hira 410 Hiragana
片仮名 Kana 411 Katakana
仮名 (平仮名+片仮名) Hrkt 412 Katakana_Or_Hiragana
日本語 (漢字+平仮名+片仮名) Jpan 413
ハングル Hang 286 Hangul
韓国語 (ハングル+漢字) Kore 287
漢字 Hani 500 Han
漢字 (簡体字) Hans 501
漢字 (繁体字) Hant 502
絵文字 Zsye 993
数学記号 Zmth 995
シンボル Zsym 996

もし、通常の日本語の文書のほかにローマ字で記述した文書を作るケースがあれば、lang="ja-Latn" のように指定することになるかと思います。

3-3. 国・地域

国・地域は ISO 3166-1 で規定されています。日本では JIS X 0304 が対応します。
2文字のアルファベット、3文字のアルファベット、3桁の数字の3種類のコードが定義されています。3桁の数字コードは国際連合統計部が定義する UN M.49 と同じコードです。

規定されているコードの例を以下に挙げます。

国・地域 alpha-2 alpha-3 numeric
(UN M.49)
アメリカ合衆国 (米国) US USA 840
グアム GU GUM 316
カナダ CA CAN 124
イギリス (英国) GB GBR 826
フランス FR FRA 250
ドイツ DE DEU 276
イタリア IT ITA 380
スペイン ES ESP 724
ポルトガル PT PRT 620
ギリシャ GR GRC 300
ロシア RU RUS 643
エジプト EG EGY 818
日本 JP JPN 392
大韓民国 (韓国) KR KOR 410
中華人民共和国 (中国) CN CHN 156
香港 HK HKG 344
台湾 TW TWN 158
南極 AQ ATA 010

ISO 3166-2 は各国の行政区画名を規定するコードです。
グアム、香港、台湾などは ISO 3166-1、ISO 3166-2 両方にコードが定義されています。

国・地域 行政区画 ISO 3166-2
アメリカ合衆国 ニューヨーク州 US-NY
カリフォルニア州 US-CA
ワシントン D.C. US-DC
グアム US-GU
日本 北海道 JP-01
東京都 JP-13
沖縄県 JP-47
中華人民共和国 北京市 CN-BJ
四川省 CN-SC
新疆ウイグル自治区 CN-XJ
香港特別行政区 CN-HK
(台湾省) CN-TW

国際連合統計部の UN M.49 では、国より広い地域のコードも定義されています。その一部を抜粋します。

地域 UN M.49
世界 001
アフリカ 002
アメリカ 019
北アメリカ 003
南アメリカ 005
アジア 142
東アジア 030
南アジア 034
東南アジア 035
中央アジア 143
西アジア 145
ヨーロッパ 150
南ヨーロッパ 039
東ヨーロッパ 151
北ヨーロッパ 154
西ヨーロッパ 155
オセアニア 009
南極 010

3-4. 通貨

通貨は ISO 4217 で規定されています。
3文字のアルファベット、3桁の数字が定義されています。原則として、3文字のアルファベットは、最初の2文字が国を表す ISO 3166-1 のコード、残りの1文字が通貨名の頭文字です。3桁の数字は ISO 3166-1 すなわち UN M.49 と同じになっています。
通貨以外に、貴金属や国際金融で使用される特定の金融商品にも X で始まる通貨コードが割り当てられています。

規定されているコードの例を以下に挙げます。

通貨 alpha numeric 小数桁 (通貨記号)
アメリカ合衆国ドル USD 840 2 $
カナダ・ドル CAD 124 2 $ / C$
スターリング・ポンド GBP 826 2 £
ユーロ EUR 978 2
ロシア・ルーブル RUB 643 2
エジプト・ポンド EGP 818 2
日本円 JPY 392 0 ¥
韓国ウォン KRW 410 0
人民元 CNY 156 2 ¥
新台湾ドル TWD 901 2 $ / NT$
金 (1トロイオンス) XAU 959
銀 (1トロイオンス) XAG 961

4. Common Locale Data Repository

Unicode コンソーシアムの Common Locale Data Repository (CLDR) では、国際化に関わる以下のデータを提供しています。

  • 言語タグの拡張属性
  • 言語名、文字体系、国名・地域名、通貨名等の訳
  • 日付・時刻、数値の書式

4-1. Unicode ロケール識別子

Unicode ロケール識別子は、CLDR の記述言語 Unicode Locale Data Markup Language (LDML) で使用される、IETF 言語タグの拡張で、u- に続けて、以下のような属性を付加することができます。

key type 説明
ca カレンダー iso8601 ISO 8601
gregory グレゴリオ暦
japanese 和暦
fw 週の開始 sun 日曜始まり
mon 月曜始まり
hc 時間周期 h11 12時制 (0-11)
h12 12時制 (1-12)
h23 24時制 (0-23)
h24 24時制 (1-24)
tz タイムゾーン utc Etc/UTC、協定世界時
usnyc America/New_York、ニューヨーク (米国)
gblon Europe/London、ロンドン (英国)
jptyo Asia/Tokyo、日本標準時
nu 数値書式 latn 算用数字
roman ローマ数字 (大文字)
jpan 漢数字 (日本)
cu 通貨 usd 米ドル
eur ユーロ
jpy 日本円
ms 単位系 metric メートル法
uksystem ヤード・ポンド法 (英国)
ussystem ヤード・ポンド法 (米国)
rg 地域 (言語以外の地域依存ロケール)
sd 行政区画 usny ニューヨーク州
jp13 東京都
va バリアント posix POSIX ロケール

利用可能な属性は、Unicode コンソーシアムの GitHub で見ることができます。

ロケール識別子の例を以下に挙げます。

ロケール識別子 説明
ja-JP-u-ca-japanese 和暦
ja-JP-u-ca-iso8601-tz-jptyo ISO 8601 形式の日付・時刻、日本標準時
en-GB-u-rg-uszzzz イギリス英語だが、カレンダー、通貨など他の属性は米国式

Java 9 から ca と nu、Java 10 から cu、fw、rg、tz のサポートが追加されています。

4-2. CLDR data

CLDR が提供する訳語や書式などのデータは、Unicode コンソーシアムの GitHub で見ることができます。

どんなデータが定義されているか、参考までに見ておくとよいかと思います。一部を抜粋します。

field type en (en-US) en-GB ja (ja-JP)
language en English English 英語
en_GB British English British English イギリス英語
en_US American English American English アメリカ英語
ja Japanese Japanese 日本語
und Unknown language ↑↑↑ 言語不明
script Latn Latin ↑↑↑ ラテン文字
Jpan Japanese ↑↑↑ 日本語の文字
Zzzz Unknown Script ↑↑↑ 不明な文字
territory 019 Americas ↑↑↑ アメリカ大陸
150 Europe ↑↑↑ ヨーロッパ
142 Asia ↑↑↑ アジア
US United States United States アメリカ合衆国
GB United Kingdom United Kingdom イギリス
JP Japan Japan 日本
EU European Union ↑↑↑ 欧州連合
UN United Nations ↑↑↑ 国際連合
ZZ Unknown Region ↑↑↑ 不明な地域
type (calendar) iso8601 ISO-8601 Calendar ↑↑↑ ISO-8601
gregory Gregorian Calendar ↑↑↑ 西暦(グレゴリオ暦)
japanese Japanese Calendar ↑↑↑ 和暦
type (number) latn Western Digits ↑↑↑ 算用数字
roman Roman Numerals ↑↑↑ ローマ数字
jpan Japanese Numerals ↑↑↑ 漢数字
dateFormat full EEEE, MMMM d, y EEEE, d MMMM y y年M月d日EEEE
short M/d/yy dd/MM/y y/MM/dd
timeFormat full h:mm:ss a zzzz HH:mm:ss zzzz H時mm分ss秒 zzzz
short h:mm a HH:mm H:mm

↑↑↑ は上位のロケール (en-GB であれば en) の値を継承することを意味します。

Java 9 からこれらのデータを参照するようになりました。

5. プログラミング言語での扱い (Java)

本章では、Java を例に、国際化に関する処理の例を紹介します。
コード例は Java 11 を基準にしています。

5-1. Locale

Java ではロケールの情報を java.util.Locale クラスで扱います。

英語、日本語、アメリカ合衆国、イギリス、日本など、いくつかの言語、国・地域に対しては定数が定義されているので、それを使うとよいでしょう。それ以外のロケールについては、言語タグを引数にしたファクトリメソッドで取得するのが簡単です。

class LocaleTest {

    @Test
    void testJapaneseCalendar() {
        // ファクトリによる生成。
        Locale locale = Locale.forLanguageTag("ja-JP-u-ca-japanese");

        // 各フィールドの取得。
        assertThat(locale.getLanguage()).isEqualTo("ja");
        assertThat(locale.getCountry()).isEqualTo("JP");
        assertThat(locale.hasExtensions()).isTrue();
        assertThat(locale.getExtension('u')).isEqualTo("ca-japanese");
        assertThat(locale.getUnicodeLocaleType("ca")).isEqualTo("japanese");

        // 言語タグへの変換。
        assertThat(locale.toLanguageTag()).isEqualTo("ja-JP-u-ca-japanese");
    }

}

フィルタリング

Locale クラスでは、RFC 4647 のフィルタリングの機能を提供しています。資料検索などで、利用者が読むことができるものだけにフィルタリングして返すなどのユースケースで使えると思います。

    @Test
    void testFilterTags() {

        List<String> tags = List.of(
                "en", "en-US", "en-GB",
                "ja", "ja-JP",
                "zh-CN", "zh-TW", "zh-Hans-CN", "zh-Hant-TW");

        // 言語・地域を指定。
        assertThat(Locale.filterTags(LanguageRange.parse("zh-TW, en-US"), tags))
            .isEqualTo(List.of("zh-TW", "en-US"));

        // 拡張言語範囲、言語のみを指定。
        assertThat(Locale.filterTags(LanguageRange.parse("zh-*-TW, en"), tags))
            .isEqualTo(List.of("zh-TW", "zh-Hant-TW", "en", "en-US", "en-GB"));
    }

ルックアップ

Locale クラスでは、RFC 4647 のルックアップの機能も提供しています。利用者にとって最適な言語のデータを1つ返すなどのユースケースで利用できます。

    @Test
    void testLookupTag() {

        List<String> tags = List.of(
                "en", "en-US", "en-GB",
                "ja", "ja-JP",
                "zh-CN", "zh-TW", "zh-Hans-CN", "zh-Hant-TW");

        // 言語・地域を指定。
        assertThat(Locale.lookupTag(LanguageRange.parse("zh-TW, en-US"), tags))
            .isEqualTo("zh-TW");

        // 拡張言語範囲、言語のみを指定。
        assertThat(Locale.lookupTag(LanguageRange.parse("zh-*-TW, en"), tags))
            .isEqualTo("zh-Hant-TW");
    }

5-2. Currency

Java では通貨の情報を java.util.Currency クラスで扱います。
通貨コードを引数にして Currency オブジェクトを取得することができます。数値コードでは取得できないようです。また、ロケールを引数にして現在の通貨を取得することができます。日時は指定できないようです。

class CurrencyTest {

    @Test
    void testUsd() {
        // 通貨コードで取得。
        Currency currency = Currency.getInstance("USD");

        // 各フィールドの取得。
        assertThat(currency.getCurrencyCode()).isEqualTo("USD");
        assertThat(currency.getNumericCode()).isEqualTo(840);
        assertThat(currency.getNumericCodeAsString()).isEqualTo("840");
        assertThat(currency.getDefaultFractionDigits()).isEqualTo(2);
        assertThat(currency.getSymbol(Locale.US)).isEqualTo("$");
        assertThat(currency.getSymbol(Locale.JAPAN)).isEqualTo("$");

        // ロケールで取得。
        assertThat(Currency.getInstance(Locale.US)).isEqualTo(currency);
    }

}

5-3. ResourceBundle

ロケールごとに異なる情報を取得する場合は、java.util.ResourceBundle クラスを使用します。

例えば、以下のような3つの設定ファイル (プロパティファイル) を用意します。ファイル名は、ja-JP ではなく ja_JP なので注意してください。
なお、プロパティファイルが ASCII 以外の文字を含む場合、Java 8 以前は native2ascii コマンドでファイルを変換する必要がありましたが、Java 9 から PropertyResourceBundle のデフォルトエンコーディングが UTF-8 になったため、ファイルを UTF-8 で作成すれば変換の必要はありません。

resources.properties

locale.id =
locale.name = (Unknown)
locale.language = und
locale.script =
locale.region =

resources_ja.properties

locale.id = ja
locale.name = 日本語
locale.language = ja
locale.script = Jpan

resources_ja_JP.properties

locale.id = ja-JP
locale.name = 日本語 (日本)
locale.region = JP

ロケール ja-JP を引数に ResourceBundle を取得し、各リソース (ここでは文字列) を取得すると、ja_JP → ja → (root) の順に検索して、最初に見つかったものを返してくれます。

class ResourceBundleTest {

    @Test
    void testJapan() {
        Locale locale = Locale.JAPAN;
        assertThat(locale.toLanguageTag()).isEqualTo("ja-JP");

        ResourceBundle resources = ResourceBundle.getBundle("resources", locale);
        assertThat(resources.getString("locale.id")).isEqualTo("ja-JP");
        assertThat(resources.getString("locale.name")).isEqualTo("日本語 (日本)");
        assertThat(resources.getString("locale.language")).isEqualTo("ja");
        assertThat(resources.getString("locale.script")).isEqualTo("Jpan");
        assertThat(resources.getString("locale.region")).isEqualTo("JP");
    }

}

中国語の場合は、少し検索順が異なっており、以下のように検索するようです。
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/ResourceBundle.Control.html#getCandidateLocales(java.lang.String,java.util.Locale)

  • ロケール zh-Hans-CN の場合: zh_Hans_CN → zh_Hans → zh_CN → zh → (root)
  • ロケール zh-CN の場合: zh_Hans_CN → zh_Hans → zh_CN → zh → (root)
  • ロケール zh-Hant-TW の場合: zh_Hant_TW → zh_Hant → zh_TW → zh → (root)
  • ロケール zh-TW の場合: zh_Hant_TW → zh_Hant → zh_TW → zh → (root)

ドキュメントに記載がありませんが、zh-Hans、zh-Hant を指定した場合は、以下のようになるようです。

  • ロケール zh-Hans の場合: zh_Hans → zh_CN → zh → (root)
  • ロケール zh-Hant の場合: zh_Hant → zh_TW → zh → (root)

zh-CN、zh-TW を簡体字、繁体字の代替として使っていた名残りでしょうか。

また、ロケール en-US を指定し、かつ resources_en_US.properties も resources_en.properties も存在しない場合、resources.properties を参照しそうに思いますが、システムのデフォルトロケールが検索されてしまい、日本語環境であれば ja_JP → ja → (root) の順に探してしまいます。
当該のリソースが存在しない場合、英語のリソースを返したい場合は、システムのデフォルトロケールを en-US に設定する必要がありそうです。

5-4. MessageFormat

ロケールによって出力するメッセージを変えたい場合は、ResourceBundle に加えて java.text.MessageFormat クラスを使用します。

先ほどと同じように、ロケールごとに設定ファイル (プロパティファイル) を作成し、メッセージのひな型を記述します。

messages_en.properties

hello = Hello, {0}!

messages_ja.properties

hello = {0}さん、こんにちは!

ResourceBundle から取得した文字列を引数に MessageFormat を生成し、置換するデータを引数に format メソッドを呼び出すと、メッセージの該当部分が引数に置換されます。

class MessageFormatTest {

    @Test
    void testUS() {
        Locale locale = Locale.US;
        assertThat(getMessage(locale, "hello", "Yamada"))
            .isEqualTo("Hello, Yamada!");
    }

    @Test
    void testJapan() {
        Locale locale = Locale.JAPAN;
        assertThat(getMessage(locale, "hello", "山田"))
            .isEqualTo("山田さん、こんにちは!");
    }

    static String getMessage(Locale locale, String code, Object... args) {
        ResourceBundle messages = ResourceBundle.getBundle("messages", locale);
        return new MessageFormat(messages.getString(code), locale).format(args);
    }

}

とは言え、実際にシステムを開発する場合、Spring Framework のように何かしらのフレームワークを使うことが多いと思います。フレームワークがメッセージ変換の機能を持ってることも多いので、その場合は、フレームワークの機能を確認してください。

6. まとめと次回の予告

システム開発で必要となる標準規格の話、連載第4回は国際化編と題して、ロケールと関連する国際規格、を紹介しました。今回取り上げた標準規格や仕組みを使える場合は、独自のコードや仕組みを再発明せずに、あるものを使った方が相互運用性や保守性の観点で好ましいと思います。

次回は、日付・時刻、数値に関する話題を取り上げようと思います。


A1. 参考文献・URL