WWDC Quick Look 💓 By SwiftGGTeam
Streamline your localized strings

Streamline your localized strings

元の動画を見る

ハイライト

Xcode 13 と Foundation は、iOS 15 と macOS Monterey でのローカライズされた文字列宣言、抽出、インポート、および構文処理プロセスを改善し、開発者が SwiftUI を使用できるようにします。String(localized:)AttributedString(localized:)多言語インターフェイスをより確実に提供するための API のフォーマット。

主要内容

チケット販売アプリのボタンには、Order。英語ユーザーは「注文する」と理解できます。翻訳者がこの単語を見たとき、それがボタンなのか、名詞なのか、並べ替えアクションなのか、注文ステータスなのかがわからない場合があります。

これはローカライゼーションに関する最も一般的な問題です。開発者はコンテキストを理解して UI を作成します。トランスレータ処理.stringsファイルには 1 行のテキストしか表示されません。同じ英語の単語でも、言語が異なれば異なる語形が使用される場合があります。数量、性別、通貨、リストを含む文字列も、ユーザーの言語と地域に応じて変化します。

以前は、開発者は多くの場合、次を使用して文字列を連結していました。String(format:)変数を入力して手動で管理します.stringsそして.stringsdict書類。コードは実行できますが、翻訳品質に問題が発生しやすくなります。アラビア語のような右から左に記述する言語には、数字と変数の分離が含まれます。ロシア語のような言語には、複数の複数のルールのセットがあります。スペイン語でも称号と性別の一致が見られます。

Apple はこのセッションでプロセスを 3 つに分割しました。

まず、コードを記述するときにローカライズ可能な文字列を直接宣言します。 SwiftUITextButtonデフォルトでは、文字列がローカライズされたコンテンツとして使用されます。 iOS 15 と macOS Monterey の新機能を使用した一般的な Swift コードString(localized:)

次に、ローカリゼーション システムにコンテキストを与えます。すべての文字列にはコメントが必要です。複雑なプロジェクトでは、テーブルを使用してファイルを分割し、バンドルを使用してフレームワークを指定したり、独自のリソースを拡張したりできます。

3 番目に、Xcode 13 にローカリゼーション ファイルを生成および更新させます。ローカライズのエクスポートは、Swift、Foundation メソッド、Info.plist、およびローカライズ済みとしてマークされたリソースからコンテンツを抽出します。 Import Localization は翻訳結果を書き戻します.strings.stringsdictおよびその他のリソース。

これを行った後は、言語の追加は主にリソースの更新とテスト作業になります。 UI コードを言語ごとに分岐する必要はありません。

詳細

1. UI コードでローカライズされた文字列を宣言する

(03:30) SwiftUI ビューで表示されるテキストは、デフォルトでローカリゼーションに参加します。通常の Swift、UIKit、または AppKit コードでは、次のように使用できます。String(localized:)明示的な宣言。

import SwiftUI

struct OrderView: View {
    var body: some View {
        VStack(spacing: 12) {
            Text("Your order is ready.")

            Button("Order") {
                placeOrder()
            }

            Text(verbatim: "Sample data")
        }
    }

    private func placeOrder() {
    }
}

let title = String(localized: "Order")

キーポイント:

  • Text("Your order is ready.")SwiftUI のテキスト ビューであり、文字列はローカライズされたコンテンツとして扱われます。
  • Button("Order")ボタンのタイトルを宣言します。これにより、ローカリゼーション プロセスも開始されます。
  • placeOrder()これはボタン操作であり、ローカライズとは関係ありません。
  • Text(verbatim: "Sample data")このテキストは翻訳の必要がなく、プレビューまたはモック データに適していることを SwiftUI に明示的に伝えます。
  • String(localized: "Order")モデル、UIKit、AppKit、文字列を返す関数など、SwiftUI ビューの外部で使用されます。

(05:15) 文字列補間もローカリゼーションに参加できます。 Apple はデモのボタンのタイトルに投票を入れました。

import SwiftUI

struct TicketOrderButton: View {
    let count = 3

    var body: some View {
        Button("Order \(count) Tickets") {
            placeOrder(count: count)
        }
    }

    private func placeOrder(count: Int) {
    }
}

let count = 3
let title = String(localized: "Order \(count) Tickets")

キーポイント:

  • let count = 3ランタイム変数を提供します。
  • Button("Order \(count) Tickets")Swift の文字列補間を使用すると、ボタンが表示されるときに実数に置き換えられます。
  • String(localized:)数値形式、複数形、右から左へのテキストでのユーザー好みの変数分離を処理します。
  • セッションはローカライズされた文字列を渡さないように明示的に通知しますString(format:)対処する。

2. コンテキストは翻訳者に任せます

(07:09) トランスレーターは文字列を 1 つずつ処理します。完全なインターフェイスが表示されない場合、コメントがコンテキストになります。

import SwiftUI

struct OrderSummaryView: View {
    let ticketCount: Int

    var body: some View {
        VStack(spacing: 12) {
            Text("Order", comment: "Button: confirms concert tickets booking")

            Text("\(ticketCount) Ordered",
                 comment: "Order summary: total number of tickets ordered")
        }
    }
}

キーポイント:

  • Text("Order", comment: ...)説明するOrder「注文」という名詞や並び替えと誤解されないよう、予約を確定するボタンです。
  • コメントの最初の部分には、文字列が出現する位置を書き込みます。例:ButtonまたはOrder summary
  • コメントの 2 番目の部分には、ビジネス アクションを記述します。例:confirms concert tickets booking
  • Text("\(ticketCount) Ordered", comment: ...)変数の意味を説明するために、翻訳者には Swift 変数名は表示されず、プレースホルダーのみが表示されます。

(06:21) コードを保存するために、類似した英語の断片を結合しないでください。言語が異なれば、必要な語形も異なる場合があります。

import Foundation

let orderNow = String(localized: "Order Now")
let orderLater = String(localized: "Order Later")

キーポイント:

  • Order Nowは完全に翻訳可能な文です。
  • Order Laterこれも独立した文です。
  • セッション この例を使用して説明します。Order「今すぐ注文」と「後で注文」では異なる翻訳が必要になる場合があります。
  • 完全な文により、翻訳者はより多くのコンテキストを得ることができ、実行時のスプライシングによって引き起こされる語順のエラーを回避できます。

3. テーブルとバンドルを使用して文字列の位置を管理する

(11:37) デフォルトで文字列が入りますLocalizable.strings。プロジェクトが大きくなる場合は、表を使用して機能またはページごとにプロジェクトを分割できます。

import SwiftUI

struct UserProfileSubtitle: View {
    let ticketCount: Int

    var body: some View {
        Text("\(ticketCount) Ordered",
             tableName: "UserProfile",
             comment: "Profile subtitle: total number of tickets ordered")
    }
}

キーポイント:

  • Textローカライズされた文字列の表示も引き続き担当します。
  • tableName: "UserProfile"文字列が配置されているテーブルを指定します。Xcode はそれに対応します。UserProfile.strings
  • セッションの場合、フランス語の翻訳が含まれますfr.lproj/UserProfile.strings
  • 翻訳者が理解しやすいように、コメントは引き続き文字列とともにエクスポートされますticketCount意味。

(13:49) フレームワーク内の文字列は、フレームワーク自体のバンドルから読み取る必要があります。それ以外の場合、ホスト アプリは独自のリソースでそれを探しますが、見つかりません。

import SwiftUI

public final class AnyClassInTicketKit {
}

public enum OrderStatus {
    case pending, processing, complete, canceled, invalid(Error)

    var displayName: String {
        switch self {
        case .complete:
            return String(localized: "Complete",
                          bundle: Bundle(for: AnyClassInTicketKit.self),
                          comment: "Standalone ticket status: order finalized")
        case .pending:
            return String(localized: "Pending",
                          bundle: Bundle(for: AnyClassInTicketKit.self),
                          comment: "Standalone ticket status: order waiting to start")
        case .processing:
            return String(localized: "Processing",
                          bundle: Bundle(for: AnyClassInTicketKit.self),
                          comment: "Standalone ticket status: order being processed")
        case .canceled:
            return String(localized: "Canceled",
                          bundle: Bundle(for: AnyClassInTicketKit.self),
                          comment: "Standalone ticket status: order canceled")
        case .invalid:
            return String(localized: "Invalid",
                          bundle: Bundle(for: AnyClassInTicketKit.self),
                          comment: "Standalone ticket status: order invalid")
        }
    }
}

struct OrderStatusLabel: View {
    var body: some View {
        Text(OrderStatus.complete.displayName)
    }
}

キーポイント:

  • AnyClassInTicketKitTicketKit フレームワークが配置されているバンドルを見つけるために使用されます。
  • OrderStatusこれは、フレームワークがホスト アプリに公開するモデルです。
  • String(localized: "Complete", bundle: ...)フレームワーク独自のローカライズされたリソースを指します。
  • comment説明するCompleteボタンアクションと誤解されないようにするための注文ステータスです。
  • Text(OrderStatus.complete.displayName)ホスト アプリがフレームワークによって提供されるローカリゼーション結果を直接使用できるようにします。

4. Xcode 13 でローカライズされたディレクトリを抽出、編集、インポートできるようにします

(14:40) Xcode 13 では、Swift 文字列抽出のコンパイラ サポートが追加されています。ローカライズのエクスポートは、Swift メソッドと Foundation メソッドを読み取り、ローカライズされたコンテンツを抽出します。

import SwiftUI

struct CheckoutView: View {
    let ticketCount: Int
    let price: Decimal

    var body: some View {
        VStack(spacing: 12) {
            Text("\(ticketCount) Ordered",
                 tableName: "UserProfile",
                 comment: "Profile subtitle: total number of tickets ordered")

            Text("Total: \(price, format: .currency(code: "USD"))",
                 comment: "Order subtitle: total price of all tickets")
        }
    }
}

キーポイント:

  • Text("\(ticketCount) Ordered", tableName: ..., comment: ...)Xcodeで抽出されます。
  • Text("Total: ...", comment: ...)これも抽出され、コメントも一緒にローカライズされたディレクトリに入力されます。
  • セッションの説明 Xcode は、アプリ名、Info.plist 内のプライバシーの説明、およびインスペクターでローカライズ済みとしてマークされたリソースも抽出します。
  • プロジェクトがカスタム ラッパーを使用してローカライズされた API をラップする場合、デフォルトでは抽出されません。ビルド設定で構成する必要がありますLocalized String Macro Names構成。

(17:43) エクスポートとインポートは、Xcode メニューで行うか、CI に入れることができます。

xcodebuild -exportLocalizations -workspace VacationPlanet.xcworkspace -localizationPath ~/Documents
xcodebuild -importLocalizations -workspace VacationPlanet.xcworkspace -localizationPath ~/Documents/de.xcloc

キーポイント:

  • 最初の行は、ローカリゼーション ディレクトリをエクスポートし、プロジェクト内の文字列、画像、ファイル、および関連コンテキストを翻訳プロセスに渡します。
  • -workspace VacationPlanet.xcworkspaceワークスペースを指定します。セッションでは、Xcode 13 がすでにワークスペースをサポートしていると述べました。
  • -localizationPath ~/Documentsエクスポート先を指定します。
  • 2行目はドイツ語をインポートします.xclocディレクトリ、Xcode が作成または更新します.strings.stringsdictおよびその他のローカライズされたリソース。
  • これら 2 つのコマンドを定期的に実行すると、新しい UI 文字列を翻訳プロセスに迅速に入力できるようになります。

5. リッチ テキスト、複数形、構文、および書式設定されたデータをローカライズする

18:28AttributedString(localized:)マークダウン構文をサポートします。開発者は、文字範囲を手動で計算することなく、スタイル付きテキストをローカライズできます。

import Foundation

let title = AttributedString(localized: "Your order is **complete**!",
                             comment: "Ticket order confirmation title")

キーポイント:

  • AttributedString(localized:)Swift ネイティブ リッチ テキストを作成します。
  • **complete**Markdown を使用して構文を太字にします。
  • 文全体がローカライズに参加し、書式設定情報は翻訳後も保持されます。
  • コメントは、これが予約確認のタイトルであることを翻訳者に伝えます。

(19:22) 数字を含む文字列を複数指定する必要があります。.stringsdict。コードを言語ごとに複数の分岐に分けて記述する必要はありません。

import Foundation

func orderTitle(ticketCount: Int) -> String {
    String(localized: "Order \(ticketCount) Ticket(s)")
}

キーポイント:

  • ticketCountは複数形を決定する数字です。
  • String(localized:)ローカライズされたリソースを読み取ります。
  • .stringsdictこのキーを各言語の複数のルールにマッピングする責任を負います。
  • セッションでは、エクスポート時に Xcode が言語ごとに必要な複数のカテゴリを補完すると述べています。たとえば、ロシア語ではさらに多くのルールが必要になります。

(22:46) 数字のない「Single、Two、All」は使用できません.stringsdict推測する。ここには 3 つの文字列を明確に記述する必要があります。

import Foundation

func orderButtonTitle(ticketCount: Int) -> String {
    if ticketCount == 1 {
        return String(localized: "Order This Ticket")
    } else if ticketCount == 2 {
        return String(localized: "Order Both Tickets")
    } else {
        return String(localized: "Order All Tickets")
    }
}

キーポイント:

  • ticketCount == 1「このチケット」が該当します。
  • ticketCount == 2「チケット2枚」に対応。
  • else「すべてのチケット」に対応。
  • セッションではロシア語で例を示します。.stringsdictone規則は、数字が 1 だけであるというわけではなく、21 と 31 も同じカテゴリに分類される可能性があります。

(23:31) Foundation は、iOS 15 と macOS Monterey に構文整合性機能を追加しました。マークダウンでinflect: trueランタイムに正しい語形を計算させることができます。

import Foundation

let ticketsCount = 3
let title = AttributedString(localized: "Order ^[\(ticketsCount) Ticket](inflect: true)")

キーポイント:

  • ticketsCount量的変数です。
  • ^[...]構文処理が必要なフラグメントをマークします。
  • (inflect: true)Foundation が実行時の量に基づいて単語の形状を調整できるようにします。
  • セッションの説明 この機能は iOS 15 および macOS Monterey に追加され、現在いくつかの言語をサポートしています。

(25:45) データを提示するときは、手書きのリスト、通貨、および数値形式を避けてください。フォーマッタを使用すると、システムが言語および地域ごとにテキストを生成できるようになります。

import SwiftUI

struct CheckoutTotalView: View {
    let price: Decimal = 9.41
    let genres = ["pop", "rock", "electronic"]

    var body: some View {
        VStack(spacing: 12) {
            Text(genres.formatted(.list(type: .or)))

            Text("Total: \(price, format: .currency(code: "USD"))",
                 comment: "Order subtitle: total price of all tickets")
        }
    }
}

キーポイント:

  • genres.formatted(.list(type: .or))地域の慣例に一致する「OR」リストをシステムに生成させます。
  • price表示される金額です。
  • .currency(code: "USD")通貨コードを指定します。
  • Text("Total: ...", comment: ...)フォーマットされた結果をローカライズされた文字列補間に入れます。
  • セッションは、データ形式をハードコーディングするのではなく、フレームワークに処理させるように開発者に思い出させます。

重要ポイント

  • 対処方法: アプリに「擬似言語ローカリゼーション」チェック ページを追加します。 実行する価値がある理由: セッションでは、ローカリゼーションへの投資がどれほど行われたとしても、さまざまな言語でのアプリの実行効果をテストする必要があります。 開始方法: キーとなる SwiftUI ページを次のように変更します。TextButtonString(localized:)、スキームまたは SwiftUI プレビューで言語を切り替え、長いテキスト、RTL、翻訳の欠落を確認します。

  • 何をすべきか: 注文、ショッピング カート、定期購読のページにあるボタンのコピーを完全な文に分割します。 実行する価値がある理由: Apple は、文字列を連結すると翻訳時に語順や語形の問題が発生する可能性があることを明確に指摘しています。 開始方法: 検索+スプライシングおよびローカル コピー変数、putOrder NowOrder Laterこのタイプのコンテンツを独立したコンテンツに変更しますString(localized:)または SwiftUI 文字列。

  • やるべきこと: アプリ内のすべての短い単語ボタンにコメントを追加します。 実行する価値がある理由:Orderこのような単語はコンテキストによって異なる意味を持ち、コメントはエクスポート ディレクトリとともに翻訳者に提供されます。 開始方法:からText("...")Button("...")String(localized:)まず、同じ動詞や名詞を含む英単語が優先され、場所や業務内容の説明が追加されます。

  • 内容: フレームワークまたは Swift パッケージのユーザーに表示される文字列をフレームワーク リソースに残します。 実行する価値がある理由: セッション記述フレームワーク文字列でバンドルを指定する必要があります。指定しない場合、Foundation はホスト アプリからバンドルを検索します。 開始方法: フレームワーク内で使用しますString(localized:bundle:comment:)、バンドルが使用されますBundle(for:)外部に公開されるフレームワーク リソースの配置displayNameこのタイプのローカライズされたプロパティ。

  • 対処方法: 価格、日付、リスト、チケットの表示をフォーマット API に変更します。 価値がある理由: Foundation のフォーマッタは、多数の言語とロケールの組み合わせをカバーし、ハードコードされた書式設定エラーを減らします。 開始方法: を使用します.formatted(.list(type: .or))リストを処理するには、次を使用します。Text("Total: \(price, format: .currency(code: "USD"))")金額を処理するには、次を使用します。.stringsdictまたはAttributedString(localized:)量的な語形を扱う。

関連セッション

  • SwiftUI アプリをローカライズする — SwiftUI の観点から、文字列、スタイル付きテキスト、画像、レイアウトのローカライズ プロセスについて説明します。
  • Foundation の最新情報 — 詳細な紹介AttributedString、フォーマット API および構文一貫性エンジン。
  • SwiftUI の新機能 — SwiftUI 2021 のテキスト、書式設定、検索、リスト、クロスプラットフォーム UI の更新について説明します。
  • SwiftUI アクセシビリティ: 基本を超えて — プレビュー API とアクセシビリティ API を使用して、多言語テストでの使用に適した複雑なインターフェイスをチェックします。

コメント

GitHub Issues · utterances