通貨の数値表記はいろいろ 

ユーザーのロケール（地域と言語設定）に合わせて、DB で管理している数値データを現地通貨へローカライズ表示している Android アプリを公開しています。各国の通貨へ対応するのに時間がかかったので、実装方法を残しておきます。

日本語設定
![cropped_jp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/1e1a5f2f-7d48-ea75-e715-651ea182ad22.png)

米国英語設定
![cropped_en.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/5384f05d-0bea-5d4d-0ebe-4b52b5d8886e.png)

サウジアラビアのアラビア語設定！？（私は読めないです...）
![cropped_ar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853334/631d60d4-3993-379c-daef-2d9bfb6670ef.png)

投稿前に同じような記事がないか検索したら、すでにわかりやすい記事がありました。せっかく書いてしまったので、投稿はしておきます。

https://qiita.com/shinido/items/a5de5848dc1331295334

## 環境
Android 14 (API 34)

## 通貨の数値表記
国ごとに通貨表記が違うのは、想像がつくかと思います。例えば、日本円の場合だと少数点以下の数字はありませんが、米ドルには小数点第2までセントという補助貨幣があります。

また、ヨーロッパの国々に多いですが、桁の区切りはドットで少数点がカンマだったりします。中東だと、小数点以下3桁の国があったりします。つまり以下の種類があったりします。（もっとあると思います...）

- 1,000,000
- 1,000,000.00
- 1.000.000,00
- 1,000,000.000

各国の通貨形式を調べて、自力で実装するのは正気とは思えないので、普通は、ロケールを指定してフォーマットすると思います。

## ソース コード
```kotlin
// フォーマットする準備
val locale = Locale.getDefault()
val currency =
   try {
      Currency.getInstance(locale)
   } catch (e: IllegalArgumentException){
      Currency.getInstance(Locale.US)
   }
val digits = currency.defaultFractionDigits
val nf = NumberFormat.getInstance(locale).apply {
   maximumFractionDigits = digits
   minimumFractionDigits = digits
}
// 適当な数字を用意
val randomInt = Random.nextInt(1000000, 9999999)
// 数値フォーマット
val formattedNumber = randomInt.toDouble() / 10.0.pow(digits.toDouble())
val formattedString = nf.format(formattedNumber)
```
このコードは、ユーザーのロケールに基づいて、Int 型の数値をそのロケールで使用される通貨の小数点以下の桁数に合わせて Double 型に調整し、数値フォーマットをそのロケールに準じた形式で行ってます。

以下、各コードを説明していきます。

### 1. ロケールの取得
```kotlin
val locale = Locale.getDefault()
```
現在のデフォルトのロケール（ユーザーの地域と言語設定）を取得します。

ここでは、デフォルトのロケールを使用していますが、ロケールをユーザーが選択できるようにしておけば、任意の通貨形式を選ぶことができます。

### 2. 通貨の取得
```kotlin
val currency =
   try {
      Currency.getInstance(locale)
   } catch (e: IllegalArgumentException){
      Currency.getInstance(Locale.US)
   }
```
取得したロケールに基づいて通貨情報を取得します。もし、ロケールに対応する通貨が見つからない場合、例外が発生し、代わりにアメリカのドル（Locale.US）の通貨情報を取得します。

### 3. 通貨の小数点以下の桁数を取得
```kotlin
val digits = currency.defaultFractionDigits
```
通貨の小数点以下の標準的な桁数（例えば、円なら0桁、ドルなら2桁）を取得します。

### 4. 数値フォーマットの設定
```kotlin
val nf = NumberFormat.getInstance(locale).apply {
   maximumFractionDigits = digits
   minimumFractionDigits = digits
}
```
取得したロケールに基づいた数値フォーマットのインスタンスを取得し、フォーマットの際に使用する最小および最大の小数点以下の桁数を通貨に基づいた標準的な桁数へ設定します。

### 5. 通貨形式にフォーマット
```kotlin
val randomInt = Random.nextInt(1000000, 9999999)
val formattedNumber = randomInt.toDouble() / 10.0.pow(digits.toDouble())
val formattedString = nf.format(formattedNumber)
```

整数を小数点以下の桁数を持つ数値に変換します。これは、整数を10のべき乗で割れば実現できます。最後に、NumberFormat オブジェクト nf を使って、フォーマットされた文字列に変換します。

:::note info
説明のため、1000000 から 9999999 までのランダムな整数を生成していますが、実際には、DB に保存された数値を用いています。DB では、どの通貨も Int 型で管理し、表示の時にユーザーの通貨形式へ変換する方式をとっています。
:::

ちなみに、数値をフォーマットする方法に `String.format` を使用することもできますが、これは実行速度がかなり遅いのでダメです。
```kotlin:コード例
val formattedString = String.format(locale, "%,.${digits}f", formattedNumber)
```
String.format が遅いのは有名な話なのにこんなことするやついる？私です...

よくわかっていないときに開発した Android アプリでは常に String.format を使用して、動作をもっさりさせていました。単純に数字をフォーマットしたいだけなら、NuberFormat を使いましょうね。

https://developer.android.com/reference/android/icu/text/NumberFormat

## さいごに
この方法であれば、未知なる通貨でもいい感じに表示してくれると思います。たとえ、アラビア語圏で使われる数字であったとしても...

スクリーンショットのアプリは、これなので動作を見たい方は、使ってやってください。

https://play.google.com/store/apps/details?id=com.jyro.gehaltverwalter