iOS と Androidで共通のローカライズファイルを作ろう! (iOS Advent Calendar 2016)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

メリークリスマス!

本ブログは Qiita で開催されているiOS Advent Calendar 2016の20日目の記事です。
前日はrb-de0さんでTinyConsoleの使い方でした。

provisioning profileが入れ替わる話を今年流行った映画に絡めてやろうとしたら、このネタの解決策をtarappoさんがipaファイルの中身を調べるというエントリで丁寧に書かれて若干凹んでます。。。

こういうネタは早いうちにやらないとダメだと痛感しました。

ローカライズ

モバイルアプリも他言語対応した時のために、各言語の辞書のようなものが用意されています。
Androidだとstrings.xml、iOSだとLocalizable.stringが該当します。

モバイルアプリを作っていると、クライアントとデザイナーとアプリ開発者の間でアプリ内の文言の確認が行われます。

開発が進んでくると、文言修正、誤字修正が五月雨に行われてきます。
前述した様にAndroid、iOSと辞書ファイルが分離している関係上、ごく稀に修正漏れが発生するケースが出てきます。AndroidとiOSが平行開発される場合、特に発生しやすくなるでしょう。

共通のファイルを使う

クライアントの画面のやり取りが画像等(pngファイル等)で行なわれていると仮定します。

この場合、修正依頼が入ると「開発者の修正漏れ」「Sketchなどのデザインファイルへの反映漏れ」「開発者への伝達漏れ」等の様々な修正漏れのリスクが生じます。

こういったケースを極力除外するためにも、プロジェクト開始時に「共通の辞書ファイル」を作成していた方良いでしょう。

Google Spread Sheet

「共通の辞書ファイル」としてオススメしたいのが、 Google Spread Sheetです。

クライアントのセキュリティポリシーによって利用できないケースもありますが、その場合はExcelを利用すると良いでしょう。
「Google Spread Sheet」にはExcelからインポート機能がありますので、これを利用してインポートしましょう。

データセットとスプレッドシートを読み込む

スクリプトエディタ

Google Spread Sheetにはスクリプトを作成することができます。(Excelのマクロの様なものです)

この機能を利用して、「共通の辞書ファイル」からAndroidだとstrings.xml、iOSだとLocalizable.stringの雛形になる文字列を生成する様にします。

共通フォーマット

「共通の辞書ファイル」のフォーマットは仮に「画面名」「ID」「日本語」「英語」「ドイツ語」としておきます。
スクリーンショット 2016-12-20 8.04.49

スクリプトの作成

スクリプト自体はスクリプトエディタで作成することができます。
メニューのツール > スクリプトエディタでスクリプトエディタを起動します。

スクリーンショット 2016-12-20 8.01.24
シートに対する操作関数はこちらを参考にしてください。

function outputLocalized() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet()

  var localizeSheet = sheet.getSheetByName("localize")

  var data = localizeSheet.getDataRange().getValues()

  // sheetの初期化
  for (var i = 2; i < data[0].length; i++) {
    var name = data[0][i]

    var xmlSheet = sheet.getSheetByName(getStringXMLSheet(name))

    if (xmlSheet == null) {
      sheet.insertSheet(getStringXMLSheet(name))
    } else {
      xmlSheet.clear()
    }

    var stringsSheet = sheet.getSheetByName(getLocalizedStringsSheet(name))

    if (stringsSheet == null) {
      sheet.insertSheet(getLocalizedStringsSheet(name))
    } else {
      stringsSheet.clear()
    }
  }

  // ヘッダである1行目は無視
  for (var i = 1; i < data.length; i++) {
    // 1列目は無視する
    var id = data[i][1]

    for (var row = 2; row < data[0].length; row++) {
      var name = data[0][row]
      var description = data[i][row]

      writeStringXML(sheet.getSheetByName(getStringXMLSheet(name)), id, description)
      writeLocalizedStrings(sheet.getSheetByName(getLocalizedStringsSheet(name)), id, description)
    }
  }
}

function getStringXMLSheet(name) {
  return "strings.xml (" + name + ")"
}

function getLocalizedStringsSheet(name) {
  return "Localizable.strings (" + name + ")"
}

function writeStringXML(sheet, id, description) {
  sheet.appendRow(['<string name="' + id + '">' + description + '</string>'])
}

function writeLocalizedStrings(sheet, id, description) {
  sheet.appendRow([id + ' = "' + description + '";'])
}


機能としては、「localize」という名前のシートから、対応言語の数に応じて「strings.xml」と「Localizable.strings」シートを生成する様にしています。

スクリプトの埋め込み

先ほど作成したoutputLocalized関数を
「localize」シートから呼べる様にします。
メニューの挿入図形描画で適当なテキストを作成しておきます。

スクリーンショット 2016-12-20 8.13.51
貼り付けたテキストの右上のボタンをクリックし、メニューのスクリプトを割り当て...をクリックします。

スクリーンショット 2016-12-20 8.15.17
出てきたダイアログにoutputLocalized関数を指定すると、テキストにスクリプトが割り当てられます。
スクリーンショット 2016-12-20 8.16.51

スクリプトの実行

スクリプトを割り当てられたテキストをクリックするとスクリプトが実行されます。

スクリーンショット 2016-12-20 8.18.03
以下の様に対応するシートに「strings.xml」と「Localizable.strings」が作成されます。

こちらがstrings.xml
スクリーンショット 2016-12-20 8.19.14

そしてこちらがLocalizable.stringsです
スクリーンショット 2016-12-20 8.19.32

ここから「strings.xml」と「Localizable.strings」に転記するという手間はありますが、コピー&ペーストで入れ替えできる分、修正ミスが起こることは減ると思います。

ゆくゆくはcsvに書き出して、shellscriptで「strings.xml」と「Localizable.strings」を生成するようにしようと思います。

まとめ

どちらかが先行開発されている時はこちらのサービスを使って別途生成する等の手段も取れるでしょう。

Google Spread SheetやExcelの利点はクライアントとデザイナーへの確認がやりやすいの一言に尽きます。
修正漏れ等のミスを防ぐためにも、人が介在する操作は極力減らしていきましょう!

明日21日のiOS Advent Calendar 2016の担当は minami1389227さんです。お楽しみに!