LoginSignup
5

posted at

updated at

Organization

iOS 15.0+ではNSLocalizedStringよりString(localized:)を使おう(Swift)

はじめに

本記事は Swift Advent Calendar 2022 の1日目の記事です。
遅くなりましたが、誰も埋めていなかったので埋めます。

ローカライズの文字列を取得する方法を紹介します。

環境

  • OS:macOS Ventura 13.0.1
  • Xcode:14.1 (14B47b)
  • Swift:5.7.1

今まで: NSLocalizedStringを使う

ローカライズの文字列を取得するとき、今までは NSLocalizedString(_:tableName:bundle:value:comment:) を使っていました。

func NSLocalizedString(
    _ key: String,
    tableName: String? = nil,
    bundle: Bundle = Bundle.main,
    value: String = "",
    comment: String
) -> String

引用: https://developer.apple.com/documentation/foundation/1418095-nslocalizedstring

Swift Packages上にあるSwiftUIのViewから呼び出す場合、以下のように使うことが多いと思います。

FooView.swift
import SwiftUI

struct FooView: View {
  var body: some View {
    Text("Foo")
      .navigationTitle(NSLocalizedString("Foo", bundle: .module, comment: ""))
  }
}

これから: String(localized:)を使う

たまたま apple/sample-food-truckのソースコード を見ていて、 String(localized:table:bundle:locale:comment:) なる String のイニシャライザがあることに気づきました。

init(
    localized keyAndValue: String.LocalizationValue,
    table: String? = nil,
    bundle: Bundle? = nil,
    locale: Locale = .current,
    comment: StaticString? = nil
)

引用: https://developer.apple.com/documentation/swift/string/init(localized:table:bundle:locale:comment:)

iOS 15.0+で使えること以外、公式ドキュメントには説明がありません。「No overview available.」すら書いてありません。

先ほどの NSLocalizedString を使った例を String(localized:) で置き換えてみます。

FooView.swift
import SwiftUI

struct FooView: View {
  var body: some View {
    Text("Foo")
-     .navigationTitle(NSLocalizedString("Foo", bundle: .module, comment: ""))
+     .navigationTitle(String(localized: "Foo", bundle: .module)
  }
}

いろいろ試したところ、iOS 15.0+からはこちらのAPIを使ったほうがいいと判断したので、この記事で紹介することにしました。

String(localized:)のメリット

String(localized:table:bundle:locale:comment:) のメリットを紹介します。

Stringを返すことがわかりやすい

定義を見るとわかりますが、 NSLocalizedString() の実態は String を返す関数です。
String のイニシャライザを使うことで、 String であることがわかりやすくなります。

commentを省略できる

NSLocalizedString では commentString 型なのに対し、 String(localized:) では StaticString? 型と、オプショナル型になっています。
しかもデフォルトで nil が指定されているので、呼び出しを省略できます。

今まで comment: "" と書くことが多かったので、省略できるとスッキリして嬉しいです。

String Interpolationに対応している

「String Interpolation(文字列補間)」とは、文字列リテラル内に埋め込まれたプレースホルダーを、実行時に実際の値へ置き換える処理のことです。

iOSのローカライズでは、以下のようにフォーマット指定子を使って実現できます。

Localizable.strings
"Set %lld" = "%lldセット目";

NSLocalizedString はString Interpolationに対応していないのに対し、 String(localized:) は対応しています。

FooView.swift
import SwiftUI

struct FooView: View {
  var body: some View {
    VStack {
      Text(NSLocalizedString("Set \(Int(3) + 1)", bundle: .module, comment: "")) // "Set 4"
      Text(String(localized: "Set \(Int(3) + 1)", bundle: .module)) // "4セット目"
    }
  }
}

これは String(localized:) に渡すキーの型が String でなく String.LocalizationValue になっているためです。

逆に言うと String を渡せないので、ローカライズしたい文字列がすでに String の場合は、何かしら工夫しないと使えません。

おまけ: Textはもっとスッキリ書ける

TextLocalizedStringKeyBundle の両方を渡せるイニシャライザが用意されているので、上記の記述はもっとスッキリ書けます。
詳細は以下の記事をご参照ください。

おわりに

String(localized:)NSLocalizedString の上位互換と言っても過言ではないので、ぜひ使っていきましょう :relaxed:
逆にデメリットがあればコメントなどで教えていただきたいです。

以上 Swift Advent Calendar 2022 の1日目の記事でした。
明日も私で LocalizedStringKeyの簡単な説明(Swift) です。

参考リンク

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5