iOSの言語設定を取得するベストプラクティス(Swift)

## はじめに

iOSの言語設定を取得するベストプラクティスを紹介します。

## 環境

### PC

- OS：macOS Big Sur 11.0.1
- Xcode：12.2 (12B45b)
- Swift：5.3.1

### シミュレータ

- 機種：iPhone SE (2nd generation)
- OS：iOS 14.2

## 結論

- アプリがローカライズされていない
  - `Locale.current` ではうまく取得できないので、 `Locale.preferredLanguages` から `Locale` を生成し直す
- アプリがローカライズされている
  - アプリ単位で優先する言語を指定できるので、「おまけ」に書いたようにローカライズの設定を使うのがベター
  - OS単位の言語設定を取得したい場合は、ローカライズされていないときと同様の方法でいい

## 言語設定の取得を試す

結論に至るまでにいろいろ試したので、過程を紹介します。

__PREFERRED LANGUAGE ORDER（使用する言語の優先順序）: English > 日本語 (アメリカ)
地域: United States（アメリカ合衆国）__

まずはシミュレータのデフォルト設定で試してみます。

|Language & Region（言語と地域）|
|:--|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/ed15a990-9c99-390d-7490-19847509ce65.png" width="375">|

|プロパティ|戻り値|
|:--|:--|
|`Locale.preferredLanguages`|`["en", "ja-US"]`|
|`Locale.current.description`|`en (current)`|
|`Locale.current.languageCode`|`en`|
|`Locale.current.regionCode`|`nil`|

ほぼ予想通りの結果ですが、 `ja-US` という表記を初めて見たのと、 `regionCode` が `nil` なのは意外でした。

__使用する言語の優先順序: 日本語 > English
地域: アメリカ合衆国__

次に、使用する言語の優先順序を入れ替えます。
地域は英語→日本語表記になったので変わったように見えますが、変えていません。

|Language & Region（言語と地域）|
|:--|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/dd8fc170-853d-3d38-628b-2415af31e2a7.png" width="375">|

|プロパティ|戻り値|
|:--|:--|
|`Locale.preferredLanguages`|`["ja-US", "en"]`|
|`Locale.current.description`|`en_US (current)`|
|`Locale.current.languageCode`|`en`|
|`Locale.current.regionCode`|`US`|

`Locale.preferredLanguages` が入れ替わったのは予想通りでしたが、他は私の中では予想外でした。
優先順序を入れ替えたことによって「iPhoneの使用言語」が `日本語` に変わったので、 `Locale.current.○○` のプロパティも日本になると思いました。

__使用する言語の優先順序: 日本語 > English
地域: 日本__

使用する言語の優先順序をそのままに、地域を日本に変えます。

|Language & Region（言語と地域）|
|:--|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/5b8a63e8-9170-dc1a-3845-0e64b131a9a8.png" width="375">|

|プロパティ|戻り値|
|:--|:--|
|`Locale.preferredLanguages`|`["ja-JP", "en-JP"]`|
|`Locale.current.description`|`en_JP (current)`|
|`Locale.current.languageCode`|`en`|
|`Locale.current.regionCode`|`JP`|

なんかいろいろおかしい気がしますｗ
シミュレータだからなのか、言語や地域の変更後に再起動していないからなのか、原因はわかりません。
正しい挙動という可能性ももちろんあります。

## おわりに

いろいろ試してもよくわかりませんでした。
何となくわかったのが、 `preferredLanguages` の要素は `{languageCode}-{regionCode}` で構成されているということです。

iOSの言語と地域について詳しい方がいたら、コメントなどで教えていただけると嬉しいです :relaxed: 

追記：
[Twitter](https://twitter.com/mishimaybe/status/1329048360381136899?s=20) でAppleの公式ドキュメントを教えていただきました。
言語IDやロケールIDについて詳しく書いてあるので、こちらを読めば深く理解できそうです。
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html#//apple_ref/doc/uid/10000171i-CH15

## おまけ：言語設定の取得をローカライズ文字列に任せる

iOSの言語設定がうまく取得できなかったので、代替案を考える必要があります。

`Localizable.string` に `"languageCode" = "en";` のようなキーと値を持たせ、言語設定の取得をローカライズ文字列に任せる方法を思いつきましたｗ
他にいい方法があれば教えてください :sob:

みなさんはアプリ内でなくサーバーから言語設定に応じた文字列を取得する場合、どのように言語設定をサーバーに渡しているのだろう… :thinking:

追記：
[@k_katsumi](https://twitter.com/k_katsumi) さんにTwitterでいろいろ教えていただきました :pray:

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">当てになるのはpreferredLanguagesで、そこから言語コードだけ、とか分解して取りたいならその値からLocaleを作り直すといいっすよ。<br><br>let locale = Locale(identifier:Locale.preferredLanguages[0])<br>print(locale.languageCode)<br>print(locale.regionCode)<br>print(locale.scriptCode)</p>&mdash; kishikawa katsumi (@k_katsumi) <a href="https://twitter.com/k_katsumi/status/1329054779138396162?ref_src=twsrc%5Etfw">November 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

実際に `preferredLanguages` から `Locale` を作り直し、各プロパティを確認しました。

```swift
print(Locale.preferredLanguages) // ["ja-JP", "en-JP"]

let language = Locale.preferredLanguages.first!
print(language) // ja-JP

let locale = Locale(identifier: language)
print(locale.description) // ja-JP (fixed)
print(locale.languageCode ?? "nil") // ja
print(locale.regionCode ?? "nil") // JP
print(locale.scriptCode ?? "nil") // nil
```

適切に `languageCode` や `regionCode` を取得できました。
自分で生成した `Locale` だと `description` の括弧内が `(current)` でなく `(fixed)` になるようです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">その上で、ケースバイケースで難しいんですけどたいていの場合はユーザーが指定した（望んでいる）ローカライズの設定に従うようにLocalizable.stringを当てにするっていうのはかなりベターな方法だと思います。</p>&mdash; kishikawa katsumi (@k_katsumi) <a href="https://twitter.com/k_katsumi/status/1329055247180779521?ref_src=twsrc%5Etfw">November 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ローカライズされているアプリは、OS単位でなくアプリ単位でも優先する言語を指定できるので、ローカライズの設定に従うのはベターな方法でした。
おまけとして書きましたが、こちらの方法を採用するのがよさそうです。

[@treastrain](https://twitter.com/treastrain) さんも採用しているとのことです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">完全に出遅れてしまったけど、私も各 Localizable.strings に<br><br>&quot;key&quot; = &quot;ja&quot;;<br>&quot;key&quot; = &quot;en&quot;;<br>&quot;key&quot; = &quot;zh-HK&quot;;<br><br>を書いて、それを拾って判定してる。 <a href="https://t.co/2B0BiEZllv">https://t.co/2B0BiEZllv</a></p>&mdash; treastrain / Tanaka.R (@treastrain) <a href="https://twitter.com/treastrain/status/1329075198469935104?ref_src=twsrc%5Etfw">November 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 参考リンク

- [Locale | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/locale)
- [NSLocale | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/nslocale)
- [current | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/nslocale/1409990-current)
- [preferredLanguages | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/nslocale/1415614-preferredlanguages)
- [description | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/locale/2292995-description)
- [languageCode | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode)
- [regionCode | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/locale/2293371-regioncode)
- [[iOS]中国語対応のために端末の言語環境の値を取得したときにハマったこと - Qiita](https://qiita.com/BlackCat/items/ea3e48a4b43b38c4da24)
- [言語設定を取得したい場合はLocale.prefferedLanguagesを使う - Qiita](https://qiita.com/hsylife/items/58193b3b237198fc495e)