Sample Code

Building a Localized Food-Ordering App ローカライズされたFood-Orderingアプリを構築する

Format, style, and localize your app’s text for use in multiple languages with string formatting, attributed strings, and automatic grammar agreement. あなたのアプリのテキストを複数の言語で使用するために、書式設定して、体裁を整えて、そしてローカライズしてください、文字列書式設定、属性付き文字列、そして自動文法一致を使って。
Download

Overview 概要

The Caffé sample app presents a list of menu items — each of which are available in a variety of sizes — that users can order from a café. In presenting the various food items and helping to prepare an order, the app uses various Foundation APIs to localize and stylize the app’s text: Caffé見本アプリは、ユーザが café から注文できるメニュー項目 — そのそれぞれは様々なサイズで利用可能です — からなるあるリストを提示します。様々な食品項目を提示することおよび注文するのを助けることにおいて、アプリは様々なFoundation APIを使ってアプリのもつテキストをローカライズしたり体裁を整えたります:

  • FormatStyle-based formatting customizes the display of currency values, dates and times, and lists of strings. FormatStyle基盤の書式設定は、通貨値それら、日付と時刻それら、そして文字列のリストそれらの表示をカスタマイズします。

  • Attributed strings allow the app to easily create styled text with Markdown for display in SwiftUI views. The app also uses localized attributed strings to build strings at runtime, even when the word order changes in different languages. 属性付き文字列は、アプリにスタイル付きテキストをMarkdownで簡単に作成させます、SwiftUIビューにおいて表示するために。アプリはまた、ローカライズされた属性付き文字列を使って文字列を実行時に組み立てます、たとえ単語順序がさまざまな異なる言語において変わる時でさえ。

  • Automatic grammar agreement handles localization situations when strings need to adjust at runtime to match grammatical gender or number in certain languages. 自動文法一致(ある語が別の語に応じて屈折(inflect 語形変化)することを一致(agreement)という)は、さまざまなローカライゼーション状況を取り扱います、文字列それらが実行時に調節を行って文法的性(文法性、文法的性別)や数を特定言語に適合させなければならない時に。

Use Formatters to Format Strings at Runtime フォーマッタを使って文字列を実行時に書式設定する

When the app launches, the user can choose one of several foods to add to their order. When the user chooses a food item, a new view shows the item’s ingredients and the available sizes with corresponding prices. アプリが起動する時、ユーザはいくつかの食品を選んで自身の注文に加えます。ユーザがある食品項目を選ぶ時、新しいビューがその項目の原材料と利用可能なサイズを該当する価格とともに示します。

The ingredient list shows an example of formatting a list of items, using the formatted(_:) method defined on the Swift Sequence type. It starts with the an array of ingredients defined by the Food type. In FoodHeaderView, the ingredientText variable takes the ingredient strings, maps each to a localized string, and then uses the formatted(_:) method to create a comma-separated list. By adding the ListFormatStyle list type ListFormatStyle.ListType.and as a format style parameter, the formatter places an “and” (or its localized equivalent) before last member of the list. 原材料リストは、いくつかの項目からなるリストを書式設定する例を、SwiftのSequence型上で定義されるformatted(_:)メソッドを使って示します。それは、ingredients(原材料それら)からなるある配列で開始します、それはFood型で定義されます。FoodHeaderViewにおいて、ingredientText変数は原材料文字列それらをとり、それぞれをローカライズされた文字列へとマップして、それからformatted(_:)メソッドを使ってコンマ区切りリストを作成します。ListFormatStyleリスト型をListFormatStyle.ListType.andに書式設定形式パラメータとして加えることによって、フォーマッタは “and”(またはそれのローカライズされた同等物)をリストの最後の項の前に置きます。


private var ingredientText: String {
    food.ingredients.map(\.localizedDescription).formatted(.list(type: .and))
}

In English, the ingredient text reads “Our pizza is made from: prosciutto, cheese, flour, and tomatoes.” In Spanish, the list reads “Nuestro pizza está hecho de: prosciutto, queso, harina y tomates.” 英語では、原材料テキストは “Our pizza is made from: prosciutto, cheese, flour, and tomatoes” と読めます。スペイン語では、リストは “Nuestro pizza está hecho de: prosciutto, queso, harina y tomates” と読めます。

The app also uses string formatters to present the price of each item, as seen here: アプリはまた、文字列フォーマッタを使って各項目の値段を表示します、ここに見られるように:


func localizedPrice(_ size: FoodSize) -> String {
    price[size]!.formatted(.currency(code: "USD"))
}

As with the list of ingredients earlier, the formatted(_:) method applies directly to the type it formats. In this case, the formatted type is a Decimal; this type conforms to Swift’s BinaryInteger, which defines the formatted(_:) method. A FormatStyle parameter indicates that the formatting should format the price as a currency, using U.S. dollars. 前の原材料のリストでのように、formatted(_:)メソッドはそれが書式設定する型に直接に適用されます。この場合には、書式設定された型はDecimalです;この型はSwiftのもつBinaryIntegerに準拠します、それはformatted(_:)メソッドを定義します。FormatStyleパラメータは、書式設定は、値段をある通貨として、U.S.ドルを使って、書式設定すべきであることを指し示します。

For more sophisticated formatting needs, some format styles support chaining modifier methods to customize a default style. The Caffé app includes a companion app for Apple Watch that shows the next date when the user is eligible to receive a free coffee. The Date presented in this view customizes the default dateTime format style to show only the weekday, hour, and minute: さらに洗練された書式設定の需要のために、いくつかの書式設定スタイルは、修飾子メソッドの連鎖をサポートすることで省略時のスタイルをカスタマイズします。Caffé アプリは、あるコンパニオンアプリをApple Watchのために含みます、それはユーザが無料コーヒーを受け取る資格がある次回の日付を示します。このビューにおいて提示されるDateは、省略時のdateTime書式設定形式をカスタマイズして、週日、時間、そして分だけを示すようにします。


var str = date.formatted(.dateTime
                            .locale(locale)
                            .minute()
                            .hour()
                            .weekday()
                            .attributed)

Use Attributed Strings to Style Text 属性付き文字列を使ってテキストの体裁を整える

The previous listing also uses the attributed modifier to return an AttributedString. Attributed strings contain text and metadata that applies to ranges of that text. In this case, the attributed string returned by the formatter uses the dateField attribute to mark which ranges of text correspond to which parts of the formatted date. This allows the app to find the weekday attribute in the attribute container and change it to an orange foreground color attribute. The SwiftUI view can then use this attribute when styling the watch display. 前のコード出力はまた、attributed修飾子を使ってAttributedStringを返します。属性付き文字列は、テキストとメタデータを含みます、それはそのテキストのいくつかの範囲に適用されます。この場合には、フォーマッタによって返される属性付き文字列は、dateField属性を使って、テキストのどの範囲が、この書式設定された日付のどの部分に対応するかを印します。これは、アプリに週日属性を属性コンテナにおいて見つけさせて、それをオレンジ背景色属性へと変えさせます。SwiftUIビューは、それからこの属性をウォッチディスプレイの体裁を整える時に使用できます。


let weekday = AttributeContainer
    .dateField(.weekday)


let color = AttributeContainer
    .foregroundColor(.orange)


str.replaceAttributes(weekday, with: color)

AttributedString is strongly-typed, meaning that all attributes must have defined names and value types. AttributedString defines attributes for Foundation, SwiftUI, AppKit, and UIKit in its AttributeScopes type. For common inline attributes like emphasis and links, attributed strings support initialization from with Markdown syntax, either in source or in .strings files. The following entry from the Spanish localization’s Localizable.strings file shows Markdown formatting for strong emphasis (**), regular emphasis (_), and links ([] for link text, followed by a URL in parentheses): AttributedStringは強く型付けされます、それは全ての属性が名前それらと値型それらを定義されていなければならないことを意味しています。AttributedStringは、属性をFoundation、SwiftUI、AppKit、そしてUIKitに対してそれのAttributeScopes型において定義します。強調およびリンクのような普通のインライン属性に対して、属性付き文字列は、Markdown構文を使うものからの初期化を、ソースにおいてまたは.stringsファイルにおいていずれでもサポートします。スペイン語ローカライゼーションのもつLocalizable.stringsファイルからの以下の登録項目は、Markdown書式設定を強い強調(**)、通常の強調(_)、そしてリンク(リンクテキストに対する[]、それに続けて丸括弧の中のURL)に対して示します:


"**Thank you!**" = "**¡Gracias!**";
"_Please visit our [website](https://www.example.com)._" = "_Visita nuestro [sitio web](https://www.example.com)._";

An app can also define custom attributes, as Caffé does with its RainbowAttribute type, an attribute that indicates a range of text to display in multiple colors. The Caffé app adds this attribute by: アプリはまたあつらえの属性を定義できます、Caffé がそれのRainbowAttribute型、複数の色で表示するテキストの範囲を指し示すある属性、でするように。Caffé アプリはこの属性を以下によって加えます:

  1. Defining the RainbowAttribute as an extension of CodableAttributedStringKey, and providing the name and value type of the attribute. RainbowAttributeCodableAttributedStringKeyの拡張として定義する、そして属性の名前と値の型を提供する。

  2. Extending AttributeScopes to define a new AttributeScope called CaffeAppAttributes, whose one member is rainbow, of type RainbowAttribute. The app also extends AttributeScopes with caffeApp, a variable of the CaffeAppAttributes type, that allows access to the Caffé app’s custom attributes with dynamic member lookup syntax. AttributeScopesを拡張して新しいAttributeScopeCaffeAppAttributesと呼ばれるものを定義します、それのあるメンバはrainbowです、型RainbowAttributeの。アプリはまたAttributeScopescaffeAppCaffeAppAttributes型の可変のもので拡張します、それは Caffé アプリのもつあつらえの属性それらに動的メンバ検索構文でアクセス可能にします。

  3. Extending AttributeDynamicLookup to provide a subscript method that takes key paths of type CaffeAppAttributes. This allows code to use dot syntax when looking up the members of CaffeAppAttributes. AttributeDynamicLookupを拡張してある添え字メソッドを提供する、それは型CaffeAppAttributesのキーパスをとるものです。これは、コードにドット構文をCaffeAppAttributesのメンバを検索する時に使えるようにします。


enum RainbowAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey {
    enum Value: String, Codable, Hashable {
        case plain
        case fun
        case extreme
    }
    
    static var name: String = "rainbow"
}


extension AttributeScopes {
    struct CaffeAppAttributes: AttributeScope {
        let rainbow: RainbowAttribute
    }
    
    var caffeApp: CaffeAppAttributes.Type { CaffeAppAttributes.self }
}


extension AttributeDynamicLookup {
    subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.CaffeAppAttributes, T>) -> T {
        self[T.self]
    }
}

The implementation of RainbowText uses these attributes by creating an AttributedString and calling a private annotateRainbowColors(from:) method to apply its color attributes. RainbowTextの実装は、これら属性の使用を、AttributedStringを作成することそしてそれの色属性を適用するためにプライベートなannotateRainbowColors(from:)メソッドを呼び出すことによって行います。 To create an AttributedString that uses custom attribute scopes, Caffé uses the init(localized:options:table:bundle:locale:comment:including:) initializer, passing the key path to the custom attribute name as the including: parameter:


init(_ localizedKey: String.LocalizationValue) {
    attributedString = RainbowText.annotateRainbowColors(
        from: AttributedString(localized: localizedKey, including: \.caffeApp))
}

To apply a custom attribute in a string, a caller uses the Markdown extension syntax, as seen in the following example, which applies two different values of the rainbow attribute: あつらえの属性をある文字列において適用するには、呼び出し側はMarkdown拡張構文を使います、以下の例で見られるように、それはrainbow属性の2つの異なる値を適用します:


RainbowText("^[Fast](rainbow: 'fun') & ^[Delicious](rainbow: 'extreme') Food")
    .font(.slogan)
    .frame(maxWidth: 260, alignment: .leading)

Simplify Localization by Performing Grammar Agreement Automatically ローカライゼーションを文法一致を自動的に実行することによって簡単にする

Some languages’ grammar require that nouns, adjectives, articles, and other parts of speech agree in number or gender with other parts of a sentence. Localized attributed strings can perform this agreement by using a template string to format the values at runtime. いくつかの言語のもつ文法は、名詞、形容詞、冠詞、そしてそのほか話し言葉の一部が、数または性別において文の他の部分と一致することを必要とします。ローカライズされた属性付き文字列は、この一致を実行することができます、テンプレート文字列を使って値それらを実行時に書式設定することによって。

In Caffé, each food’s detail view has a button indicating how many of each item the user has selected to add to their order. The app fills in this button text with the number, size, and food item to add to the order: Caffé において、各食べ物のもつ詳細ビューは、ユーザが選択した、自身の注文に加えることになる各項目がいくつかを指し示しているボタンを持ちます。アプリは、このボタンテキストを、注文に加えることになる個数、サイズ、そして食べ物項目で満たします:


Button(
    "Add ^[\(quantity) \(foodSizeSelection.localizedName) \(food.localizedName)](inflect: true) to your order",
    action: orderButtonTapped
)

The syntax ^[text](inflect:true) tells the generated attributed string to inflect the string, meaning to perform automatic grammar agreement on the range of text within the square braces. This process takes into account the value of any numeric substitutions and grammatical gender of string substitutions. In English, this causes the food name to pluralize when quantity is not equal to 1. 構文^[text](inflect:true)は、生成された属性付き文字列にその文字列を屈折(語形変化)させるように伝えます、自動的に文法一致を角括弧内のテキストの範囲上で実行しようとして。この処理は、あらゆる数値的置換および文字列置換の文法的性の値を考慮に入れます。英語では、これは食べ物名が複数形になることをquantity1と等しくない場合に引き起こします。

In Spanish, the localized string in the .strings file uses the parameter reordering syntax to place the noun before the adjective, like the following: スペイン語では、.stringsファイルの中のローカライズされた文字列は、パラメータ再配列構文を使って名詞を形容詞の前に置きます、以下のように:


"Añadir ^[%1$lld %3$@ %2$@](inflect: true) a tu pedido";

When the automatic grammar engine inflects the generated string for Spanish, it pluralizes the food name, as it does in English. In Spanish, it also adjusts the adjective (foodSizeSelection.localizedName) to match the number of quantity and the grammatical gender of food.localizedName. For example, one small salad becomes “1 ensalada pequeña” in Spanish, while two small salads is “2 ensaladas pequeñas”. In both cases, the grammar engine changes the adjective “pequeño” to match the feminine gender of “ensalada”. 自動文法エンジンがその生成された文字列をスペイン語に対して屈折(語形変化)させる時、それは食べ物名を複数形にします、それが英語においてするように。スペイン語では、それはまた形容詞を調整して(foodSizeSelection.localizedName)、quantityの数そしてfood.localizedNameの文法的性に合わせます。例えば、one small salad(1つの小さなサラダ)は “1 ensalada pequeña” にスペイン語ではなります、一方で two small salads(2つの小さなサラダ)は “2 ensaladas pequeñas” です。両方の場合において、文法エンジンは形容詞 “pequeño” を変化させて “ensalada” の女性に合わせます。

In some languages, an app may need to provide part-of-speech information to the inflection engine. いくつかの言語では、アプリは品詞情報を屈折エンジンに提供する必要があるかもしれません。 This happens in English, where the words “sandwich” and “juice” are both a noun and a verb. In Spanish, the food size terms “grande” and “enorme” can be used as both adjectives and nouns. The inflection engine logs a warning when it encounters this type of ambiguity. スペイン語では、食べ物のサイズ用語 “grande” と “enorme” は、形容詞と名詞の両方として使われます。屈折エンジンは、それがこの種の多義性に出くわす時に警告を記録します。 To clarify intent, the inflection engine accepts a grammar markup that wraps the substitution with the syntax ^[…](morphology: {…}) and provides part-of-speech information. The following entry from the English strings file shows an example of this disambiguation: 英語の strings ファイルからの以下の登録項目は、この明確化の例を示します:


"Add ^[%lld %@ %@](inflect: true) to your order" = "Add ^[%lld %@ ^[%@](morphology: { partOfSpeech: \"noun\" })](inflect: true) to your order";

See Also 参照

Essentials 要点