1. 概要

国際化は、さまざまな言語、地域、文化、または政治固有のデータをサポートするアプリケーションを準備するプロセスです。これは、現代の多言語アプリケーションの重要な側面です。

をさらに読むには、国際化のための非常に人気のある略語(おそらく実際の名前よりも人気があります)があることを知っておく必要があります– i18n は、「i」と「n」の間に18文字あるためです’。

現在のエンタープライズプログラムでは、世界のさまざまな地域や複数の文化圏の人々にサービスを提供することが重要です。 異なる文化的または言語的地域は、言語固有の説明だけでなく、通貨、数字の表現、さらには異なる日付と時刻の構成も決定します。

たとえば、国固有の番号に焦点を当てましょう。 それらには、さまざまな10進数と1000の区切り文字があります。

  • 102,300.45(米国)
  • 102 300,45(ポーランド)
  • 102.300,45(ドイツ)

さまざまな日付形式もあります。

  • 2018年1月1日月曜日15:20:34CET(米国)
  • lundi 1 janvier 2018 15 h 20 CET(フランス)。
  • 2018年1月1日星期一下午03時20分34秒CET(中国)

さらに、国ごとに固有の通貨記号があります。

  • £1,200.60(イギリス)
  • €1.200,60(イタリア)
  • 1 200,60€(フランス)
  • $ 1,200.60(米国)

知っておくべき重要な事実は、フランスとイタリアのように、国が同じ通貨と通貨記号を持っている場合でも、通貨記号の位置が異なる可能性があるということです。

2. ローカリゼーション

Java内には、Localeクラスと呼ばれる素晴らしい機能があります。

これにより、文化的なロケールをすばやく区別し、コンテンツを適切にフォーマットできます。 これは、国際化プロセスの中で不可欠です。 i18nと同じように、ローカリゼーションにも略語–l10n。があります。

Locale を使用する主な理由は、必要なすべてのロケール固有のフォーマットに再コンパイルせずにアクセスできるためです。 アプリケーションは同時に複数のロケールを処理できるため、新しい言語のサポートは簡単です。

ロケールは通常、アンダースコアで区切られた言語、国、およびバリアントの略語で表されます。

  • de(ドイツ語)
  • it_CH(イタリア語、スイス)
  • en_US_UNIX(米国、UNIXプラットフォーム)

2.1. 田畑

私たちはすでにそれを学びましたロケールは、言語コード、国コード、およびバリアントで構成されています。 設定できるフィールドはさらに2つあります。スクリプトと拡張機能です。

フィールドのリストを見て、ルールが何であるかを見てみましょう。

  • Language は、 ISO639alpha-2またはalpha-3コードまたは登録済み言語サブタグにすることができます。
  • 地域(国)は、 ISO 3166alpha-2国コードまたはUN数値-3市外局番です。
  • Variant は大文字と小文字を区別する値、またはLocaleのバリエーションを指定する値のセットです。
  • Script は、有効な ISO 15924alpha-4コードである必要があります。
  • Extensions は、単一文字キーとString値で構成されるマップです。

IANA言語サブタグレジストリには、言語領域バリアント、およびスクリプトの可能な値が含まれています。

可能なextension値のリストはありませんが、値は整形式のBCP-47サブタグである必要があります。 キーと値は常に小文字に変換されます。

2.2. Locale.Builder

Localeオブジェクトを作成する方法はいくつかあります。 考えられる1つの方法は、Locale.Builderを利用することです。 Locale.Builder には、オブジェクトを構築し、同時にこれらの値を検証するために使用できる5つのセッターメソッドがあります。

Locale locale = new Locale.Builder()
  .setLanguage("fr")
  .setRegion("CA")
  .setVariant("POSIX")
  .setScript("Latn")
  .build();

上記のLocaleString表現は、 fr_CA_POSIX_#Latnです。

‘variant’の設定は、バリアント値に公式の制限がないため、少し注意が必要な場合がありますが、setterメソッドではBCP-47に準拠している必要があります

それ以外の場合は、IllformedLocaleExceptionをスローします。

検証に合格しない値を使用する必要がある場合は、値を検証しないため、Localeコンストラクターを使用できます。

2.3. コンストラクター

Locale には、次の3つのコンストラクターがあります。

  • 新しいロケール(文字列言語)
  • 新しいロケール(文字列言語、文字列の国)
  • 新しいロケール(文字列言語、文字列の国、文字列バリアント)

3パラメーターコンストラクター:

Locale locale = new Locale("pl", "PL", "UNIX");

有効なバリアントは、5〜8文字の英数字または1文字の数字の後に3文字の英数字のStringである必要があります。 「UNIX」はコンストラクターを介してのみvariantフィールドに適用できます。これは、これらの要件を満たしていないためです。

ただし、コンストラクターを使用して Locale オブジェクトを作成することには、1つの欠点があります。それは、拡張機能とスクリプトフィールドを設定できないことです。

2.4. 定数

これはおそらく、Localesを取得するための最も簡単で最も制限された方法です。 Locale クラスには、最も人気のある国または言語を表すいくつかの静的定数があります。

Locale japan = Locale.JAPAN;
Locale japanese = Locale.JAPANESE;

2.5. 言語タグ

Locale を作成する別の方法は、静的ファクトリメソッド forLanguageTag(String languageTag)を呼び出すことです。 このメソッドには、 IETF BCP47標準を満たすStringが必要です。

UK Localeを作成する方法は次のとおりです。

Locale uk = Locale.forLanguageTag("en-UK");

2.6. 利用可能なロケール

Locale オブジェクトの複数の組み合わせを作成することはできますが、それらを使用できない場合があります。

注意すべき重要な注意点は、プラットフォーム上のロケールは、Javaランタイム内にインストールされているロケールに依存しているということです。

フォーマットにLocalesを使用しているため、異なるフォーマッターでは、ランタイムにインストールされているLocalesのさらに小さなセットを使用できる場合があります。

利用可能なロケールの配列を取得する方法を確認しましょう。

Locale[] numberFormatLocales = NumberFormat.getAvailableLocales();
Locale[] dateFormatLocales = DateFormat.getAvailableLocales();
Locale[] locales = Locale.getAvailableLocales();

その後、ロケールが使用可能なロケールの中にあるかどうかを確認できます。

使用可能なロケールのセットは、Javaプラットフォームおよび機能のさまざまな領域のさまざまな実装によって異なることを覚えておく必要があります。

サポートされているロケールの完全なリストは、OracleのJavaSE開発キットのWebページで入手できます。

2.7. デフォルトのロケール

ローカリゼーションを操作しているときに、JVMインスタンスのデフォルトのLocaleが何であるかを知る必要がある場合があります。 幸い、それを行う簡単な方法があります。

Locale defaultLocale = Locale.getDefault();

また、同様のセッターメソッドを呼び出すことにより、デフォルトのLocaleを指定できます。

Locale.setDefault(Locale.CANADA_FRENCH);

これは、JVMインスタンスに依存しないJUnitテストを作成する場合に特に関係があります。

3. 数と通貨

このセクションでは、さまざまなロケール固有の規則に準拠する必要がある数値と通貨のフォーマッターについて説明します。

プリミティブ数値タイプ( int double )とそれに相当するオブジェクト( Integer Double )をフォーマットするには、[ X152X]NumberFormatクラスとその静的ファクトリメソッド。

2つの方法が私たちにとって興味深いものです。

  • NumberFormat.getInstance(ロケールロケール)
  • NumberFormat.getCurrencyInstance(ロケールロケール)

サンプルコードを調べてみましょう。

Locale usLocale = Locale.US;
double number = 102300.456d;
NumberFormat usNumberFormat = NumberFormat.getInstance(usLocale);

assertEquals(usNumberFormat.format(number), "102,300.456");

ご覧のとおり、 Locale を作成し、それを使用して NumberFormat インスタンスを取得し、サンプル番号をフォーマットするのと同じくらい簡単です。 出力にロケール固有の10進数と1000の区切り文字が含まれていることがわかります。

別の例を次に示します。

Locale usLocale = Locale.US;
BigDecimal number = new BigDecimal(102_300.456d);

NumberFormat usNumberFormat = NumberFormat.getCurrencyInstance(usLocale); 
assertEquals(usNumberFormat.format(number), "$102,300.46");

通貨のフォーマットには、数値のフォーマットと同じ手順が含まれます。 唯一の違いは、フォーマッタが通貨記号と小数部を2桁に丸めることです。

4. 日時

次に、数値の書式設定よりもおそらく複雑な日付と時刻の書式設定について学習します。

まず、Java8には完全に新しいDate / Time APIが含まれているため、日付と時刻のフォーマットが大幅に変更されたことを知っておく必要があります。 したがって、さまざまなフォーマッタクラスを調べます。

4.1. DateTimeFormatter

Java 8が導入されて以来、日付と時刻をローカライズするためのメインクラスはDateTimeFormatterクラスです。 を実装するクラスで動作します TemporalAccessor たとえば、インターフェース LocalDateTime LocalDate、LocalTime また ZonedDateTime。 を作成するには DateTimeFormatter 少なくともパターンを提供する必要があり、それからロケール。 サンプルコードを見てみましょう:

Locale.setDefault(Locale.US);
LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 1, 10, 15, 50, 500);
String pattern = "dd-MMMM-yyyy HH:mm:ss.SSS";

DateTimeFormatter defaultTimeFormatter = DateTimeFormatter.ofPattern(pattern);
DateTimeFormatter deTimeFormatter = DateTimeFormatter.ofPattern(pattern, Locale.GERMANY);

assertEquals(
  "01-January-2018 10:15:50.000", 
  defaultTimeFormatter.format(localDateTime));
assertEquals(
  "01-Januar-2018 10:15:50.000", 
  deTimeFormatter.format(localDateTime));

DateTimeFormatter を取得した後、 format()メソッドを呼び出すだけでよいことがわかります。

理解を深めるために、考えられるパターン文字をよく理解しておく必要があります。

たとえば、文字を見てみましょう。

Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   y       year-of-era                 year              2004; 04
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978

説明付きのすべての可能なパターン文字は、 DateTimeFormatterのJavaドキュメントにあります。最終的な値はシンボルの数に依存することを知っておく価値があります。 完全な月の名前を出力する例には「MMMM」がありますが、単一の「M」文字は先行0なしで月番号を示します。

DateTimeFormatter を終了するために、LocalizedDateTimeをフォーマットする方法を見てみましょう。

LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 1, 10, 15, 50, 500);
ZoneId losAngelesTimeZone = TimeZone.getTimeZone("America/Los_Angeles").toZoneId();

DateTimeFormatter localizedTimeFormatter = DateTimeFormatter
  .ofLocalizedDateTime(FormatStyle.FULL);
String formattedLocalizedTime = localizedTimeFormatter.format(
  ZonedDateTime.of(localDateTime, losAngelesTimeZone));

assertEquals("Monday, January 1, 2018 10:15:50 AM PST", formattedLocalizedTime);

LocalizedDateTime をフォーマットするために、 ofLocalizedDateTime(FormatStyle dateTimeStyle)メソッドを使用して、事前定義されたFormatStyle。を提供できます。

Java 8 Date / Time APIの詳細については、既存の記事こちらをご覧ください。

4.2. DateFormatおよびSimpleDateFormatter

DatesCalendarsを使用するプロジェクトで作業することは今でも一般的であるため、DateFormatを使用して日付と時刻をフォーマットする機能を簡単に紹介します。 ]SimpleDateFormatクラス。

最初のものの能力を分析しましょう:

GregorianCalendar gregorianCalendar = new GregorianCalendar(2018, 1, 1, 10, 15, 20);
Date date = gregorianCalendar.getTime();

DateFormat ffInstance = DateFormat.getDateTimeInstance(
  DateFormat.FULL, DateFormat.FULL, Locale.ITALY);
DateFormat smInstance = DateFormat.getDateTimeInstance(
  DateFormat.SHORT, DateFormat.MEDIUM, Locale.ITALY);

assertEquals("giovedì 1 febbraio 2018 10.15.20 CET", ffInstance.format(date));
assertEquals("01/02/18 10.15.20", smInstance.format(date));

DateFormatDatesと連携し、次の3つの便利なメソッドがあります。

  • getDateTimeInstance
  • getDateInstance
  • getTimeInstance

それらはすべて、DateFormatの事前定義された値をパラメーターとして受け取ります。 各メソッドはオーバーロードされているため、Localeを渡すこともできます。 DateTimeFormatter で行われているように、カスタムパターンを使用する場合は、SimpleDateFormatを使用できます。 短いコードスニペットを見てみましょう。

GregorianCalendar gregorianCalendar = new GregorianCalendar(
  2018, 1, 1, 10, 15, 20);
Date date = gregorianCalendar.getTime();
Locale.setDefault(new Locale("pl", "PL"));

SimpleDateFormat fullMonthDateFormat = new SimpleDateFormat(
  "dd-MMMM-yyyy HH:mm:ss:SSS");
SimpleDateFormat shortMonthsimpleDateFormat = new SimpleDateFormat(
  "dd-MM-yyyy HH:mm:ss:SSS");

assertEquals(
  "01-lutego-2018 10:15:20:000", fullMonthDateFormat.format(date));
assertEquals(
  "01-02-2018 10:15:20:000" , shortMonthsimpleDateFormat.format(date));

5. カスタマイズ

いくつかの優れた設計上の決定により、ロケール固有のフォーマットパターンに縛られることはなく、出力に完全に満足するようにほぼすべての詳細を構成できます。

数値の書式をカスタマイズするには、DecimalFormatおよびDecimalFormatSymbolsを使用できます。

簡単な例を考えてみましょう。

Locale.setDefault(Locale.FRANCE);
BigDecimal number = new BigDecimal(102_300.456d);

DecimalFormat zeroDecimalFormat = new DecimalFormat("000000000.0000");
DecimalFormat dollarDecimalFormat = new DecimalFormat("$###,###.##");

assertEquals(zeroDecimalFormat.format(number), "000102300,4560");
assertEquals(dollarDecimalFormat.format(number), "$102 300,46");

DecimalFormatのドキュメントには、考えられるすべてのパターン文字が示されています。 ここで知っておく必要があるのは、「000000000.000」が先頭または末尾のゼロを決定し、「、」が1000の区切り文字であり、「。」であるということだけです。 10進数です。

通貨記号を追加することも可能です。 DateFormatSymbol クラスを使用しても、同じ結果が得られることが以下でわかります。

Locale.setDefault(Locale.FRANCE);
BigDecimal number = new BigDecimal(102_300.456d);

DecimalFormatSymbols decimalFormatSymbols = DecimalFormatSymbols.getInstance();
decimalFormatSymbols.setGroupingSeparator('^');
decimalFormatSymbols.setDecimalSeparator('@');
DecimalFormat separatorsDecimalFormat = new DecimalFormat("$###,###.##");
separatorsDecimalFormat.setGroupingSize(4);
separatorsDecimalFormat.setCurrency(Currency.getInstance(Locale.JAPAN));
separatorsDecimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);

assertEquals(separatorsDecimalFormat.format(number), "$10^2300@46");

ご覧のとおり、 DecimalFormatSymbols クラスを使用すると、想像できる任意の数値フォーマットを指定できます。

SimpleDataFormatをカスタマイズするには、DateFormatSymbolsを使用できます。

曜日名の変更がいかに簡単か見てみましょう。

Date date = new GregorianCalendar(2018, 1, 1, 10, 15, 20).getTime();
Locale.setDefault(new Locale("pl", "PL"));

DateFormatSymbols dateFormatSymbols = new DateFormatSymbols();
dateFormatSymbols.setWeekdays(new String[]{"A", "B", "C", "D", "E", "F", "G", "H"});
SimpleDateFormat newDaysDateFormat = new SimpleDateFormat(
  "EEEE-MMMM-yyyy HH:mm:ss:SSS", dateFormatSymbols);

assertEquals("F-lutego-2018 10:15:20:000", newDaysDateFormat.format(date));

6. リソースバンドル

最後に、 JVM の国際化の重要な部分は、リソースバンドルメカニズムです。

ResourceBundle の目的は、個別のファイルに外部化できるローカライズされたメッセージ/説明をアプリケーションに提供することです。 以前の記事の1つであるリソースバンドルのガイドで、リソースバンドルの使用法と構成について説明します。

7. 結論

ロケールとそれらを利用するフォーマッターは、国際化されたアプリケーションの作成に役立つツールです。 これらのツールを使用すると、複数のビルドを行わずに、またはJavaが Locale をサポートするかどうかを心配することなく、ユーザーの言語または文化の設定に動的に適応できるアプリケーションを作成できます。

ユーザーがどこにいても、どの言語でも話せる世界では、これらの変更を適用できるということは、世界中のより多くのユーザーがアプリケーションをより直感的で理解しやすいものにすることができることを意味します。

Spring Bootアプリケーションを使用する場合、 Spring BootInternationalizationに関する便利な記事もあります。

このチュートリアルのソースコードは、完全な例とともに、GitHubにあります。