見出し画像

SwiftUIアプリのローカライズ

ローカライズはアプリを各国の言語に対応させることです。
WWDC2021 ではローカライズのセッションで魅力的な発表がいくつもありましたが、利用可能な新OSの登場はまだ先です。

ローカライズは数年の経験がある開発者であれば慣れていると思いますが、いろいろ機能がふえていてドキュメントもあちこち参照しなければならずなかなかやっかいです。

この記事では iOS 14.6 と Xcode 12.5 で実行可能な範囲をまとめています。
前半は UIKit と SwiftUI で共通のローカライズ関連資料のアップデートと基本、後半に SwiftUI のサンプルの説明などを書きます。

サンプルは iOS と iPadOS 用アプリです。

先日 iOS iPadOS 用アプリ『絵文字アナライザ』をリリースしました。
SwiftUI のアプリで日本語と英語に対応させました。


この記事では「絵文字アナライザ」でも使った "\(変数名)" のローカライズや、表示幅に対応した文字列の表示、iPhone と iPad などデバイス別の文字列表示、それに英語で複数形や序数(1st 2nd 3rd 4th)対応などを解説します。


ローカライズについては『アプリの中身 リソースとローカライズ』にも書きました。

バンドルやリソース、Xcode の設定やシミュレーターの強力な機能などについてはこちらの記事を参照してください。

宣伝ですが、この有料記事を含むマガジン「iOSアプリ開発入門」が記事2本文の料金で8本の記事を読めるのでお得です😅。



1 ローカライズについての資料

iPhone や iPad それに Mac は各国(175の国や地域・40言語)で使われているので Apple もアプリのローカライズ対応促進のため資料を用意しています。

このページは日本語に翻訳済みで概要が詳しく書かれていて、必要なドキュメントへもリンクしています。

このページは英語はもちろん日本語のほか中国語と韓国語で表示でき、Appleさんがローカライズに力を入れていることがわかります。
ただし Xcode 11 かつ iOS 13 と2020年の内容です。
WWDC2021で発表された内容に対応するバージョンに更新が期待されます。
時期的には iOS 15 リリースのタイミング、あるいはWWDC2021ビデオに日本語字幕がつく頃ではないかと希望を込めて予想しています。

このドキュメントにはアプリの開発だけではなく、ストアでの公開に関係する情報もあります。
ローカリゼーションを念頭に置いてAppを構築する」からはじまっていて、アプリの企画段階から参照すべき資料です。

現在ローカライズ関連の開発用ドキュメントが英文ですがいくつか出ていて、コーディングや Xcode での操作などに関連することは Localization ページからアクセスできます。

Developer アプリでは フレームワーク >  インターなショナライゼーションとローカリゼーション に関係するビデオが揃っています。
今年のWWDCを除き日本語字幕が付いています。
(webで公開されている古いビデオなど一部には日本語字幕がない場合でも Developer アプリでは日本語字幕が表示できる場合があります)
今年のビデオも昨年同様九月頃までには日本語字幕が付くと思います。

説明文だけではわかりにくい作業もビデオでデモを見るとわかりやすくおすすめです。


1-1 アプリ名のローカライズ

ホーム画面でアプリアイコン下に表示するアプリ名については『アプリの中身 リソースとローカライズ』にも書きました。

もうひとつ App Store で表示されるアプリ名があります。

ストアで表示され、検索対象でもあるアプリ名も各国別に設定可能です。
ただしアップロードしたアプリの名前が表示されるわけではありません
App Store Connect で設定が必要です。
設定と変更が可能なのは審査前だけですので注意してください。(アップデートのタイミングで変更可能です)

App Store Connect ヘルプ App Store 情報のローカライズ

ここに書かれているがAppの名前の上限は30文字だそうです。
(そのほかにも詳しく日本語で書かれています)


2 文字列のローカライズの基本

アプリはあらかじめどの言語に対応するかを決め、各言語別の表示文字列をそれぞれ用意することで各国語に対応します。

Xcode のローカライズは Adding Support for Languages and Regions(英文)などに書かれています。
まずプロジェクトに Localizations を追加します。

ローカライズに未対応のアプリはひとつの言語だけで表示します。
通常は英語がデフォルトなので英語版アプリになります。

ローカライズ対応を何もしなくても(Localizations を追加しない状態)、ユニコードで表示可能な文字は表示できます。
デフォルト設定のアプリで、表示文字列を日本語にすることは可能です。
この場合 Edit ボタンなどシステムの機能により表示される文字列は日本語ではなく常に英語で表示されます。

英語のほか日本語にも対応したアプリでは、日本語環境で実行すると日本語表示、英語環境で実行すると英語表示になります。

日本語に対応するための Xcode の操作は『アプリの中身 リソースとローカライズ』の「3-1 英語以外の言語に対応」を参照してください。

デバイスで共通して使う言語は設定アプリで設定・変更します。
設定するとホーム画面や各アプリもその言語で表示します。
iOS 14ではアプリ別に利用する言語を選ぶことができます。(OSの設定は日本語で、指定したアプリだけドイツ語で表示するなどが可能)
設定アプリでアプリを選び「言語」を切り替えることができます。
画面は Safari の言語設定画面です。

画像2

絵文字アナライザのように英語と日本語にだけ対応している場合はこのような表示になります。(Japanese(jp) だけを Localizations に追加しています)

画像2


コード内の文字列を各国語に対応させるには、コード内ではキーを使いキーに対応した各国語の文字列の表から表示する文字列を取り出しそれを表示します。
このしくみは macOS も iOS / iPadOS も同じです。

表から文字列を取り出すにはNSLocalizedString(_:tableName:bundle:value:comment:) 関数を使います。

let localizedTitle = NSLocalizedString("Open Button", comment: "")

言語別テーブルをキーで参照(例では "Open Button")し一致する文字列を返します。

キーの文字列が "Open Button" の場合の日本語のテーブル例

"Open Button" = "開く";

英語のテーブル例

"Open Button" = "Open";

これで localizedTitle は日本語🇯🇵環境では「開く」、英語🇺🇸環境では「Open」が代入されます。

各テーブルはファイル名「Localizable.strings」のテキストファイルです。
拡張子から「ストリングファイル」と呼ぶこともあります。
ファイルをローカライズすると Xcode は自動的にファイルを言語別のフォルダーに作成(または移動)します。
このためファイル名は同じ各国語別ファイルが存在します。

Localizable.strings」ファイルの各データの書式は

// コメントの書式はソースコードと同じ
"キー文字列" = "ローカライズ文字列";

です。
行末のセミコロンは省略できません
キー文字列に改行を含むことはできませんが、ローカライズ文字列は改行に対応しています。

ローカライズ文字列はもちろんキー文字列もユニコードの文字(漢字や絵文字も)が使えます。


2-1 NSLocalizedString(_:tableName:bundle:value:comment:) の使い方

NSLocalizedString(_:tableName:bundle:value:comment:) は Swift言語では Foundation フレームワークで関数として定義されています。
C言語ではマクロとして定義されていて Objective-C でもマクロを利用していました。(このため関数版のドキュメントでは Swift のみで Objective-C はありません)

Xcode Help にまだマクロと表示している部分が残っていました。
マクロはプリプロセッサにより展開されます。
// 引数と戻り値
func NSLocalizedString(_ key: String,
 tableName: String? = nil,
 bundle: Bundle = Bundle.main,
 value: String = "",
 comment: String) -> String

key:
文字列の検索に使うキーです。
変数ではなく引用符で囲みリテラルで指定します。
空文字列は指定できません。

tableName:
デフォルトのストリングファイル(ファイル名 Localizable.strings)以外を使う場合に指定します。
複数のテーブルを使い分けないのであれば省略します。

bundle:
検索するストリングファイルを含むバンドルを指定できます。
デフォルトではこの引数を省略するとメインバンドルを利用します。
この引数はデフォルトが設定されているので通常は省略します。

value:
ローカライズ文字列のデフォルト値。
キーにマッチする文字列が見つからない場合に使われます。
省略可能で、省略すると空(文字数ゼロ)の文字列となります。

comment:
この文字列に関する任意のコメントです。
コメントなので空でもかまいません。
翻訳を依頼する場合にはここに翻訳のために必要な情報や似た字面の key を区別するためのコメントを書きます。

戻り値
key に対応するローカライズされた文字列を返します。
key がみつからず value が空の文字列の場合は key を返します。
key がみつからず value が空の文字列ではない場合は value を返します。


2-2 ローカライズの手順

いろいろな方針が考えられますが、外部に翻訳を依頼する場合にはアプリがほぼ完成してからローカライズするのが確実です。
ローカライズは翻訳だけではなく言語により複数形や序数といった考慮が必要な点が多いので、ローカライズを開始してから仕様変更が発生すると(ローカライズもれ発生など)混乱しがちです。

アプリの開発中(機能の実装をほぼ完成する前まで)はひとつの言語ですすめると良いでしょう。
通常は開発者のネイティブ言語(日本人なら日本語)かアプリデフォルトの英語にすることが多いでしょう。
私も日本語でまず開発し英語を追加しました。(キー文字列は日本語で問題ありません)

✳️ローカライズ手順は個人開発と大規模開発では変わります
✳️外部に翻訳を依頼する場合はXcodeのエクスポート機能が便利です

ローカライズのために Xcode にはローカライズが必要な情報を抽出してエクスポートする機能があります。
エクスポートした情報を翻訳者に渡して翻訳してもらい、翻訳結果をインポートします。

なおエクスポートのメニューは Xcode 12.5 では Product メニューの Export Localizations... です。
Xcode Help や Exporting Localizations では Editor > Export for Localization と書かれていますが、Xcode 12.5 では変わっています。

エクスポートで言語別に保存される拡張子 .xcloc ファイルは Finder のコンテキストメニュー「パッケージの内容を表示」で確認できます。
(Mac App Store を「xcloc」で検索するといくつかツールらしいアプリがみつかりますが確認していません)

スクリーンショット 2021-06-22 16.19.35

Source Contents フォルダーを辿っていくと言語別フォルダーに Localizable.stringsdict・Localizable.strings・InfoPlist.strings があります。
通常の手順ではそれぞれを翻訳した文字列で修正しインポートします。

個人開発の場合はエクスポート・インポートは使わなくても各国語に対応は可能です。


2-3 Info.plist 文字列のローカライズ

デバイス内の写真へのアクセスやカメラやマイクロフォンを利用する場合には利用者の許可が必要です。
これについては日本語のドキュメント「保護されたリソースへのアクセスを要求する」に説明があります。

このドキュメントでは位置情報を例に Info.plist にNSLocationWhenInUseUsageDescription をキーに表示する文字列を設定しています。
NSLocationWhenInUseUsageDescription は位置情報の利用許可ですが、画像を保存するなら NSPhotoLibraryAddUsageDescription 、広告などでトラッキングする場合には NSUserTrackingUsageDescription などが必要です。

この許可を求める文字列もローカライズが必要です。
Info.plist にローカライズ用のキーを設定してもキー文字列を表示するだけでローカライズされた文字列を表示しません

ローカライズするには InfoPlist.strings ファイルを使います。(「Localizable.strings」とは別です、アプリ名のローカライズに使うファイルです)
このファイルに

// 日本語にローカライズした例
NSPhotoLibraryAddUsageDescription = "選択した画像を保存します";
NSUserTrackingUsageDescription = "お客様のデータは、お客様にパーソナライズされた広告を配信するために使用されます。";
// アプリ名のデータは割愛

などと設定します(日本語用のファイル例です)。
なお NSPhotoLibraryAddUsageDescription などの予約語に引用符は不要です。


2-4 ローカライズ不要な工夫

絵文字やアイコンを使うことで文字列を不要にすれば、文字列の翻訳も不要になります。
文字列が不要になればローカライズしていない国や地域でも利用されることが期待されます。
ただし、絵文字やSF Symbols の利用には一部制約(アプリアイコンに利用できないなど)があります

SF Symbols にはローカライズされているものもあります。
SF Symbols アプリの Info Sidebar で確認できます。

スクリーンショット 2021-06-16 16.06.58

たとえば "textformat.size" は使用した日本語対応アプリで言語を日本語にすると「ぁあ」と表示されます。


2-5 ローカライズもれ対策

ローカライズは各言語に切り替えてアプリを動かし表示が正しいことを確認しなければなりません。

「Localizable.strings」の設定を忘れている項目はキー文字列をそのまま表示します。

注意が必要なのはリリース直前などに表示文言を変更する場合に、「Localizable.strings」の文言ではなく、コードのキー文字列を変更してしまうことです。

キー文字列を変更してしまうとデフォルト言語での実行では変更できたたように見えますが、言語を切り替えるとキー文字列が表示され結果的にローカライズもれになります。

これを防ぐため私はキー文字列の先頭に目立つ絵文字「❇️」を追加しました。

キーの文字列を "❇️Open Button" とするのです、日本語のテーブル例

"❇️Open Button" = "開く";

英語のテーブル例

"❇️Open Button" = "Open";

このようにするとコードのキー文字列を変更するとテーブルには一致する文字列がなくなり、画面にはキーを表示するので❇️の部分が目立ちローカライズもれだとすぐにわかります。


3 フォーマット処理

文字列の中に変数の値や計算結果を含む場合のフォーマット処理です。
ここでは DateFormatter 型を使う年月日の表示フォーマットなどとは別で、変数の内容を文字で表示することです。

フォーマット処理は SwiftUI でも UIKit でも必要になり、ローカライズするしないにかかわらず必要になります。
Swift言語では文字リテラルの中で \(変数名) を利用できるので、フォーマット処理は意識せずに済む場合がほとんどになりました(Swift言語の機能の String Interpolation 〔英文〕)。
しかし変数値表示を含む文字列のローカライズは当然発生します。

フォーマット処理は localizedStringWithFormat(_:_:) 関数を使う方法と String 型の init(format:_:) や init(format:locale:_:) などを使う方法があります。
どちらもフォーマット処理後の String型文字列が得られます。

ただし引数はローカライズ用のテーブルを引くためのキー文字列ではなく、フォーマット処理用の文字列です。


3-1 localizedStringWithFormat(_:_:)

localizedStringWithFormat(_:_:) は String型 Type Method と NSString型 Type Method の二つがあります。
利用する場合には String. または NSString.明示しなければエラーになります。


3-2 フォーマットの書式

古いドキュメントですが Resource Programming Guide の String Resources が詳しいです。
(古いためか iPadOS の Safari ではタイトル部分が見えません)
ローカライズ文字列の仕様は変わっていませんが、コードは Objective-C です。

フォーマット指定の書式は

に一覧があります(英文です)。
表示する変数の型により書式がかわります

Objective-C ではフォーマットは頻繁に使っていました。
フォーマットに不慣れな場合は 
String.localizedStringWithFormat("num=%lld", num)
などと localizedStringWithFormat(_:_:) で動作を確認し慣れてください。

ひとつの文字列に複数の変数の値や計算結果を含む場合は、言語により値の表示順の変更が必要な場合があります。
その場合は Resource Programming Guide の Formatting String Resources にあるように 

"%@ Error! %@ failed!" = "%2$@ blah blah, %1$@ blah!";

などと設定することで対応可能です。
「%2$@」はキーの二つ目の %@ に対応します。


4 SwiftUIのローカライズ

前置きがずいぶん長くなりましたが SwiftUIのローカライズについては「Preparing Views for Localization(英文)」書かれています。

Text など SwiftUI のビューのイニシャライザーには引数にローカライズキー文字列を渡せるものがあります。
その引数の型は LocalizedStringKey型 です(しかもフォーマット処理にも対応しています)。

たとえば Text のイニシャライザー init(_:tableName:bundle:comment:) は

init(_ key: LocalizedStringKey, 
tableName: String? = nil, 
bundle: Bundle? = nil, 
comment: StaticString? = nil)

最初の引数がローカライズキーです。
それ以外の引数は省略可能です。
NSLocalizedString(_:tableName:bundle:value:comment:) とvalue以外の引数は同じです。

このイニシャライザーはローカライズしない場合にも使えます。
ローカライズのテーブルにキー文字列がみつからない場合はキー文字列そのものを表示します。


4-1 LocalizedStringKey型

LocalizedStringKey型は SwiftUI のローカライズに不可欠です。
LocalizedStringKey型引数にはローカライズ用のキーを文字リテラルで渡します。

LocalizedStringKey型をコード内でイニシャライズして利用することはできますが、文字列として(String型)取り出すためのプロパティがありません。
このため NSLocalizedString(_:tableName:bundle:value:comment:) 関数の代わりにローカライズ後の文字列を取り出すことに使うことはできません。


4-2 \(変数名) を含む場合のローカライズ

文字リテラル中に \(変数名) がある場合はローカライズ用キーはどのように指定するべきでしょうか?

Text("Copying \(copyOperation.numFiles) files")

の場合のローカライズキー文字列は "Copying \(copyOperation.numFiles) files" ではありません
キー文字列はフォーマット書式の "Copying %lld files" と設定します。
日本語用のテーブルなら

"Copying %lld files" = "%lld ファイルコピー中";

のように使います。

私ははじめて Swift を使ったアプリを多言語対応しようとした時  \(変数名) を含む部分をローカライズする方法がわかりませんでした。
エクスポートするとフォーマット書式に置き換えられていて驚きました。
この記事のために調べ直し Preparing Views for Localization に書かれていることを知りました。


4-3 サンプル

SwiftUI アプリのローカライズで最強のサンプルを見つけました。

Apple の WWDC2020 10160: Formatters: Make Data Human-Friendly
Displaying Human-Friendly Content は SwiftUI で Formatter を使うサンプルです。

2020年のビデオなので日本語字幕が付いています。

このサンプルを Xcode 12.5 で開くと Update to recommended settings のワーニングが出ています。

ここから先は

7,711字 / 30画像 / 1ファイル
この記事のみ ¥ 500

今後も記事を増やすつもりです。 サポートしていただけると大変はげみになります。