プライマリ言語を日本語にしても、App Store の「言語」は英語のままだった
日本語しか入っていない iOS アプリを公開したら、App Store の商品ページの「言語」が英語になっていた。App Store Connect のプライマリ言語は日本語にしてある。設定を疑って探し回ったが、あの欄はプライマリ言語を見ていない。バイナリが宣言しているローカリゼーションから出ていて、Capacitor が生成した Xcode プロジェクトは en のままだった。
日本語しか入っていないアプリが「英語」で並んでいた
商品ページを見た印象から入ると話が曖昧になるので、先に機械が返す値で確認した。App Store の公開情報は iTunes Lookup API から bundle ID で引ける。審査を通ったあとのアプリなら、誰でも1コマンドで同じものが見られる。
$ API='https://itunes.apple.com/lookup?bundleId=com.ccya.nekokintai' $ curl -s "$API&country=jp" | jq -r '.results[0].trackName' ねこ勤怠 作業記録をつけるにゃー $ curl -s "$API&country=jp" | jq -c '.results[0].languageCodesISO2A' ["EN"] # 米国ストアフロントに聞いても、返ってくるアプリ名は日本語のまま $ curl -s "$API&country=us" | jq -r '.results[0].trackName' ねこ勤怠 作業記録をつけるにゃー
languageCodesISO2A が ["EN"] の1件だけになっている。一方で trackName は日本語で、しかも米国ストアフロントに問い合わせても日本語のまま返ってくる。プライマリ言語が日本語で、それが他ストアの既定として使われている証拠だ。
つまり、同じ商品ページの中で メタデータは日本語、言語宣言は英語 という食い違いが起きている。どちらも Apple が配っている値なので、片方の設定をもう片方の原因だと考えているかぎり出口がない。
あの欄はメタデータではなくバイナリから出る
商品ページの「言語」は、アプリバンドルが宣言しているローカリゼーションから導出される。App Store Connect のプライマリ言語が決めるのは、説明文・スクリーンショット・アプリ名といったメタデータの既定言語だけで、この欄には一切効かない。
該当するのは CFBundleDevelopmentRegion とバンドル内の .lproj だ。Capacitor が生成した iOS プロジェクトを開くと、こうなっていた。
<!-- ios/App/App/Info.plist -->
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
// ios/App/App.xcodeproj/project.pbxproj
developmentRegion = en;
knownRegions = (
en,
Base,
);バンドル内にあるのは Base.lproj だけで、en.lproj も ja.lproj も存在しない。ローカライズされた文字列を1つも持たないアプリなので、開発地域として宣言された en がそのまま「このアプリの言語」として扱われる。UI の文言は全部日本語なのに、である。
App Store Connect の画面には、その宣言が出てこない
これを公開まで持ち越したのは、確認する場所が分かれていたからだ。App Store Connect で言語に関わる設定は「プライマリ言語」とローカリゼーションの一覧しかなく、そこを日本語にすれば商品ページの言語表記も日本語になる、と読める。バイナリ側の宣言はビルド設定の中にあって、App Store Connect の画面には現れない。
審査も止まらない。宣言している言語と実際の UI の言語が食い違っていることは、リジェクト対象になっていない。だから「審査を通った」ことは、この種の食い違いが無いことの証明にならなかった。
設定した場所と、結果が出る場所が別のシステムに属していることがある。ここでは前者が App Store Connect、後者がバイナリだった。片方を触って直らないとき、まず疑うのは値ではなく「その値が本当にその表示の入力なのか」だ。
直し方と、ストアに出るまでの距離
宣言を日本語に変える。CFBundleLocalizations を明示的に足しているのは保険で、これがあれば .lproj の構成に依存せず対応言語を宣言できる。
<!-- ios/App/App/Info.plist -->
<key>CFBundleDevelopmentRegion</key>
<string>ja</string>
<key>CFBundleLocalizations</key>
<array>
<string>ja</string>
</array>
// ios/App/App.xcodeproj/project.pbxproj
developmentRegion = ja;
knownRegions = (
ja,
Base,
);差分は2ファイルで済むが、ストアの表示はこれで変わらない。あの欄は審査を通ったバイナリから導出されるので、バージョンを上げて再ビルドし、審査を通して公開されるまで英語のままだ。この記事を書いている時点でも、上の Lookup API はまだ ["EN"] を返す。
そこで、この1点だけで審査を回す案は捨てた。商品ページの検索インデックスに入るのはアプリ名・サブタイトル・キーワード欄といったメタデータで、バイナリの言語宣言が検索順位に効くという話は Apple のドキュメントにも見当たらない。実害は「日本語で使えるのか分からない」という表示上のもので、コンバージョン経由の二次的な影響にとどまる。それなら次の機能アップデートに同梱するほうが、審査1回ぶんに見合う。
ただし直した理由をコードに残しておかないと、Capacitor の再生成や次に触ったときの整理で en に戻る。Info.plist の該当キーの真上に書いた。
まとめ
クロスプラットフォームのラッパーを使うと、ネイティブプロジェクトは「生成物」として扱いたくなる。実際ほとんどの値は触らなくても動く。ただし生成された雛形の既定値のいくつかは、動作に影響しないままストアの表示になって外に出ていく。開発地域はその1つで、アプリが英語圏向けであることを前提にした値が入っている。
もう1つ。公開済みアプリの状態は、管理画面を目で見るより API から引いたほうが速い。「言語が英語になっている気がする」と「languageCodesISO2A が ["EN"] を返す」は、調査の出発点として別物だった。