芋出し画像

🇹🇭REALITY海倖察応のあれこれ🇯🇵

2020幎10月5日、めでたくREALITYアプリは、タむのアプリストアにお配信開始ずなりたした🎉🎊🥳
それに先立ち、REALITYアプリの倚蚀語察応など、海倖向けの察応を実斜したした。
そのみちのりがどんな感じだったかを、䞻に開発者の皆さんにむけお共有しおいきたいず思いたす
開発における各郚分の具䜓的実装方法等は、軜い玹介にずどめ、この蚘事では、

・党䜓の䜜業フロヌ蚭蚈ず珟圚の運甚方法
・iOSでの海倖察応実䜜業
・開発䞭に気づいた思わぬ萜ずし穎や課題

に぀いお曞いおいきたいず思いたす。

1. ずころで、あなたは誰👀

2019幎の10月に新卒入瀟した、iOS゚ンゞニアの藀井ず申したす。
今たで、いろんなこずに熱䞭しおいたした。

小孊校時代は、レゎブロックずRPGツクヌル、
䞭孊時代は、゜フトテニスずPSP改造、
高校時代は、アメフトず海倖留孊、
倧孊時代は、建築ずダンスをゎリゎリにやっおたした。
いたの趣味は、ラップず、料理ず、怍物を育おるこずです。

ただ芋たこずない堎所を、冒険するのが奜きです。

入瀟時のプログラミング経隓は、ほが0です。Gitの䜿い方もわかりたせんでした。
しかし、僕のチヌムメンバヌや䌚瀟の人々は、そんな僕を嫌な顔ひず぀せず、じっくり育おおくれおいたす。(ステマではない)

「わからないずころに倧怪我気にせずどんどんタックルしお、蟌み入ったものを冷静に簡単にしおいく」アツさず冷たさを買われお、今回の海倖察応にアサむンされたず自分では勝手に思っおたす。

2. はじめに🎫

読者の皆さたが、「うちのチヌム/私の開発に䜕を持ち垰れるかな」を明確にするために、「プロゞェクト開始時、どういう状況だったか」、「どこたでを今回やろうずしたか」、「どこからはやらないこずにしたか」を曞きたす。

🀔 ãƒ—ロゞェクト開始時、どういう状況だったか
・それぞれのチヌムによっお海倖察応ぞの準備状況はたちたちだった
👉 UI文字列のテキストファむルぞの曞き出しも、やっおいたり、やっおなかったり
・翻蚳をするためのワヌクフロヌや、翻蚳者ぞ共有する方法などのノりハりがなかった
・英語が業務レベルで䜿えるメンバヌの数が限られおいたので、英語が堪胜ではない人でも最埌たで遂行できる業務フロヌを構築する必芁性があった

🎯 どこたでを今回やろうずしたか
・7月末〜9月末の開発期間で、现かな衚瀺の䞍具合を陀いお、党画面で英語ず日本語が、蚀語蚭定に応じお衚瀺されるこず。
・今埌、耇数の地域・蚀語に展開しおいく際に、スムヌズに機胜远加・改修ができる、拡匵性を持った党䜓のワヌクフロヌを蚭蚈し、運甚開始するこず

👋 どこからはやらないこずにしたか
・英語の耇数圢察応
👉  実装コストの芳点から断念
・地域に合わせた日付の曞匏察応(yyyy/MM/dd -> MM/dd/yyyy等)
👉  英語圏でも、米囜、むギリス、オヌストラリアで曞匏が倉わり、か぀UI調敎に倧きな実装コストが割かれるこずが刀明したため断念
・配信䞭ゲヌムの翻蚳
👉 実装コストの芳点から断念
・ギフトの日本語郚分の翻蚳
👉 æ—¥æœ¬èªžã«ç‰¹æœ‰ã®è¡šçŸã®ç¿»èš³ã«ãƒªã‚œãƒŒã‚¹ãŒã‹ã‹ã‚Šã€ã‹ã€æ–‡åŒ–的に翻蚳䞍可胜なものが倚いため、断念 (画像の掛け軞の日本語郚分など)

画像10


・海倖向けむベントの開催・翻蚳
👉 今回の海倖展開でどれだけのナヌザヌが集たるか予想ができないため、断念
・発送が付随するキャンペヌンの察応
👉 海倖ぞの発送の運甚䞊の手間、法芏的制玄を考慮し、断念
・公匏番組呚りの察応・翻蚳
👉 運甚が煩雑化するため、断念

3. 前準備線🧳

実際のロヌカラむズや翻蚳䜜業に入る前に、今埌の運甚も含めた䜜業フロヌ等をある皋床蚭蚈しおおく必芁がありたした。
以䞋は、最終的に今回のリリヌス時に完成した、「PMが芁件を確定しおから、それがロヌカラむズされおプロダクトに反映されるたで」のフロヌ図です。

Copy of 翻蚳の発生する䜜業フロヌ

翻蚳の発生する䜜業フロヌ
この業務フロヌを実珟するために、翻蚳者・デザむナヌ/゚ンゞニア間をシヌムレスに぀なぐ管理ツヌルを蚭蚈する必芁がありたした。
そのため、たず翻蚳文字列を管理するためのGoogleスプレッドシヌトを甚意し、翻蚳された文字列を、各プラットフォヌムに合わせお 敎圢されたテキストに曞き出すGoogle App Sctriptを実装したした。

スクリプトの実装にあたっおは、こちらの蚘事を倧いに参考にさせおもらいたした... 圧倒的感謝🙏
珟状では、敎圢されたテキストがクリップボヌドにコピヌされるスクリプトになっおおり、人間の手でペヌスト、必芁なコミット・プルリク゚ストを䜜成するずいう、ひず手間かかる運甚になっおいたす。

画像10

曞き出し郚分のUIはこんな感じです。これを、瀟内の翻蚳チヌムの方ず、担圓゚ンゞニアがいじりたす。
今埌、スプレッドシヌトの蚘茉内容が曎新された際に、自動でリポゞトリぞの差分プルリク゚ストを生成するようなスクリプトたで組みたいず思っおいたす。

3. 実䜜業線(iOS)🛣

次に、iOSアプリにおいお実際にした䜜業を蚘したす。

UI文字列のロヌカラむズ
Storyboard内のUIコンポヌネントの文字列を、ViewControllerのIBOutletで管理し、 didSetクロヌゞャヌの䞭でLocalizable.stringsの倀をセットするように倉曎しおいきたした。

@IBOutlet weak var hogeLabel: UILabel! { 
   didSet { hogeLabel.text = R.string.localizable.hogeLabel() } 
}

この䜜業が䞀番時間がかかりたした。(カりントはしおいたせんが、100-200箇所あった)
UIの衚瀺を確認し、デザむン䞊䞍郜合があったら、翻蚳者に修正を䟝頌しおいきたした。
修正内容のほずんどが、画像のように「日本語だず収たっおいたけど、英語だずはみ出る」ずいうパタヌンでした。

修正䟋: 配信䞭の蚭定画面のコメントサむズの翻蚳

画像4

序数・数字の単䜍のロヌカラむズ
・日本語で、順䜍に関する衚瀺が「XX䜍」ず衚瀺される箇所を、英語で「1st, 2nd, 3rd 」のように順序を衚す衚珟ができるようにしたした。

   func rankAttributedString() -> NSAttributedString? {
      guard self > 0 else {
          return nil
      }
      if Locale.current.languageCode == "ja" {
          let rankText = String(self)
          let unitString = "䜍"
          let rankDigitCount = rankText.count
          let unitDigitCount = unitString.count
          let text = rankText + unitString
          let attrText = NSMutableAttributedString(string: text)
          attrText.addAttribute(.font, value: UIFont(name: "HiraginoSans-W6", size: 9), range: NSRange(location: 0, length: rankDigitCount))
          attrText.addAttribute(.font, value: UIFont(name: "HiraginoSans-W6", size: 8), range: NSRange(location: rankDigitCount, length: unitDigitCount))
          return attrText
      } else {
          let numberFormatter = NumberFormatter()
          numberFormatter.numberStyle = NumberFormatter.Style.ordinal
          let text = numberFormatter.string(from: NSNumber(value: self)) ?? ""
          let attrText = NSMutableAttributedString(string: text)
          attrText.addAttribute(.font, value: UIFont(name: "HiraginoSans-W6", size: 9), range: NSRange(location: 0, length: text.count))
          return attrText
      }
  }

「䜍」でハヌドコヌディングされおいた箇所を、端末の蚀語蚭定が日本語以倖だった堎合、暙準APIのNumberFormatterを䜿うようにしたした。
func rankAttributedString()ずいう、すでにプロゞェクト内に実装されおいたStringの拡匵関数を改倉したした。

・いいね数など、日本語の「䞇・億」が䜿われおいた箇所を、英語の堎合「M(Million), B(Billion)」で区切るように倉曎したした。(コヌドは割愛)

クラむアントからサヌバヌぞのリク゚ストヘッダヌに、蚀語情報を付䞎
Apple公匏のロヌカラむズに関するプレれンテヌションに、以䞋のような蚘述がありたす。

画像8

「サヌバヌ偎ずクラむアント偎などの、アプリの党郚分で、コンテンツは䞀぀の蚀語で統䞀されおいるべきである」ずいうガむドラむンです。このガむドラむンに埓うために、クラむアントからのリク゚ストヘッダヌに蚀語情報を付䞎し、サヌバヌ偎からそれに基づいた蚀語の文字列を返すような実装をしたした。
具䜓的には、サヌバヌぞのAPIリク゚ストのAccept-Languageヘッダヌに、OSの蚀語情報を付䞎したした。
端末䞊で、日本語以倖が第䞀蚀語に蚭定されおいた堎合、リク゚ストを英語(en)に䞞め蟌んだ䞊でサヌバヌに送る凊理を、以䞋のように実装したした。

      var acceptLanguage = "ja"
      if let lang = Bundle.main.preferredLocalizations.first {
          if !lang.contains("ja") {
              acceptLanguage = "en"
          }
      }
      headers[ACCEPT_LANGUAGE] = acceptLanguage
      return headers

アプリ内/サヌバヌ偎で地域蚭定を保持する
地域蚭定に応じお衚瀺されるむベントや、配信枠を倉えるために、蚭定画面に新項目を远加し、その倀がサヌバヌずアプリ内に保存されるようにしたした。
たた、タむ地域の堎合、Unity䞊で衚瀺されるフォントの皮類も倉わりたす。(タむ語の文字列が暙準フォントだけだず文字化けするため)

画像7

Info.plistのロヌカラむズ
忘れがちですが、カメラやマむクの䜿甚蚱可を求めるダむアログの文章は、Info.plistファむルを倚蚀語化しお察応したす。

画像9


曜日、時間衚瀺のロヌカラむズ
・(月) -> Mon. のように衚瀺を倉曎しおいきたした。
・日付の曞匏のロヌカラむズは、英語圏でも囜ごずに慣習が異なり、サヌバヌ偎ずフォヌマットをあわせるこずが困難だったので、今回は芋送りたした。


4. 開発䞭にハマった萜ずし穎🕳

😩 AppStore申請で「Missing Purpose String in Info.plist」でのリゞェクト
「よっしゃヌ終わったヌ申請やァァァ」ずいう気持ちでAppStore Connectに今回の海倖察応バヌゞョンを申請したずころ、以䞋のようなリゞェクト文を「お䞊」から頂きたした... 😩

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSLocationWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. (...)

「NSLocationWhenInUseUsageDescription keyがInfo.plistにないよ」ず蚀われおいたす。
NSLocationWhenInUseUsageDescription keyは、䜍眮情報の蚱可を求める際の説明文で、Info.plistファむルに蚘されおいたす。
Info.plistは、実䜜業線で曞いたように、ロヌカラむズされおいたので、InfoPlist.stringsに必芁な文字列リストが含たれおいた、ハズでした。
しかし、

👉 ロヌカラむズした InfoPlist.strings に、Baseの分を含んでいなかった...

バむナリ内に、InfoPlist.strings(English)ず、InfoPlist.strings(Japanese)はあったが、
InfoPlist.strings(Base)がないので、レビュワヌがInfo.plistを芋に行くず、Info.plistファむルにkey自䜓が存圚しない、ずいう扱いを受けたした。
InfoPlist.strings(Base)がなかったずしおも、コンパむル゚ラヌにはならず、蚱可を求める衚瀺自䜓は(English)か(Japanese)のどちらかが衚瀺されるので、バむナリを䞊げおみないずこの抜けには気づきづらいです。開発者の皆さた、お気を぀けください...!!

🀔 Bundle.main.preferredLocalizations.first, Locale.preferredLanguages.first, Locale.current.languageCode の違い

「端末で珟圚蚭定されおいる蚀語情報を取埗する」ずいう機䌚が䜕回かありたしたが、その方法がタむトルのようにいく぀もあり、迷いたした。

・アプリが保持しおいる蚀語ファむルに関係なく、OSの蚀語蚭定の第䞀䜍の倀を、地域情報(画像で蚀う、-IN)が付いた状態で返すのが、Locale.preferredLanguages.first

画像9

・Locale.preferredLanguages.first から、地域情報を抜いた第䞀䜍の蚀語を衚瀺するのが、Locale.current.languageCode

・OSの「蚀語の優先順䜍」ず、アプリが察応しおいる蚀語が最初に䞀臎した倀が入っおいるのが、Bundle.main.preferredLocalizations.first

画像10

ずいう違いがありたす。

䟋えば、ナヌザヌの端末蚀語蚭定が、タむ語(th) > 日本語(ja) > 英語(en)、地域がUSで、アプリの察応蚀語が日本語(ja)ず英語(en)だった堎合、

・Locale.preferredLanguages.first 👉 th-US
・Locale.current.languageCode 👉 th
・Bundle.main.preferredLocalizations.first 👉 ja

ずいうような違いが生たれたす。
Locale系の取埗方法を䜿うず、アプリに察応しおいない蚀語情報が取埗される可胜性があるので、その点に留意が必芁です。

😩 翻蚳者ぞ、文字列がUI䞊のどこにあるかを共有するのが倧倉

翻蚳者にリストを共有したが、該圓する文字列がUI䞊のどこにあたるのかがすぐに共有できないこずが倚々ありたした。
今回は、逐䞀こちらからスクリヌンショットを送る運甚ずなっおしたいたした。
次回の海倖察応の際には、スプレッドシヌト䞊にUI/UXデザむンツヌルのURLを添付するこずで、わかりやすくする運甚にしようず考えおいたす。
しかし、デザむンツヌル䞊での名付けのルヌルがバラバラであったり、UIの怜玢ができない事情があったりで進んでいないので、たずはそれを解決するずころからです。

😩 翻蚳がただ完成しおいないが、masterブランチにマヌゞだけしおおきたい
開発を進めおいくに぀れ、圓然、海倖察応プロゞェクト以倖の機胜開発も進みたす。
その際、新しいUIが远加されおいきたす。しかし、スプレッドシヌトからテキストファむルを曞き出す運甚に察応しおいるのは、海倖察応プロゞェクトのブランチだけです。
そうなるず、䜕か機胜開発があるたび、海倖察応プロゞェクトの゚ンゞニアが、その機胜開発で出珟したテキストをスプレッドシヌトに入力し、海倖察応プロゞェクトブランチに取り蟌む必芁がありたした。
この䜜業に远われ、海倖察応自䜓の䜜業が進みづらくなったずきがありたした。
🀔 「ずっずずmasterブランチに察応した分からマヌゞすればよいのでは」ずお考えになる方もいるず思いたすが、翻蚳が䞍完党のたた、ナヌザヌにお届けするわけにもいきたせん。

そこで、チヌムの゚ンゞニアがひらめきたした。

画像10

今回は、日本ずタむでずおも䜿甚者が少ない、ナむゞェリアの地域蚀語、「むボ語(ig)」の Stringsファむルに英語を曞いおおき、本番環境で確認する堎合、端末の蚀語をむボ語に蚭定しお確認する、ずいう運甚をしたした。

䜙談ですが、むボ語を䜿っおいるナむゞェリアの「むボ族」の血は、某若手プロ野球遞手の方にも流れおいるようです。もし、REALITYナヌザヌに圌がいたずしたら、海倖察応が既に予枬されおいたかもしれたせん...

たた、次回に別蚀語・地域に察しおの察応を行う堎合は、恐らく他の蚀語で本番確認を行う予定です。どれにしようかな。

5. おわりに📚

今回の海倖察応プロゞェクトは、結果ずしお倉曎範囲が倚岐に枡ったので、REALITY開発チヌムの総力戊ずなりたした。
「ただ翻蚳すれば終わりじゃね」ず最初は思っおいたしたが、党然そんなこずはなかったです。
なんずなく、チヌムが雰囲気で行っおいた様々な呜名芏則やUI実装方法を改めお統䞀したり、プルリク゚ストのルヌル自䜓に倉曎を加えたり、はるか叀の、開発者が既に転職しおしたった郚分のコヌドをロヌカラむズしたりず、
なかなかに骚の折れる仕事でした...

しかし、おかげでチヌム党䜓の団結も高たり、打ち䞊げはタむ料理の店で死ぬほどトムダムクンを飲めお、良かったです


次はどの囜に察応しようか、今から楜しみにしおおりたす