[Swift Tips] 複数のLocalizable.stringsにアクセスする

## 環境

- Xcode 10.1
- Swift 4.2.1

## 目的

分割されたEmbedded Framework毎にLocalizable.stringsを持ち、横断的に文字列を取得する。

## 課題

- どうやってEmbedded Framework毎にLocalizable.stringsを持つのか？
- Embedded Framework間で連動するNSLocalizedStringの使い方とは？

## どうやってEmbedded Framework毎にLocalizable.stringsを持つのか?

単純に「File」メミューから「File...」を選んで、さらに「Strings File」を選んでプロジェクト（ターゲット）に追加してください。名前はスタンダードな「Localizable.strings」で構いません。
これはEmbedded Frameworkでは名前空間が異なるためです。

![スクリーンショット 2019-02-07 9.39.07.png](https://qiita-image-store.s3.amazonaws.com/0/37634/33373b0c-a213-ca3d-3f17-01626e251add.png)

![スクリーンショット 2019-02-07 9.36.18.png](https://qiita-image-store.s3.amazonaws.com/0/37634/e1fcfcb8-68fc-e4ee-bc0a-6ab21b6278d2.png)

問題は、この新たな「Localizable.strings」をどうやって呼び出すかです。

下記の例ではCommonUtilクラス（クラス名は何でも良い）を、先ほどLocalizable.stringsを作成したEmbedded Framework内に作成します。

```swift:Swift

public class CommonUtil
{
    public static let shared = CommonUtil()
    private init(){
        bundle = Bundle(for: type(of: self))
    }
    public let bundle: Bundle
}

```

上記のコードで、Embedded FrameworkのBundleを取得できるようになります。

そして、Bundleがとファイル名（Localizable.strings）が分かればNSLocalizedString()を使うことができます。

```swift:Swift
extension String
{
    public var localized: String
    {
        let bundle = CommonUtil.shared.bundle
        let result = NSLocalizedString(self, tableName: "Localizable", bundle: bundle, value: "", comment: self)
        return result
    }
}
```

上記のコードは、Stringクラスのextension内でlocalizedプロパティを定義しています。
このlocalizedプロパティは先ほどの `CommonUtil.shared.bundle` を使ってEmbedded FrameworkのBundleを取得し、Localizable.stringsを指定しています。

## Embedded Framework間で連動するNSLocalizedStringの使い方とは？

ところで、NSLocalizedString()は非常に使いにくい関数です。それは、Localizable.stringsにキーがない場合は、キーを返り値として戻してくることです。
例えば、

```
/* 
  Localizable.strings
*/

"確定" = "確定";
"キャンセル" = "キャンセル";

```
というようなLocalizable.stringsがあったとして、

```swift:Swift
let val = NSLocalizedString("もどる")
```
とすると、"もどる"が返ってきてしまいます。
つまり、Localizable.stringsにそのキーが無いことが判定できません。

この場合、複数のLocalizable.stringsから単語を検索できなくなり、大変不便です。

そこで、Localizable.stringsにあるルールを付加することでこれを解決します。

```
/* 
  Localizable.strings
*/

"_確定" = "確定";
"_キャンセル" = "キャンセル";

```

上記のアンスコは例ですが、取得する単語とキーは一致させない、というルールです。
これによって、

```swift:Swift
extension String
{
    public var localized: String
    {
        let result = NSLocalizedString(self, value: "", comment: "")
        if result == self
        {
            // 無かった
        }
        return result
    }
}
```

というように書くことができます。

## 結論

まとめると、下記のように書くことで、Mainバンドルに無かったキーを、Embedded FrameworkのLocalizable.stringsから検索して取得することができるようになります。

```swift:Swift

import Foundation

extension String
{
    public var localized: String
    {
        let result = NSLocalizedString(self, value: "", comment: "")
        if result == self
        {
            return embedded_localized
        }
        return result
    }
    
    public var embedded_localized: String
    {
        let bundle = CommonUtil.shared.bundle
        let result = NSLocalizedString(self, tableName: "Localizable", bundle: bundle, value: "", comment: self)
        return result
    }
}
```

基本的な単語で、どのプロジェクトでも変わらないような物は、こちらに集めることで、少し便利かもしれません。また、Embedded Framework内でLocalizable.stringsを使ってUIViewを表示したりする場合も有効かもしれません。