Java 8での国際化と地域化

1.概要

  • 国際化とは、言語、地域、文化、政治に固有のさまざまなデータをサポートするためのアプリケーションを作成するプロセスです。

  • ** をさらに読むために、国際化のための非常にポピュラーな略語(おそらく実際の名前よりもポピュラー)があることを知っておくべきです - i18n は 'i’と 'n’の間の18文字のためです。

今日の企業向けプログラムでは、世界のさまざまな場所または複数の文化的地域の人々にサービスを提供することが重要です。異なる文化や言語の地域では、言語固有の説明だけでなく、通貨、数字の表現、さらには異なる日付と時刻の構成も決定されません。

たとえば、国固有の数字に注目しましょう。彼らは様々な小数点と千の区切り記号があります。

  • 102,300.45(アメリカ合衆国)

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

  • 102.300、45(ドイツ)

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

  • 2018年1月1日、月曜日、3:20:34 PM CET(アメリカ合衆国)

  • 1月1日2018 15時間20 CET(フランス)。

  • 2018年1月1日星期1時03时20分34秒CET(中国)

さらに、国によって通貨記号が異なります。

  • £1,200.60(イギリス)

  • €1.200,60(イタリア)

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

  • 1,200.60米ドル(アメリカ合衆国)

知っておくべき重要な事実は、たとえ国が同じ通貨と通貨記号を持っていたとしても - フランスとイタリアのように - 彼らの通貨記号の位置は異なるかもしれないということです。

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

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

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

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

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

  • de(ドイツ語)

  • it__CH(イタリア語、スイス)

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

2.1. フィールド

  • Locale は言語コード、国別コード、および派生形で構成されていることをすでに学びました。設定可能なフィールドがさらに2つあります。

スクリプトと拡張機能** 。

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

  • 言語 ISO 639 alpha-2またはalpha-3 コード、あるいは登録済みです。

言語サブタグ。

  • 地域 (Country)は ISO 3166 alpha-2 country codeまたは__UNです。

数値3__市外局番。

  • Variant は、大文字と小文字を区別する値、または

Locale のバリエーション。

  • スクリプト は有効な ISO 15924 alpha-4 コードでなければなりません。

  • Extensions は単一の文字キーと

String

IANA Language Subtag Registry には、 language region variant 、および script に指定できる値が含まれています。

可能な extension 値のリストはありませんが、値は整形式のhttps://docs.oracle.com/javase/tutorial/i18n/locale/extensions.html[ 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();

上記の Locale String 表現は fr CA POSIX #Latn__です。

setterメソッドでは BCP-47 に準拠している必要がありますが、** 「variant」の設定はバリアント値に対する公式の制限がないため、少しトリッキーになるかもしれません。

そうでない場合は、 IllformedLocaleException がスローされます。

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

2.3. コンストラクタ

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

  • 新しいロケール(String language)

  • 新しいロケール(String language、String country)

  • 新しいロケール(String language、String country、String variant)

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

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

有効な variant は、5から8文字の英数字または単一の数値の後に3文字の英数字の String でなければなりません。これらの要件を満たしていないため、コンストラクタを介してのみ variant フィールドに「UNIX」を適用できます。

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

2.4. 定数

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

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

2.5. 言語タグ

Locale を作成するもう1つの方法は、静的ファクトリメソッド forLanguageTag(String languageTag) を呼び出すことです。このメソッドは IETF BCP 47 規格を満たす String を必要とします。

これが私達がどのように英国 Locale を作成できるかです:

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

2.6. 利用可能なロケール

Locale オブジェクトを複数組み合わせて作成することはできますが、使用できない可能性があります。

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

フォーマットに Locales を使用しているので、さまざまなフォーマッタがランタイムにインストールされている利用可能な Locales のさらに小さいセットを持つことができます。

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

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

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

利用可能なロケールのセットは、Javaプラットフォームのさまざまな実装と機能のさまざまな分野で異なることを覚えておいてください。

サポートされているロケールの完全なリストはhttp://www.oracle.com/technetwork/java/javase/documentation/java9locales-3559485.html[OracleのJava SE Development Kit Webページ]にあります。]

2.7. デフォルトロケール

ローカライズ作業中は、 JVM インスタンスのデフォルトの Locale が何であるかを知る必要があるかもしれません。幸いなことに、それを行う簡単な方法があります。

Locale defaultLocale = Locale.getDefault();

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

Locale.setDefault(Locale.CANADA__FRENCH);

JVM インスタンスに依存しない JUnit テストを作成したい場合は特に重要です。

3.数字と通貨

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

プリミティブな数値型( int double )と同等のオブジェクト( Integer Double )をフォーマットするには、 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進数と千の区切り記号が含まれています。

これは別の例です。

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

それでは、数字のフォーマットよりも複雑な日付と時刻のフォーマットについて学びます。

まず第一に、我々はそれが完全に新しい Date/Time APIを含むので、日付と時刻のフォーマットがJava 8で著しく変化したことを知るべきです。

そのため、さまざまなフォーマッタクラスを見ていきます。

4.1. DateTimeFormatter

  • Java 8が導入されて以来、日付と時刻をローカライズするためのメインクラスは DateTimeFormatter クラスです** 。これは TemporalAccessor インターフェースを実装するクラス(例えば、 LocalDateTime LocalDate、LocalTime 、または __ZonedDateTime)で動作します。 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[ DateTimeFormatter ]のJavaドキュメントにあります。最終的な値がシンボルの数に依存することを知るために** 。この例では、月の名前を完全に表示する「MMMM」がありますが、「M」の文字を1つ付けると、先頭に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の詳細については、既存の記事リンク:/java-8-date-time-intro[here]を参照してください。

** 4.2. DateFormat SimpleDateFormatter

**

Dates Calendars を利用するプロジェクトで作業することは依然として一般的なので、 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));

DateFormat Dates と連携して機能し、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」が先行または後続のゼロを決定し、「、」が千単位の区切り文字で、「。」が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 クラスを使用すると、想像できる数値形式を指定できます。

  • 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 における国際化の重要な部分は Resource Bundle メカニズムです。

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

7.まとめ

__Localと それらを利用するフォーマッターは国際化されたアプリケーションを作成するのを助けるツールです。これらのツールを使用すると、複数のビルドやJavaが Locale__をサポートしているかどうかを気にせずに、ユーザーの言語や文化の設定に動的に適応できるアプリケーションを作成できます。

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

Spring Bootアプリケーションを扱うときには、 Spring Boot Internationalization という便利な記事もあります。

このチュートリアルのソースコードは、完全な例とともに、https://github.com/eugenp/tutorials/tree/master/core-java-8/src/test/java/com/baeldung/internationalization[over onにあります。 GitHub]。