🗒️

[SwiftUI]LocalizedStringを便利に扱う

2022/05/09に公開

LocalizedStringの定義

  • 基本的な使い方はこちら
  • しかし、この方法ではキーをリテラルで記述する必要があるため、キーをenumに定義して呼び出せるようにする
  • そもそもSwiftGenを利用する選択肢があると思うが、ライブラリを入れるまでも無い規模であったり、サードパーティ製ライブラリの依存を増やしたく無い場合もあると思うので、そのようなケースの対応としてメモ

enum化

  • Localizable.strings に下記のような設定があったとして、対応するenumを定義する
// Localizable.strings
"SAMPLE_TEXT"="サンプル";
// Localization.swift
enum L10n: String {
  case sampleText
}
  • このenumの中にNSLocalizedStringを利用してStringを返す変数を定義しておく
  • また、このサンプルではLocalizedStringは大文字のスネークケース、enumはキャメルケースで記述しているため、スネークケースに変換して呼び出せるように、Stringextentionも作っておく
    reference: https://gist.github.com/dmsl1805/ad9a14b127d0409cf9621dc13d237457
// Localization.swift
enum L10n: String {
  case sampleText
  
  var localized: String {
    NSLocalizedString(rawValue.snakeCased(), comment: "")
  }
}

extension String {
  func snakeCased() -> String {
    let pattern = "([a-z0-9])([A-Z])"
    let regex = try? NSRegularExpression(pattern: pattern, options: [])
    let range = NSRange(location: 0, length: count)
    return regex?.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2").uppercased() ?? ""
  }
}
  • これでL10n.sample.localizedのように呼び出せるようになる

パラメータを渡したい場合

  • "SAMPLE_PARAMETERIZED %@"="%@のサンプル";など、パラメータを渡したいケースのために、引数を受け取るメソッドを追加する
  • 下記の例では、sampleParameterizedSAMPLE_PARAMETERIZEDと変換し、渡されたパラメータ数に応じて%@を付加しているが、ここはもっと良い方法があるかもしれない
    reference: https://qiita.com/hogehoge_samba/items/124840f1407b49efd73d
// Localization.swift
enum L10n: String {
  case sampleText
  case sampleParameterized
  
  var localized: String {
    NSLocalizedString(rawValue.snakeCased(), comment: "")
  }
  
  func localized(_ args: CVarArg...) -> String {
    var value = rawValue.snakeCased()
    args.forEach { _ in
      value = "\(value) %@"
    }
    
    return NSString(
      format: NSLocalizedString(value, comment: ""),
      arguments: getVaList(args)
    ) as String
  }
}

extension String {
  func snakeCased() -> String {
    let pattern = "([a-z0-9])([A-Z])"
    let regex = try? NSRegularExpression(pattern: pattern, options: [])
    let range = NSRange(location: 0, length: count)
    return regex?.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2").uppercased() ?? ""
  }
}
  • 一応これでText(L10n.sampleParameterized.localized("SwiftUI"))と呼び出し、SwiftUIのサンプルと出力されるようになる

Discussion