WWDC Quick Look 💓 By SwiftGGTeam
Code-along: Explore localization with Xcode

Code-along: Explore localization with Xcode

元の動画を見る

ハイライト

Xcode 26 はオンデバイスモデルを用いて String Catalog の翻訳コメントを自動生成し、さらに文字列を型安全な Swift シンボルへとコンパイルできるようになりました。


主要内容

翻訳担当者は通常、コードを目にすることも、動作中の app に触れることもありません。「Landmarks」という単語だけを渡されても、それが app 名なのか地図上のランドマークなのかは推測するしかありません。同様に、"%@ is contained in %@" の 2 つのプレースホルダが何を指しているかも、文脈なしには正しく訳すことができません。これはローカライズ作業で最も頻発する課題です。開発者は文字列を書き終えたらそのまま離れてしまい、翻訳担当者は何度も問い合わせに戻ってこなければなりません。

本セッションでは Andreas が Landmarks サンプルプロジェクトを使い、Xcode のローカライズパイプラインをゼロから紹介しています。中心となるのは Xcode 26 で導入された 2 つの新機能、コメントの自動生成シンボル生成です。前者はオンデバイスモデルが文字列の出現箇所を解析し、"The text label on a button to cancel the deletion of a collection" のような説明文を自動的に書き起こします。後者は String Catalog のエントリを LocalizedStringResource の静的プロパティや関数へとコンパイルし、コードを書く際にオートコンプリートとコンパイル時チェックを享受できるようにします。両者が解決しようとしているのは同じ問題、すなわち「文字列とその文脈を切り離さない」ということです。


詳細

スタート: Xcode に文字列を自動収集させる01:34

Localizable という名前の String Catalog を新規作成して一度ビルドするだけで、Xcode はコードをスキャンしてローカライズ可能なすべての文字列を catalog に書き込みます。SwiftUI の API(TextButton など)はデフォルトでローカライズ経路を通り、Foundation 側では String(localized:) で明示的にマークします。

// import SwiftUI
Text("Featured Landmark", comment: "Big headline in the hero image of featured landmarks.")

Button("Keep") { }

// import Foundation
String(localized: "New Collection", comment: "Default name for a new user-created collection.")

ポイント:

  • Text("Featured Landmark", comment:): SwiftUI の Text は文字列リテラルをそのままローカライズキーとして受け取り、comment 引数は翻訳担当者向けのコンテキストとして String Catalog に書き込まれます。
  • Button("Keep") { }: Button のタイトル引数も同様にデフォルトでローカライズ対象となり、追加のアノテーションは不要です。
  • String(localized:): Foundation 側の対応 API で、ViewModel、Model、ユーティリティ関数など SwiftUI 以外のコードで利用します。

%lld のようなプレースホルダを含む文字列は、右クリックメニューの “Vary by Plural” を選ぶことで one / other 形を個別に設定でき、実行時に数値に応じて自動で切り替わります。

翻訳担当者にコンテキストを与える06:00

Andreas は良いコメントを 3 つの要素に分解しています。どの UI 要素で使われるか(ボタン、サブタイトル、tab bar など)、周囲にどんな UI 要素があるか、プレースホルダにどんな内容が入るか。

Text("Delete",
comment: "Delete button shown in an alert asking for confirmation to delete the collection.")

String(localized: "Shared by Friends", comment: "Subtitle of post that was shared by friends.")

ポイント:

  • 1 行目のコメントは “Delete” が alert 内に出現する確認削除ボタンであることを明示しており、翻訳担当者が動詞のタイトルと取り違えることを防ぎます。
  • 2 行目のコメントは “Shared by Friends” が post のサブタイトルであることを示しており、翻訳時により口語的な表現を選びやすくなります。

Xcode 26 では文字列の右クリックメニューに “Generate Comment” が追加されました。オンデバイスモデルが文字列の出現箇所を解析してコメントを自動生成し、開発者はそれを承認、書き換え、または追記することができます。Settings → Editing で “automatically generate string catalog comments” を有効にすると、新しく抽出されたすべての文字列に自動でコメントが付与されます。

エクスポートされた XLIFF ファイルでは、自動生成されたコメントに auto-generated マークが付くため、翻訳ツール側でも識別が容易です(09:13)。

<trans-unit id="Grand Canyon" xml:space="preserve">
<source>Grand Canyon</source>
<target state="new">Grand Canyon</target>
<note from="auto-generated">Suggestion for searching landmarks</note>
</trans-unit>

マルチターゲットプロジェクト: #bundletableName09:58

プロジェクトを framework や Swift Package に分割すると、各 target が独自の String Catalog を持つ可能性があります。ローカライズ API は実行時に正しいリソースを見つけるために bundle 引数を必要とします。

// メイン app 内
Text("My Collections",
comment: "Section title above user-created collections.")

// Swift Package または Framework 内
Text("My Collections",
bundle: #bundle,
comment: "Section title above user-created collections.")

ポイント:

  • メイン app 内では bundle 引数を省略すると Bundle.main 相当となり、Xcode はメイン bundle からリソースを探します。
  • #bundle は Xcode 26 で導入されたマクロで、コンパイル時に現在の target に対応する bundle へと展開されます。framework や Swift Package 内に書けば、その framework 自身のリソースを参照できます。
  • #bundle は古いバージョンの OS でも動作するため、移行時に可用性チェックは不要です。

文字列を機能ごとにファイル分割したい場合は、tableName 引数を加えるだけで対応できます(10:56)。

Text("My Collections",
tableName: "Discover",
comment: "Section title above user-created collections.")

Text("My Collections",
tableName: "Discover",
bundle: #bundle,
comment: "Section title above user-created collections.")

ポイント:

  • tableName: "Discover" を指定すると、この文字列はデフォルトの Localizable.xcstrings ではなく Discover.xcstrings へ抽出されます。
  • 1 つの String Catalog ファイルが 1 つの table に対応するため、feature、screen、user flow ごとに table で分類することで単一ファイルの肥大化を避けられます。

型安全なシンボル生成17:31

Xcode 26 は逆方向のフローもサポートしています。先に String Catalog で手動で key(TITLESUBTITLE など)を追加すると、Xcode は LocalizedStringResource 上に対応する静的プロパティや関数を自動生成します。プレースホルダを含むエントリは関数として生成され、プレースホルダ名がそのまま引数ラベルになります。

// SwiftUI でシンボルを使用
Text(.introductionTitle)

.navigationSubtitle(.subtitle(friendsPosts: 42))


// Foundation でシンボルを使用
String(localized: .curatedCollection)


// LocalizedStringResource を受け取るカスタム型
struct CollectionDetailEditingView: View {
    let title: LocalizedStringResource

    init(title: LocalizedStringResource) {
        self.title = title
    }
}
CollectionDetailEditingView(title: .editingTitle)

ポイント:

  • Text(.introductionTitle): プレースホルダのない key は静的プロパティとして生成されます。先頭に . を付けて呼び出せば、型推論によって LocalizedStringResource が選ばれます。
  • .subtitle(friendsPosts: 42): プレースホルダを含む key は関数として生成され、プレースホルダ名 friendsPosts が引数ラベルとなり、コンパイル時に型がチェックされます。
  • String(localized: .curatedCollection): Foundation の String(localized:)LocalizedStringResource を受け取るため、シンボルは SwiftUI コードと非 SwiftUI コードの両方で共通に使えます。
  • デフォルト table Localizable のシンボルは直接 LocalizedStringResource 上に生成されますが、カスタム table(例えば Discover)のシンボルは LocalizedStringResource.Discover 名前空間にネストされ、.Discover.xxx の形でアクセスします。
  • 新規プロジェクトではデフォルトで有効です。既存プロジェクトでは build setting の “Generate String Catalog Symbols” を有効にするだけで利用できます。

2 つのワークフローは “Refactor > Convert Strings to Symbols” で相互変換でき、各呼び出し箇所の書き換え結果を Xcode がプレビュー表示します(19:37)。


重要ポイント

  • やること: チームの Xcode Settings で “automatically generate string catalog comments” をデフォルトで有効にする。

    • なぜ価値があるか: 翻訳の手戻りの大半はコンテキスト不足に起因しています。オンデバイスモデルが生成する説明は日常的な文字列の大部分をカバーし、開発者は不正確な箇所のみを手動で微調整すれば済みます。
    • 始め方: Settings → Editing でこのオプションにチェックを入れます。XLIFF をエクスポートする際にエントリに auto-generated ラベルが付いていることを確認し、翻訳ツール側に「これは機械の初稿である」ことを伝えましょう。
  • やること: マルチターゲットプロジェクト内のハードコードされた Bundle(for:)#bundle マクロへ置き換える。

    • なぜ価値があるか: #bundle はコンパイル時に展開されるため、実行時のリフレクションによる型解決を回避でき、framework ごとに helper を書く重複作業も省けます。
    • 始め方: framework と Swift Package 内で Bundle(for:Bundle.module を全文検索し、1 つずつ bundle: #bundle に置き換えます。一度ビルドして、String Catalog の bundle 列が現在の target を正しく指していることを確認しましょう。
  • やること: 文字列が 200 件を超える catalog を table 単位で分割し、feature 名(Discover、Settings、Profile など)で命名する。

    • なぜ価値があるか: 単一の String Catalog ファイルが大きくなりすぎると Xcode の編集が重くなり、翻訳作業のコンフリクトも増えます。table 分割によって複数人が異なる feature を編集しても干渉しなくなります。
    • 始め方: 各 feature モジュール内に <Feature>.xcstrings を新規作成し、呼び出し側に tableName: "<Feature>" を追加します。#bundle と組み合わせれば、framework 側でも自動的に正しいリソースを解決できます。
  • やること: 成熟した feature では “Refactor > Convert Strings to Symbols” を使ってシンボル参照へ変換する。

    • なぜ価値があるか: シンボル化により key と value が分離され、文言の修正は catalog のみで完結し、コードに手を加える必要がなくなります。オートコンプリートでタイポを減らし、プレースホルダの型もコンパイル時に検証されます。
    • 始め方: まず新しい feature でパイロット導入し、build setting “Generate String Catalog Symbols” を有効化します。プレビュー画面で 1 件ずつ key 名を確認し(自動生成された feedTitle をより意味的な名前に変更するなど)、commit するとよいでしょう。

関連 Session

コメント

GitHub Issues · utterances