Java ローカライズとフォーマット

JavaGoldの資格取得のためにアウトプットかつ、備忘録のための記事です。
誤り等、わかりずらい等があればご指摘お願いいたします:bow_tone3:
# ローカライズとフォーマット
内容
・国際化対応のプログラムを作成するために必要となる**ロケール**について
・ロケールごと表示文字列などを切り替える際に使用する**リソースバンドル**について
・数値や日付のフォーマットについて
## ロケール
ロケールとは、国や言語で分けた「地域」を表す情報。
プログラムを実行する地域によって表示を変えたい場合などに使用する。
ロケールは、java.util.Localクラスのオブジェクトで表す。
オブジェクトの生成方法は、newによるインスタンス以外に、Localeクラスの定数や、getDefault()メソッドからも取得できる。
また、LocaleクラスのBuild()メソッドも使用できる。
コンストラクタを使用するLocaleクラスとは異なり、setterメソッドによってLocaleオブジェクトを構成する。
```java:sampleコード
// Java実行環境のロケールを返す
Locale japan = Locale.getDefault();
System.out.println(japan.getDisplayCountry() + " : " + japan.getDisplayLanguage());

// 引数で指定した言語コード・国コードからロケールを生成する
Locale english = new Locale("en","US");
System.out.println(english.getDisplayCountry() + " : " + english.getDisplayLanguage());

System.out.println(english.getDisplayCountry(english) + " : " + english.getDisplayLanguage(english));
		
System.out.println(english.getCountry() + " : " + english.getLanguage());

// build()メソッドでオブジェクト生成のやり方
Locale bulder = new Locale.Builder().setLanguage("ja").setScript("Jpan").setRegion("JP").build();
System.out.println(bulder.getCountry() + " : " + bulder.getLanguage());

// 定数でのオブジェクト生成
Locale japan2 = Locale.JAPAN;
System.out.println(japan2.getDisplayCountry() + " : " + japan2.getDisplayLanguage());
```
ロケールオブジェクトが持つ国名/言語名を返す
`final String getDisplayCountry()`
`final String getDisplayLanguage()`

引数で指定したロケールの表示で国名/言語名を返す
`String getDisplayCountry(Locale inLocale)`
`String getDisplayLanguage(Locale inLocale)`

ロケールオブジェクトがもつ国名/言語名を返す
`String getCountry()`
`String getLanguage()`

`setLanguage()`は言語、`setScript()`はISO15924で定義されている4文字の文字体系、`setRegion()`は地域を指定


## リソースバンドル
### リソースバンドルとは
アプリケーションのユーザーインターフェースにおいて、ロケールがUSの場合は英語で、JPの場合は日本語で表示する、と自動敵に表示を切り替えるような国際化を実現するために**リソースバンドル**を使用する
リソースバンドルは`java.util.ResourceBundle`のサブクラスである`ListResourceBundle`や`PropertyResourceBundle`を使用し扱う。

両クラスの概要↓
ListResourceBundle：リソースバンドルをリソースのリストとして管理するクラス
PropertyResourceBundle：リソースバンドルをプロパティファイルで管理するクラス

### ListResourceBundleクラスの利用
ListResourceBundleクラスは、リソースバンドルをクラスとして定義する。
以下がルール↓
・ListResourceBundleクラスを継承した、publicなクラスを作成する
・getContents()メソッドをオーバーライドし、配列でリソースのリストを作成する
・リソースは、キーと値を要素とする配列を作成する

getContents()メソッドの構文
`protected abstract Object[][] getContents()` : 各リソース（キーと値）をObject型の配列にして返す

#### リソースバンドルとその利用クラスの実装
```java:デフォルトロケール用のリソースバンドル
public class MyResource extends ListResourceBundle{

	@Override
	protected Object[][] getContents() {
		Object[][] contents = {
				{"send", "送信"},
				{"cancel","取消"}
		};
		return contents;
	}
	
}
```
```java:米国用のリソースバンドル
public class MyResource_en_US extends ListResourceBundle{
	
	@Override
	protected Object[][] getContents() {
		Object[][] contents = {
				{"send", "send"},
				{"cancel","cancel"}
		};
		return contents;
	}

}
```
ファイル名は任意だが、共通して「**MyResource**」という名前を使用している。
これを**基底名**と呼ぶ
基底名、言語コード、国コードを組み合わせてリソースバンドルのファイルを命名する
```text:命名の例
・MyResource
基底名のみの場合は、デフォルトロケールの場合に読み込まれる。

・MyResource_en
基底名＋言語コードの場合は、言語コードを指定したロケールの場合に読み込まれる。
_でつなぐ

・MyResource_en_US
基底名＋言語コード＋国コードの場合は、言語コード及び国コードを指定したロケールの場合に読み込まれる
_でつなぐ
```
作成したバンドルの読み込みには`getBundle()メソッド`を使用する

`static final ResourceBundle getBundle(String baseName)`
引数で指定された名前、デフォルトのロケール、および呼び出し側のクラスローダーを使用してリソースバンドルを取得する

`static final ResourceBundle getBundle(String baseName, Locale locale)`
引数で指定された名前、指定されたロケール、および呼び出し側のクラスローダーを使用してリソースバンドルを取得する

引数のbaseNameは、リソースバンドルの基底名を文字列で指定する。
リソースバンドルをパッケージ化している場合は、「パッケージ名.基底名」と指定する

リソースバンドル内の検索用メソッド
`boolean containsKey(String key)`：引数で指定されたキーがリソースバンドル内にあればtrue、なければfalse

`final Object getObject(String key)`：引数で指定されたキーに格納されたオブジェクトを返す
 
`final String getString(String key)`：引数で指定されたキーに格納された文字列を返す

`final String[] getStringArray(String key)`：引数で指定されたキーに格納された文字列の配列を返す

`Set<String> keySet()`：このリソースバンドルに含まれているすべてのキーをSet型で返す

### PropertyResourceBundleクラスの利用
ListResourceBundleクラスがリソースバンドルをクラスファイルから用意するのに対して、
PropertyResourceBundleはリソースバンドルをテキスト形式のプロパティファイルで用意する。
```text:プロパティファイル作成のルール
・プロパティファイル名は、ListResourceBundleと同じ、基底名、言語コード、国コードの組み合わせにする。
・拡張子は.propateisとする。
・リソースであるキーと値のペアは、プロパティファイル内に「キー＝値」の形式で記述する。
```
PropertyResourceBundleクラスによるリソースバンドルの使用ではサブクラス化をする必要はなく、プロパティファイルを作成するのみ。
```properties:デフォルトのプロパティファイル
send=P_\u9001\u4fe1
cancel=P_\u53d6\u6d88
```
PropertyResourceBundleではプロパティファイルをISO-8859-1エンコードで読み込むため、日本語等はUnicodeしておく必要がある。

```properties:米国用のプロパティファイル
send=P_send
cancel=P_cancel
```
```java:リソースバンドルを利用するクラス
Locale japan = Locale.getDefault();
Locale us = new Locale("en", "US");
Locale[] locArray = {japa, us};
for(Locale locale : locArray) {
    ResourceBundle obj1 = ResourceBundle.getBundle("MyResource", locale);
    System.out.println("send : " + obj1.getString("send"));
    System.out.println("cancel : " + obj1.getString("cancel"));
}
```

### リソースバンドルの検索
リソースバンドルは検索する順番が規定されているため、内容が重複していたとしても、先に見つかったリソースバンドルが使用される仕組みになっている。
```text:リソースバンドルの検索の優先順位
1 言語コード、国コードが一致するクラスファイル
2 言語コード、国コードが一致するプロパティファイル
3 言語コードが一致するクラスファイル
4 言語コードが一致するプロパティファイル
5 デフォルトロケール用のクラスファイル
6 デフォルトロケール用のプロパティファイル
```
ロケール対応した適切なリソースバンドルが読み込めない場合は、`MissingResourceException例外`が発生する。

## フォーマット
### フォーマットとは
扱うデータによっては、格納している形式と表示形式を変えたい場合がある。
例）格納：1000 / 表示：￥1,000
みたいな処理を**フォーマット(書式化)** と呼ぶ。

↓主なフォーマット処理用クラス↓
数値および通貨
・NumberFormat
・DecimalFormat
・DecimalFormatSysmbols

日付及び時刻
・DateFormat
・SimpleDateFormat
・DateFormatSysbol

テキストメッセージ
・MessageFormat
・ChoiceFormat

### 数値のフォーマット
数値および通貨をフォーマットするには、`java.text.NumberFormatクラス`を使用する
NumberFormatクラスは抽象クラスであるため、newによるインスタンス化はできない。

```java:NumberFormatを使用した例
		NumberFormat jpnNum = NumberFormat.getInstance();
		NumberFormat jpCur = NumberFormat.getCurrencyInstance();
		System.out.println("日本の数値 : " + jpnNum.format(50000));
		System.out.println("日本の通貨 : " + jpCur.format(50000));
		
		NumberFormat usNum = NumberFormat.getInstance(Locale.US);
		NumberFormat usCur = NumberFormat.getCurrencyInstance(Locale.US);
		System.out.println("米国の数値 : " + usNum.format(50000));
		System.out.println("米国の通貨 : " + usCur.format(50000));
        
        // 表示結果
        // 日本の数値 : 50,000
        // 日本の通貨 : ￥50,000
        // 米国の数値 : 50,000
        // 米国の通貨 : $50,000.00
```
また、NumberFormatクラスには文字列から読み込みを行うparse()メソッドも提供されている
```java:parse()メソッドの利用
		try {
			NumberFormat usNum = NumberFormat.getInstance(Locale.US);
			Number value1 = usNum.parse("500.12");
			System.out.println("value1 : " + value1);
			NumberFormat usCur = NumberFormat.getCurrencyInstance(Locale.US);
			double value2 = (double)usCur.parse("$20,456.99");
			System.out.println("value2 : " + value2);
		} catch (Exception e) {
			e.printStackTrace();
		}

        // 表示結果
        // value1 : 500.12
        // value2 : 20456.99

```

DecimalFormatクラスは指定したパターンで数値を自由にフォーマットすることができる。
パターンは基本的には0や#などのパターンで数値を自由に組み合わせて作成する。
```java:DecimalFormatの利用例
	public static void main(String[] args) {
		customFormat("###,###.###", 123456789);
		customFormat("###.###", 123456789);
		customFormat("000000.000", 123.78);
		customFormat("####,###.###", 12345.67);

	}
	
	static public void customFormat(String pattern, double value) {
		DecimalFormat myFormat = new DecimalFormat(pattern);
		String fData = myFormat.format(value);
		System.out.println(fData);
	}

    // 表示結果
    // 123,456,789
    // 123456789
    // 000123.780
    // 12,345.67
```
DecimalFormatクラスは、newによるインスタンス化を行う。
桁数が足りない場合は、丸めがされる

### 日付のフォーマット
```java:LocalDateクラスの利用
		LocalDate date = LocalDate.of(2016, Month.FEBRUARY, 1);
		System.out.println("getYear : " + date.getYear());
		System.out.println("getMonth : "+ date.getMonth());
		System.err.println("getMonthValue : " + date.getMonthValue());
		System.err.println("getDayOfMonth : " + date.getDayOfMonth());
		System.err.println("getDayOfYear : "+ date.getDayOfYear());
		System.err.println("getDayOfWeek : " + date.getDayOfWeek());
        // 結果
        // getYear : 2016
        // getMonth : FEBRUARY
        // getMonthValue : 2
        // getDayOfMonth : 1
        // getDayOfYear : 32
        // getDayOfWeek : MONDAY
```
`getYear`：年を取得
`getMonth`：月を取得(Month型)
`getMonthValue`：月を取得(int型)
`getDayOfMonth`月の日の間の値をint型で返す(1-31)
`getDayOfYear`：年の日の間の値をint型で返す（1-365）
例の場合1月の31 + 2月の1日 = 32日
`getDayOfWeek`：曜日を取得

### ロケール固有の日付フォーマット
#### ofLocalizedDate()とofLocalizedTime()
```java:FormatStyle列挙型の利用
		LocalDate date = LocalDate.of(2016, Month.FEBRUARY, 20);
		LocalTime time = LocalTime.of(10, 30, 45);
		
		LocalDateTime dateTime = LocalDateTime.of(date, time);
		System.out.println("date : " + date);
		System.out.println("time : " + time);
		System.out.println("dateTime : " + dateTime);
		
		DateTimeFormatter fm1 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
		DateTimeFormatter ft1 = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM);
		System.out.println("MEDIUM_DATE : " + fm1.format(date));
		System.out.println("MIDEUM_TIME : " + ft1.format(time));
		System.out.println("MIDEUM_DATETIME : " + fm1.format(dateTime));
```