fkm blog

software開発に関することを書いていきます

RTLサポートの強化

Android 4.4 KitKat 冬コミ原稿リレーを開催」の11/18分です.

4.4では, アプリをRTL言語対応するための機能強化が行われています.

RTL言語って何?

RTLとはRight-To-Leftの略で, アラビア語ヘブライ語などの右から左に記述する言語のことです.

AndroidにおけるRTL言語対応の歴史

本題に入る前に, AndroidにおけるRTL環境のサポートの流れを見ておきます.

  • values-arによるRTL文字の表示(1.5ぐらいからずっと?)
  • RTLレイアウトのサポート(4.2から)
  • layout-ldrtlなどによるRTL環境用リソースのサポート(4.2から)

ということで, RTL用というグループが登場したのが割と最近でした. 4.2以前では言語毎にlayout-arのようなフォルダを作り, RTL環境で自然となるようリソースを作る必要がありました.

これをふまえて4.4で追加された機能を見ていきましょう. 4.4では以下の機能が追加されています.

  • 画像の自動反転
  • 任意のLTR(?)言語でRTL環境のレイアウトを使うオプション

機能が明確なので, 原稿リレー担当の中でかなり簡単な部類で申し訳ないなーと思っていましたが, 実際サポートしてみよとするとはまりどころが割とたくさんあったので記事として意味はありそうです.

サンプルアプリを元に紹介していきます.

RTL対応前のサンプルアプリ

サンプルアプリは, リボンのような背景のTextViewを設置しているだけです. Javaのコードは1行も書いてません.

Layout XMLはこんな感じです. 親のRelativeLayoutは省略してます.

<TextView
    android:id="@+id/text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    android:gravity="center_vertical"
    android:textColor="@android:color/white"
    android:background="@drawable/ribbon"
    />

画像の反転

4.3までは, RTL用に反転した画像を用意する必要がありました. それだと単純にリソースの量が倍になってしまうので, 4.4では設定しておくと反転してくれます.

反転させるには, drawable XMLファイルを作成し, android:autoMirrored="true" を付けます.

今回はファイル名をdrawable/ribbon_rtl.xmlにしました. 元の画像ファイルが単純なpngファイルの場合は, nine-patchではなくbitmapを使ってください.

<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ribbon"
    android:autoMirrored="true" 
    >
</nine-patch>

確認するために, 敢えて言語設定をアラビア語にしてアプリを起動させてみます.

はい, だめですね アラビア語にすると左上に時計が表示されるので, スクリーンショットの貼り間違えではなさそうです.

RTLのサポート

実はRTLはAndroidManifestでサポートしてるよ!という設定を書かないと有効になりません. 4.2でRTL Layoutがサポートされた際に公式ドキュメントにこんな風に書いてあります.

To begin supporting RTL layouts in your app, set the android:supportsRtl attribute to the element in your manifest file and set it “true". Once you enable this, the system will enable various RTL APIs to display your app with RTL layouts.

訳:
アプリでRTL Layoutのサポートを開始するには, マニフェストの要素にandroid:supportsRtl属性を"true"でセットしてね. これ有効にしたら, RTL Layoutで表示するためのいろんなRTL APIをシステムは有効にするよ!

ということで, AndroidManifest.xmlに言われた通りに追記してみます.

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:supportsRtl="true"
    >
<!-- 中略 -->
</application>

実行してみましょう. アラビア語の環境で.

背景画像, ちゃんと反転しました.

でも文字は反転されていません.

RTLとUnicode

ここでせっかくなのでRTLとUnicodeについても見てみます.

Androidに限らず, RTLにちゃんと対応した文字列を表示できるモノ(TextViewや, ラベルなど)では, Unicodeで書かれた内容に従って文字列を描画します. 文字列中にRTL言語で用いられる文字が登場した時点で, "これは右寄せで, 右から左へ描画するものだ"と判断し, 右寄せで描画します.

ここで, アラビア語をほんの少しかじってみると分かるのですが, 英単語など, LTRで描画するものはLTRで描画するというルールがあったりします. さすがにAndroidをdiordnAとは描画しないようです.

なので, 先ほど実行した例は文字列中に右寄せすべき文字がでてこないので, 背景画像は反転したものの, 文字列自体は左よせになるのが正しそうです.

文字もちゃんと右寄せするには?

上記をふまえ, 文字もちゃんと右よせするには, ちゃんとアラビア語のリソースを用意する必要がありそうです.

res/values-ar/strings.xmlを作り, アラビア語を含むようにします. エディタではアラビア語がそのまま表示されているのですが, はてなダイアリーだと実体参照に変換されちゃいますね(´・_・`) この表現でもちゃんとアラビア語, 出るので大丈夫です.

<resources>
    <string name="app_name">RTLSample</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">&#1605;&#1585;&#1581;&#1576;&#1575; &#1575;&#1604;&#1593;&#1575;&#1604;&#1605;</string>
</resources>

アラビア語環境で実行してみましょう.

ちゃんと文字列も右寄せになり, 画像の反転とセットで対応できたと言えそうです.

実際のレイアウトを確認するには?

文字列を右寄せにして描画してもらうには, RTL言語の文字を入れる必要があることは上記で述べました.

ここでこんな疑問が出てきます.

文字列を右寄せにした場合のレイアウトのテストってどうやるんだ?

ダミーの文字列を使うという手が浮かびますが,

  • res/values-ar内のアラビア語ファイルは本番でも使われるのでダミーという訳にはいかない
  • res/values内でダミーの文字列を使いたいけど, これも本番ではデフォルトの英語で使われるのでダミーという訳にはいかない.

となってしまい, デザイナーが気合いでアラビア語を読みつつ確認という非現実的な解しか残りません.

単純に文字列が変わるだけなら, 端末を2台用意して, "このボタンはおそらく日本語ではこの文字列に相当するんだろうなぁ"と確認しながら作業できそうですが, ListViewでソートして表示 みたいな機能があったりするともうお手上げです.

この面倒そうな問題の解決に役立ちそうなのが, 4.4の開発者オプションに追加された"RTLレイアウト方向を使用"オプションです.

この項目にチェックを付けると, 言語はそのままに, すべてのレイアウトがRTL方向になります. アラビア語環境にしたときに気づくとは思うのですが, スクロールの方向やカレンダーの方向まですべて左右逆になります. Google Nowのカレンダーの方向も右から左になってますよね.

プリンスオブペルシャの上下が逆さまに見える薬を飲んでしまった状況に似た何かを感じます.

早速, 日本語環境でRTLレイアウト方向を使用してアプリを起動してみます.

えーと, もし文字列がRTL環境でも英単語のままであれば, 駄目なことがわかります.

幸い, Unicodeには強制的に描画方向をRTLにするという制御文字(RIGHT-TO-LEFT MARK)があるので, これを使います. ちなみにこの制御文字を除去し忘れてリリースしてしまった場合, 右から左へ文字列が描画されている星1つがたくさんついたアプリになる恐れがあるので注意が必要です.

<string name="hello_world">&#8207;Hello world!</string>

再度, 起動してみます.

'!'の位置がやや気になりますが, これでよさそうです.

最後にプログラム中で画像反転の制御

ここまでJava側を一切触らずに記事を書いてきましたが, 最後に動的にレイアウトを作りたい人向けに, プログラム中で画像反転の制御をONにする方法を紹介します.

TextViewの背景を@drawable/ribbonに戻し, RTL環境でもXML上では反転しないようにしています.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
        
    findViewById(R.id.text).getBackground().setAutoMirrored(true);
}

Drawableに対して, setAutoMirrored(true)を呼ぶとandroid:autoMirrored="true"と同様の結果が得られます. XMLファイルを大量に作成するよりは, こちらのほうが全体としてサイズを小さくできる可能性はありそうです.

まとめ

4.4でRTL環境のサポートがより容易になりました. 反転させた画像を大量に用意する必要が無くなった上, 開発者の言語でRTL環境のレイアウト確認ができるのは大きいと思います.

Wikipediaによると, アラビア語を母語にする人が約2億3500万人, ヘブライ語で約1000万人いるようです. これを機会に, アプリをこの2億5000万人ほどのRTL環境の方に使っていただけるようサポートしてみてはどうでしょうか?