WWDC Quick Look 💓 By SwiftGGTeam
Localize your SwiftUI app

Localize your SwiftUI app

元の動画を見る

ハイライト

SwiftUI は、Text、LocalizedStringKey、Markdown、FormatStyle、キーボード ショートカット、および Xcode 13 エクスポート プロセスのローカリゼーション機能を補完し、開発者が文字列、形式、レイアウトをシステムに任せて処理できるようにし、多言語対応における手動修正を削減します。

主要内容

多くのアプリケーションを初めてローカライズする場合、問題は細部にあります。

英語のインターフェイスでは、ボタンは次のように書かれます。Done、ラベルは次のように書かれますIngredients、問題ないようです。ロシア語、アラビア語、リトアニア語になると、テキストの長さ、意味的文脈、書く方向、キーボードのレイアウトがすべて変わります。

以前は、開発者は多くの場合、これらの変更を自分で確認する必要がありました。どの文字列を翻訳者にエクスポートできますか?どれのIngredientsレシピの「材料」はどれですか?メニューの「具材」はどれですか?地域ごとにカロリー単位を表示するにはどうすればよいですか? US 以外のキーボードでショートカット キーを押すことはできますか?これらの問題は、リリースまで簡単に延期される可能性があります。

このセッションでは、Apple は Fruta サンプル アプリケーションを使用して新しいパスをデモンストレーションしました。SwiftUI のデフォルトは次のとおりです。Text文字列リテラルはローカライゼーション キーとして扱われます。カスタムビューで使用できるのはLocalizedStringKey同じ行動を続けてください。スタイルは Markdown に任せます。値は新しいフォーマット API に委ねられます。レイアウトとキーボード ショートカットはシステムに委ねられ、自動的に適応されます。

Xcode 13 ではエクスポート プロセスも変更されます。ローカライズされたコンテンツをエクスポートする場合、Xcode はターゲットをビルドし、Swift コンパイラーを通じて抽出できます。LocalizedStringKey。複数行の文字列と SwiftUI カスタム ビューの文字列を、より正確にローカリゼーション カタログに入力できるようになりました。

その結果、開発者は言語ごとに UI を作成する必要がなくなります。翻訳可能なコンテンツを公開し、翻訳に十分なコンテキストを与え、システムを使用してデータをフォーマットするだけで、SwiftUI が言語の違いの多くを処理します。

詳細

文字列リテラルはローカリゼーション テーブルを自動的にチェックします

(01:34) SwiftUIText文字列リテラルを受信すると、ローカライズされた文字列検索がメイン バンドルで行われます。ボタンのタイトル、ラベル、プロンプトのコピーはすべてここから始めることができます。

import SwiftUI

struct RewardsSheet: View {
    let done: () -> Void

    var body: some View {
        Button(action: done) {
            Text("Done", comment: "Button title to dismiss rewards sheet")
        }
    }
}

キーポイント:

  • Button(action: done)受信したものを使用するdoneクロージャにより報酬ページが閉じられます。
  • Text("Done", comment: ...)バンドルDoneローカライズ可能な文字列として公開されます。
  • comment翻訳にコンテキストを与える: これは、ボーナス シートを閉じるボタンのタイトルです。
  • 実行時に、SwiftUI は現在の言語に基づいてバンドルから対応する翻訳を検索します。

(01:58) 文字列補間も抽出されます。 Xcode 13 は、補間変数の型に基づいて形式指定子を推測します。

import SwiftUI

struct RewardsCard: View {
    let totalStamps: Int

    var body: some View {
        Text("You are \(10 - totalStamps) points away from a free smoothie!")
    }
}

キーポイント:

  • totalStampsユーザーが獲得したポイント数です。
  • 10 - totalStamps無料のスムージーまでどれだけ離れているかを計算してください。
  • 補間式は、エクスポートされたローカライズされた文字列ファイルまたはカタログに組み込まれます。
  • Xcode 13ベースInt手動による書き込みエラーを減らすための型推論形式のプレースホルダー%d%@機会。

(02:06)同じ英語の単語でも、シナリオによっては異なる翻訳が必要になる場合があります。Text通過できるtableName文字列を別のテーブルに置きます。

import SwiftUI

struct IngredientHeaders: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text(
                "Ingredients.recipe",
                tableName: "Ingredients",
                comment: "Ingredients in a recipe. For languages that have different words for \"Ingredient\" based on semantic context."
            )
            Text(
                "Ingredients.menu",
                tableName: "Ingredients",
                comment: "Ingredients in a smoothie. For languages that have different words for \"Ingredient\" based on semantic context."
            )
        }
    }
}

キーポイント:

  • 両方の文字列が配置されますIngredients表と中は同じタイプのコピーライティングを管理しやすくします。
  • Ingredients.recipeレシピのコンテキストで使用されます。
  • Ingredients.menuスムージー メニューのコンテキストで使用されます。
  • comment翻訳者に明示的に伝えてください: 一部の言語では、意味論的な文脈に基づいて異なる単語が選択されます。

カスタム SwiftUI ビューは LocalizedStringKey を受け取る必要があります

(02:52) この問題はカスタム ビューでよく発生します。あなたが置いたStringコンポーネントに渡され、コンポーネント内で渡されます。Text, Xcode は、このリテラルを SwiftUI ローカリゼーション キーとして扱うことができない場合があります。

解決策は、パラメータのタイプを次のように変更することです。LocalizedStringKey

import SwiftUI

struct Card: View {
    var title: LocalizedStringKey
    var subtitle: LocalizedStringKey

    var body: some View {
        Circle()
            .fill(BackgroundStyle())
            .overlay(
                VStack(spacing: 16) {
                    Text(title)
                    Text(subtitle)
                }
            )
    }
}

struct OrderCompleteView: View {
    var body: some View {
        Card(
            title: "Thank you for your order!",
            subtitle: "We will notify you when your order is ready."
        )
    }
}

キーポイント:

  • titleそしてsubtitle使用LocalizedStringKey、SwiftUI のローカライズされたセマンティクスを保持します。
  • Text(title)そしてText(subtitle)実行時にバンドルから翻訳を読み取ります。
  • Card(...)文字列リテラルは引き続き呼び出し側サイトに渡され、書き込みメソッドは変更されません。
  • Xcode がローカライズされたコンテンツをエクスポートする場合、それを抽出して渡すことができます。Cardタイトルとサブタイトル。

(03:47) Xcode 13 は、ローカライズされたコンテンツをエクスポートするときにコンパイラーを使用できます。複数行の文字列も正しく解析されます。

import SwiftUI

struct SmoothieDescription: View {
    var body: some View {
        Text("""
        A delicious blend of tropical fruits and blueberries will
        have you sambaing around like you never knew you could!
        """,
        comment: "Tropical Blue smoothie description")
    }
}

キーポイント:

  • 三重引用符は複数行の文字列を宣言し、長い説明のコピーに適しています。
  • Textローカライズ可能なコンテンツを引き続き受信します。
  • commentこの説明をトロピカル ブルー スムージーとしてマークします。
  • Xcode 13 は Swift コンパイラーを使用して文字列を抽出し、複数行のテキスト解析エラーを減らします。

レイアウトはデフォルトで長いテキストと右から左へ記述する言語をサポートします

(04:12) ローカライズ後は、通常、テキストが長くなります。 SwiftUI コントロールはText直接切断しないように線を巻き付けてください。

(04:26) 右から左へ記述する言語はレイアウトの方向にも影響します。 SwiftUI は、必要に応じてテーブルのセルのレイアウトを反転し、タブ バーのシンボルをミラーリングします。

開発者はハードコーディングを避ける必要があるleftそしてright。 Appleがプレゼンテーションで強調したことleadingこの方向性は執筆とは何の関係もありません。

import SwiftUI

struct SmoothieSummary: View {
    let smoothieTitle: LocalizedStringKey
    let ingredients: LocalizedStringKey

    var body: some View {
        VStack(alignment: .leading) {
            Text(smoothieTitle)
                .font(.headline)
            Text(ingredients)
        }
    }
}

キーポイント:

  • VStack(alignment: .leading)セマンティック指向を使用します。
  • 左から右に記述する言語では、leading通常は左側に対応します。
  • 右から左に記述する言語では、システムは次のことができます。leading反対側への地図。
  • Textローカライズされたスムージー名と成分テキストの表示を担当します。

マークダウンにより、翻訳によってスタイルの配置が決定されます

(05:13) SwiftUITextMarkdown を使用してローカライズされた文字列を直接表示できます。これを行う際のポイントは、スタイルと翻訳を同じ文字列に入れることです。

import SwiftUI

struct LemonberryDescription: View {
    var body: some View {
        Text(
            "A refreshing blend that's a *real kick*!",
            comment: "Lemonberry smoothie description"
        )
    }
}

キーポイント:

  • *real kick*マークダウン強調を使用します。
  • 文字列はローカライズされたコンテンツとして引き続きエクスポートされます。
  • 翻訳で​​は、ターゲット言語に応じて強調語を調整できます。
  • SwiftUI はスタイルを直接レンダリングしますText、複数のテキスト断片を分割する必要はありません。

FormatStyle はローカライズされた値を直接処理します

(06:04) iOS 15 より前では、Fruta はMeasurementFormatter、カロリーを文字列に変換します。

import Foundation
import SwiftUI

struct LegacyCaloriesView: View {
    let nutritionFactKilocalories: Double

    static let measurementFormatter: MeasurementFormatter = {
        let formatter = MeasurementFormatter()
        formatter.unitStyle = .long
        formatter.unitOptions = .providedUnit
        return formatter
    }()

    var body: some View {
        let calories = Measurement<UnitEnergy>(
            value: nutritionFactKilocalories,
            unit: .kilocalories
        )

        return VStack(alignment: .leading) {
            Text(Self.measurementFormatter.string(from: calories))
            Text("Energy: \(calories, formatter: Self.measurementFormatter)")
        }
    }
}

キーポイント:

  • Measurement<UnitEnergy>エネルギー値を単位で表します。
  • MeasurementFormatter単位スタイルを次のように設定します。.long
  • .providedUnit着信を維持する.kilocaloriesユニット。
  • 初めTextまず測定値を文字列に変換します。
  • 2番目Text補間にフォーマッタを使用します。

(06:22) iOS 15 の新しいフォーマット API では、プレースメントのフォーマットを宣言できます。

import Foundation
import SwiftUI

struct CaloriesView: View {
    let nutritionFactKilocalories: Double

    var body: some View {
        let calories = Measurement<UnitEnergy>(
            value: nutritionFactKilocalories,
            unit: .kilocalories
        )

        return VStack(alignment: .leading) {
            Text(calories.formatted(.measurement(width: .wide, usage: .food)))
            Text("Energy: \(calories, format: .measurement(width: .wide, usage: .food))")
        }
    }
}

キーポイント:

  • calories.formatted(...)現在のロケールに適した表示テキストを直接返します。
  • .measurement(width: .wide, usage: .food)測定値の表示幅や使用シーンを宣言します。
  • usage: .foodこれが食料エネルギーのシナリオであることをシステムに伝えます。
  • Text補間を直接受信可能format:、コードは UI セマンティクスに近くなります。

キーボード ショートカットは、現在のキーボード レイアウトに従って再マップされます。

(06:53) macOS Monterey および iPadOS 15 は、ユーザーの現在のキーボード レイアウトに基づいて SwiftUI で定義されたショートカット キーを調整します。

アップルの例は、Command +。 US キーボードでは入力が簡単です。リトアニア語キーボードでは、Command キーを押しながら元の組み合わせを入力できません。システムはそれを入力可能な組み合わせに再マップします。

import SwiftUI

struct SmoothieCommands: Commands {
    let smoothie: Smoothie

    var body: some Commands {
        CommandMenu(Text("Smoothie", comment: "Menu title for smoothie-related actions")) {
            SmoothieFavoriteButton(smoothie)
                .keyboardShortcut("+")
        }
    }
}

struct Smoothie {}

struct SmoothieFavoriteButton: View {
    let smoothie: Smoothie

    init(_ smoothie: Smoothie) {
        self.smoothie = smoothie
    }

    var body: some View {
        Button("Favorite") {}
    }
}

キーポイント:

  • CommandMenuタイトルに使われているのは、Text、翻訳コメントを追加できます。
  • SmoothieFavoriteButton(smoothie)メニュー内のアクションエントリです。
  • .keyboardShortcut("+")お気に入りを追加するためのショートカット キーを定義します。
  • システムは現在のキーボード レイアウトに従って入力の組み合わせを調整するため、開発者はキーボード レイアウトごとに分岐を記述する必要はありません。

Xcode 13 のローカリゼーション ワークフローはコンパイラに依存します

(08:47) Xcode 13 は Swift 文字列抽出を強化します。プロジェクトのビルド設定Use Compiler to Extract Swift Strings新しい Swift プロジェクトではデフォルトで有効になっており、古い SwiftUI プロジェクトでは手動で開くことができます。

import SwiftUI

struct StepperView: View {
    var label: LocalizedStringKey
    @Binding var value: Int

    var body: some View {
        Stepper(value: $value) {
            Text(label)
        }
    }
}

キーポイント:

  • label使用LocalizedStringKey、カスタム ビューがローカライズされた情報を飲み込むのを防ぐため。
  • @Binding var value外部ステータスを受信します。
  • Stepper(value: $value)バインド値を変更します。
  • Text(label)ステッパーの横にローカライズ可能なラベルを表示します。

(13:53) Xcode ローカリゼーション カタログには、キー、ソース文字列、翻訳、コメントが表示されます。翻訳者は変数名を確認できないため、あいまいな文字列はコメント化する必要があります。

import SwiftUI

struct FavoritesTab: View {
    var body: some View {
        Label {
            Text("Favorites", comment: "Tab title. Noun: the list of favorite smoothies.")
        } icon: {
            Image(systemName: "heart")
        }
    }
}

キーポイント:

  • Labelタブバー項目に使用されます。
  • タイトル用Text提供するには初期化が必要ですcomment
  • 注意事項Favoritesコレクションリストを指す名詞です。
  • 翻訳者は、それがアクションなのか列名なのかを推測する必要がありません。

重要ポイント

  • 何をすべきか: カスタム コンポーネントに対してローカリゼーション タイプの監査を実行します。 実行する価値がある理由: このセッションでは、カスタム SwiftUI ビューで使用する必要があることが明確になります。LocalizedStringKeyまたは受け取るTextそうしないと、文字列が正しくエクスポートされない可能性があります。 開始方法: プロジェクト内を検索しますvar title: Stringlet label: String、まず表示パラメータを次のように変更します。LocalizedStringKeyを確認し、擬似言語プレビューを使用して確認します。

  • 対処方法: 支払い、回収、購読などのリスクの高いコピーライティングの翻訳注釈を補足します。 実行する価値がある理由: Xcode カタログの翻訳では変数名が表示されません。Buy recipe for %@このような文字列は曖昧になる可能性があります。 開始方法:Text("Favorites")に変更しますText("Favorites", comment: "...")Label使用する必要がありますLabel { Text(...) } icon: { ... }書き方。

  • 内容: 数値表示を手書き文字列からFormatStyle実行する価値がある理由: カロリー、長さ、体重、日付の形式は地域によって異なります。Text("%lf Calories")翻訳には地域差が考慮されます。 開始方法:MeasurementFormatterまたは、文字列の連結は次のように置き換えられます。value.formatted(...)、存在するText補間で使用されるformat:

  • 対処方法: Markdown を使用して、ローカライズされたテキストの強調スタイルを管理します。 実行する価値がある理由: 言語が異なれば、強調する立場も異なる場合があります。翻訳可能な文字列にスタイルを埋め込むと、翻訳によってキーワードが調整されます。 開始方法: 複数入れるTextスプライスされた強調コピーはマークダウン文字列に結合されます。Text("A *real kick*!")

  • 内容: 公開前に疑似言語とキーボード レイアウトのチェックを追加します。 実行する価値がある理由: 擬似言語では抽出されていない文字列が公開される可能性があり、US 以外のキーボードでは入力できないショートカット キーが公開される可能性があります。 開始方法: スキーム エディターのアプリ言語でアクセント付き擬似言語を選択します。一般的なコマンド用に予約されています.keyboardShortcut(...)、macOS と iPadOS にレイアウトの再マッピングを行わせます。

関連セッション

コメント

GitHub Issues · utterances