Java 8での国際化と地域化

Java 8の国際化とローカリゼーション

1. 概要

Internationalization is a process of preparing an application to support various linguistic, regional, cultural or political-specific data.これは、最新の多言語アプリケーションの重要な側面です。

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

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

たとえば、国固有の数値に焦点を当てましょう。 さまざまな小数および千の区切り記号があります。

  • 102,300.45(米国)

  • 102 300,45(ポーランド)

  • 102.300,45 (Germany)

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

  • 2018年1月1日月曜日午後3時20分34秒CET(米国)

  • lundi 1 janvier 2018 15 h 20 CET(フランス)。

  • 2018年1月1日星期1下午後3時20分34秒CET(中国)

さらに、国によって固有の通貨記号があります。

  • £1,200.60(イギリス)

  • €1.200,60(イタリア)

  • 1 200,60€(フランス)

  • $1,200.60 (United States)

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

2. ローカライゼーション

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

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

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

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

  • de(ドイツ語)

  • it_CH(イタリア、スイス)

  • en_US_UNIX(米国、UNIXプラットフォーム)

2.1. フィールド

Locale consists of language code, country code, and variant. There are two more possible fields to set: script and extensionsについてはすでに学習しました。

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

  • Languageは、ISO 639 alpha-2 or alpha-3コードまたは登録済み言語のサブタグにすることができます。

  • Region(国)はISO 3166 alpha-2国コードまたはUN numeric-3市外局番です。

  • Variantは、大文字と小文字を区別する値またはLocaleのバリエーションを指定する値のセットです。

  • Scriptは有効なISO 15924 alpha-4コードである必要があります。

  • Extensionsは、1文字のキーとStringの値で構成されるマップです。

IANA Language Subtag Registryには、languageregionvariant、およびscriptの可能な値が含まれています。

可能な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_です。

setting ‘variant' may be a little bit tricky as there’s no official restriction on variant values, although the setter method requires it to be BCP-47 compliantであることを知っておくとよいでしょう。

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

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

2.3. コンストラクタ

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

  • 新しいロケール(文字列言語)

  • 新しいロケール(文字列言語、文字列の国)

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

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

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

有効なvariantは、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 BCP 47標準を満たすStringが必要です。

これが、英国のLocaleを作成する方法です。

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

2.6. 利用可能なロケール

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

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

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

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

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

その後、Localeが使用可能なLocales.の中にあるかどうかを確認できます

the set of available locales is different for various implementations of the Java Platformand various areas of functionalityであることを覚えておく必要があります。

サポートされているロケールの完全なリストは、the Oracle’s Java SE Development Kit webpage.で入手できます。

2.7. デフォルトのLocale

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

Locale defaultLocale = Locale.getDefault();

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

Locale.setDefault(Locale.CANADA_FRENCH);

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

3. 数字と通貨

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

プリミティブ数型(intdouble)とそれに相当するオブジェクト(IntegerDouble)をフォーマットするには、NumberFormatクラスとその静的クラスを使用する必要がありますファクトリメソッド。

私たちにとって興味深い方法は2つあります。

  • NumberFormat.getInstance(Locale locale)

  • NumberFormat.getCurrencyInstance(Locale locale)

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

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

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

ご覧のとおり、Localeを作成し、それを使用してNumberFormatインスタンスを取得し、サンプル番号をフォーマットするのと同じくらい簡単です。 the output includes locale-specific decimal and thousand separatorsであることがわかります。

別の例を次に示します。

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. 日時

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

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

4.1. DateTimeFormatter

Since Java 8 was introduced, the main class for localizing of dates and times is the DateTimeFormatter classTemporalAccessorインターフェースを実装するクラス(LocalDateTimeLocalDate, LocalTimeZonedDateTime. など)で動作します。DateTimeFormatterを作成するには、少なくともパターンを指定する必要があります。 Locale. サンプルコードを見てみましょう:

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

説明付きの可能なすべてのパターン文字は、https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html.It’s worth to know that final value depends on the number of symbolsにあります。 完全な月名を出力する例には「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 8Date/Time APIの詳細については、既存の記事hereがあります。

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. カスタマイズ

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

数値の書式をカスタマイズするには、DecimalFormatDecimalFormatSymbolsを使用できます。

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

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");

The DecimalFormat documentationは、可能なすべてのパターン文字を表示します。 ここで知っておく必要があるのは、「000000000.000」が先頭または末尾のゼロを決定し、「、」が千の区切り文字であり、「。」であることです。 10進数の1です。

通貨記号を追加することもできます。 以下に、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^[email protected]");

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

To customize SimpleDataFormat, we can use 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の国際化の重要な部分は、Resource Bundleメカニズムです。

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

7. 結論

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

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

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

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