アプリには、特定の文化に固有のリソースが含まれていることがあります。たとえば、現在のロケールの言語に翻訳された、文化固有の文字列がアプリに含まれていることがあります。
文化固有のリソースは、それ以外の部分と切り離すことをおすすめします。Android は、システムのロケール設定に基づいて、言語固有のリソースや文化固有のリソースを解決します。さまざまなロケールをサポートするには、Android プロジェクトでリソース ディレクトリを使用します。
アプリユーザーの文化に合わせてカスタマイズしたリソースを指定できます。また、ユーザーの言語や文化に適した各種のリソースタイプを提供できます。たとえば下のスクリーンショットの場合、アプリは文字列リソースとドローアブル リソースを、デバイスのデフォルト ロケール(en_US
)とスペイン語ロケール(es_ES
)で表示しています。
Android SDK Tools を使用してプロジェクトを作成すると、プロジェクトの最上位に res/
ディレクトリが生成されます。この res/
ディレクトリには、さまざまなリソースタイプ用のサブディレクトリがあります。また、res/values/strings.xml
ファイルなど、文字列値を保持するデフォルト ファイルもいくつかあります。
さまざまな言語をサポートする場合、ロケール別のリソースを使用すること以外にも対応すべき点があります。たとえば、アラビア語やヘブライ語のように RTL(右から左)記述法を使用する言語を UI ロケールとして選択するユーザーもいます。英語などの LTR 記述法を使用する言語を UI ロケールとして設定している他のユーザーが、RTL 記述法を使用する言語のコンテンツを表示したり生成したりすることもあります。両方のユーザーに対応するには、アプリで次の処理を行う必要があります。
- RTL ロケールに RTL UI レイアウトを採用する。
- 書式設定されたメッセージに表示されるテキストデータの向きを検出して、宣言する。通常は、このガイドで説明するように、テキストデータの向きを判断するメソッドを呼び出します。
ロケール ディレクトリとリソース ファイルを作成する
ロケールのサポートを追加するには、res/
内に追加ディレクトリを作成します。各ディレクトリ名が次の形式に従っている必要があります。
<resource type>-b+<language code>[+<country code>]
たとえば、values-b+es/
は、言語コードが es
のロケールを対象とする文字列リソースを格納します。同様に、mipmap-b+es+ES/
は、言語コードが es
で国コードが ES
のロケールを対象とするアイコンを格納します。
Android は、実行時のデバイスのロケール設定に従って、適切なリソースを読み込みます。詳細については、代替リソースを提供するをご覧ください。
サポートするロケールを決めたら、リソースのサブディレクトリとファイルを作成します。次に例を示します。
MyProject/ res/ values/ strings.xml values-b+es/ strings.xml mipmap/ country_flag.png mipmap-b+es+ES/ country_flag.png
リソース ファイルにローカライズされたリソースを入力します。ローカライズされた文字列と画像のリソース ファイルの例を次に示します。
/values/strings.xml
の英語文字列(デフォルト ロケール):
<resources> <string name="hello_world">Hello World!</string> </resources>
/values-b+es/strings.xml
のスペイン語文字列(es
ロケール):
<resources> <string name="hello_world">¡Hola Mundo!</string> </resources>
/mipmap/country_flag.png
の米国国旗アイコン(デフォルト ロケール):
/mipmap-b+es+ES/country_flag.png
のスペイン国旗アイコン(es_ES
ロケール):
注: どのリソースタイプに対しても、ロケール修飾子などの構成修飾子を使用できます(ビットマップ ドローアブルのローカライズ版を提供する場合など)。詳細については、アプリをローカライズするをご覧ください。
アプリでリソースを使用する
ソースコードや各種 XML ファイル内で各リソースの name
属性(R.<resource type>.<resource name>
)を使用すると、リソースを参照できます。このように、リソースはさまざまな方法で参照できます。次に例を示します。
Kotlin
// Get a string resource val hello = resources.getString(R.string.hello_world) // Or supply a string resource to a method that requires a string TextView(this).apply { setText(R.string.hello_world) }
Java
// Get a string resource String hello = getResources().getString(R.string.hello_world); // Or supply a string resource to a method that requires a string TextView textView = new TextView(this); textView.setText(R.string.hello_world);
XML ファイルでは、XML 属性が互換値を受け入れる限り、@<resource type>/<resource name>
構文を使用してリソースを参照できます。次に例を示します。
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/country_flag" />
注: ユーザーの言語設定の優先度が正しくなるようにするには、resConfigs
プロパティを使用して、アプリがサポートする言語を指定します。詳細については、アプリがサポートする言語を指定するをご覧ください。
メッセージのテキストを書式設定する
アプリでよく行われるタスクとして、テキストの書式設定があります。ローカライズ メッセージを書式設定するには、テキストや数値データを適切な位置に挿入します。残念ながら、RTL UI や RTL データの場合、単純に書式設定するだけでは表示が不正確になったり、出力されたテキストが判読不能になったりすることがあります。
アラビア語、ヘブライ語、ペルシャ語、ウルドゥー語などの言語は RTL で記述されます。しかし、数字や埋め込み LTR テキストなどの一部の要素は、RTL テキスト内でも LTR で記述されます。英語など、LTR 記述法を使用する言語も双方向です。埋め込み RTL 記述法を含む場合、その部分は RTL で表示する必要があります。
多くの場合、アプリはこのような埋め込みの逆方向テキストのインスタンスを生成します。たとえば、任意の言語と任意のテキスト方向のテキストデータをローカライズ メッセージに挿入します。このように複数の方向が混在している場合、どこからどこまでが逆方向テキストなのかを示すヒントが存在しないことも珍しくないため、アプリの生成したテキストによってユーザー エクスペリエンスが低下することがあります。
通常、システムのデフォルトでは双方向テキストは想定どおりに表示されますが、アプリによってローカライズ済みメッセージに挿入されると適切に表示されない可能性があります。テキストが正しく表示されない可能性が高い状況の例を次に示します。
-
メッセージの先頭に挿入されるテキスト:
PERSON_NAME さんから着信です
-
住所や電話番号など、数字で始まるテキスト:
987 654-3210
-
電話番号など、区切り記号で始まるテキスト
+19876543210
-
区切り記号で終わるテキスト:
本当によろしいですか?
-
すでに両方向が含まれているテキスト:
ヘブライ語の単語「בננה」はバナナを意味します。
例
「%s のことですか?」というメッセージをアプリで表示する必要があるとします。%s の部分は、実行時に住所が挿入されます。アプリは複数の UI ロケールをサポートしているため、メッセージはロケール固有のリソースから取得され、デバイスが RTL ロケールに設定されている場合は RTL 方向が使用されます。たとえばヘブライ語の UI では、メッセージは次のようになります。
האם התכוונת ל %s?
ただし、候補となる住所が、ロケール言語のテキストが含まれないデータベースから取得される可能性もあります。たとえば、カリフォルニアの住所は英語のテキストを使用するデータベースにあります。テキスト方向に関するヒントを指定せずに「15 Bay Street, Laurel, CA」という住所を RTL メッセージに挿入すると、結果は予期しないもの、または間違ったものになります。
האם התכוונת ל 15 Bay Street, Laurel, CA?
番地が住所の左側ではなく右側に表示されるため、郵便番号のように見えてしまいます。LTR テキスト方向を使用するメッセージに RTL テキストを含める場合も、同じ問題が発生する可能性があります。
説明と解決策
上の例の場合、テキスト書式設定ツールが「15」を住所の一部であると指定していないことが原因で問題が発生しています。その結果、「15」がその前の RTL テキストに含まれるのか、その後の LTR テキストに含まれるのか、システムが判別できなくなっています。
この問題を解決するには、BidiFormatter
クラスの unicodeWrap()
メソッドを使用します。このメソッドは、文字列の方向を検出して、方向を宣言する Unicode フォーマット文字で文字列をラップします。
unicodeWrap()
の使用方法を次のコード スニペットに示します。
Kotlin
val mySuggestion = "15 Bay Street, Laurel, CA" val myBidiFormatter: BidiFormatter = BidiFormatter.getInstance() // The "did_you_mean" localized string resource includes // a "%s" placeholder for the suggestion. String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion))
Java
String mySuggestion = "15 Bay Street, Laurel, CA"; BidiFormatter myBidiFormatter = BidiFormatter.getInstance(); // The "did_you_mean" localized string resource includes // a "%s" placeholder for the suggestion. String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion));
これにより、「15」は、LTR として宣言されたテキスト内に表示されるため、正しい位置に表示されることになります。
האם התכוונת ל 15 Bay Street, Laurel, CA?
ローカライズ メッセージに挿入するすべてのテキスト部分に対して unicodeWrap()
メソッドを使用します。ただし、次のいずれかに該当する場合を除きます。
- URI や SQL クエリなど、機械で判読可能な文字列にテキストを挿入する場合。
- テキスト部分がすでに適切にラップされているとわかっている場合。
注: アプリのターゲットが Android 4.3(API レベル 18)以降の場合は、Android フレームワークに含まれているバージョンの BidiFormatter
を使用してください。それ以外の場合は、サポート ライブラリに含まれているバージョンの BidiFormatter
を使用してください。
数値を書式設定する
アプリのロジックで数字を文字列に変換するには、メソッド呼び出しではなく書式設定文字列を使用します。
Kotlin
var myIntAsString = "$myInt"
Java
String myIntAsString = String.format("%d", myInt);
これにより、算用数字以外の数字表記を使用するロケールであっても、適切に数字が書式設定されます。
ペルシャ語ロケールや大半のアラビア語ロケールなど、独自の数字表記を使用するロケールのデバイス上で、String.format()
を使用して SQL クエリを作成すると、クエリに渡されるパラメータが数字の場合に問題が発生します。ロケール固有の数字表記で書式設定された数字は、SQL 内では無効な文字になります。
ASCII 形式の数字を保持し、SQL クエリを有効な状態のまま維持するには、最初のパラメータとしてロケールを指定するオーバーロード バージョンの String.format()
を使用する必要があります。ロケール引数 Locale.US
を使用します。
レイアウト ミラーリングをサポートする
RTL 記述法のユーザーは RTL ユーザー インターフェースを優先して使います。これには右揃えのメニュー、右揃えのテキスト、次に進むことを示す左向きの矢印が含まれます。
たとえば、設定アプリの画面の LTR バージョンと RTL バージョンの比較を図 4 に示します。
アプリに RTL サポートを追加する場合は、以下の点に注意してください。
- RTL テキスト ミラーリングがアプリ内でサポートされるのは、Android 4.2(API レベル 17)以降を搭載しているデバイスで使用する場合に限られます。古いデバイスでテキスト ミラーリングをサポートする方法については、旧式アプリ向けのサポートを提供するをご覧ください。
- アプリが RTL テキスト方向をサポートしているかテストするには、このガイドで説明しているように、開発者向けオプションを使用してテストを実行し、RTL 記述法を使用してアプリを使用するユーザーを招待します。
注: ミラーリングに適している要素や適していない要素のリストなど、レイアウト ミラーリングに関連する詳細な設計ガイドラインについては、双方向マテリアル デザイン ガイドラインをご覧ください。
アプリの中で UI レイアウトをミラーリングして RTL ロケールで RTL 表示にするには、以降のセクションに示す手順を実施します。
ビルドファイルとマニフェスト ファイルを編集する
アプリ モジュールの build.gradle
ファイルとアプリ マニフェスト ファイルを次のように編集します。
build.gradle (Module: app)
Groovy
android { ... defaultConfig { targetSdkVersion 17 // Or higher ... } }
Kotlin
android { ... defaultConfig { targetSdkVersion(17) // Or higher ... } }
AndroidManifest.xml
<manifest ... > ... <application ... android:supportsRtl="true"> </application> </manifest>
注: アプリのターゲットが Android 4.1.1(API レベル 16)以前の場合、android:supportsRtl
属性と、アプリのレイアウト ファイル内にあるすべての start
属性値、end
属性値は無視されます。この場合、RTL レイアウト ミラーリングがアプリ内で自動実行されることはありません。
既存のリソースを更新する
既存のレイアウト リソース ファイルで、left
と right
をそれぞれ start
と end
に変換します。これにより、ユーザーの言語設定に基づいてアプリの UI 要素を自動的に配置できるようになります。
注: リソースを更新する前に、Android 4.1.1(API レベル 16)以前をターゲットとするアプリ向けのサポートを提供する方法について確認してください。詳細については、旧式アプリ向けのサポートを提供するをご覧ください。
フレームワークの RTL 配置機能を使用するには、レイアウト ファイルで表 1 に示されている属性を変更します。
ターゲット SDK バージョンや、left
属性と right
属性の定義の有無、start
属性と end
属性の定義の有無に基づいて、システムが UI 配置属性を処理する方法を表 2 に示します。
|
left と right の定義 | start と end の定義 | 結果 |
---|---|---|---|
○ | ○ | ○ |
start と end を使用し、left と right をオーバーライド
|
○ | ○ | × | left と right を使用 |
○ | × | ○ | start と end を使用 |
× | ○ | ○ |
left と right を使用(start と end は無視)
|
× | ○ | × | left と right を使用 |
× | × | ○ |
start と end が left と right に解決される
|
方向別のリソースや言語別のリソースを追加する
ここでは、各種の言語やテキスト方向に合わせたカスタム値を含む特殊なバージョンのレイアウト、ドローアブル、値のリソース ファイルを追加する手順について説明します。
Android 4.2(API レベル 17)以降の場合、-ldrtl
(layout-direction-right-to-left)と -ldltr
(layout-direction-left-to-right)のリソース修飾子を使用できます。既存のリソースに関して下位互換性を維持するため、旧バージョンの Android では、リソースの言語修飾子を使用して、適切なテキスト方向を推測します。
ヘブライ語、アラビア語、ペルシャ語などの RTL 記述法をサポートするために、特別なレイアウト ファイルを追加するとします。そのためには、res/
ディレクトリ内に layout-ldrtl/
ディレクトリを追加します。たとえば、次のようになります。
res/ layout/ main.xml This layout file is loaded by default. layout-ldrtl/ main.xml This layout file is loaded for languages using an RTL text direction, including Arabic, Persian, and Hebrew.
アラビア語テキスト専用の特殊なレイアウト バージョンを追加する場合、ディレクトリ構造は次のようになります。
res/ layout/ main.xml This layout file is loaded by default. layout-ar/ main.xml This layout file is loaded for Arabic text. layout-ldrtl/ main.xml This layout file is loaded only for non-Arabic languages that use an RTL text direction.
注: 言語別のリソースは、デフォルトのリソースよりも優先されるレイアウト方向別のリソースよりも優先されます。
サポート対象のウィジェットを使用する
Android 4.2(API レベル 17)以降、フレームワーク UI 要素のほとんどが、RTL テキスト方向を自動的にサポートします。ただし、ViewPager
など、RTL テキスト方向をサポートしていないフレームワーク要素もいくつかあります。
ホーム画面ウィジェットは、対応するマニフェスト ファイル内に属性割り当ての android:supportsRtl="true"
が組み込まれていれば、RTL テキスト方向をサポートします。
旧式アプリ向けのサポートを提供する
アプリが Android 4.1.1(API レベル 16)以前をターゲットとしている場合は、start
と end
に加えて、left
属性と right
属性も組み込みます。
レイアウトが RTL テキスト方向を使用する必要があるかどうかをチェックするには、次のロジックを使用します。
Kotlin
private fun shouldUseLayoutRtl(): Boolean { return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { View.LAYOUT_DIRECTION_RTL == layoutDirection } else { false } }
Java
private boolean shouldUseLayoutRtl() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { return View.LAYOUT_DIRECTION_RTL == getLayoutDirection(); } else { return false; } }
注: 互換性の問題を回避するため、Android SDK Build Tools のバージョン 23.0.1 以降を使用してください。
開発者向けオプションを使用してテストする
Android 4.4(API レベル 19)以降を搭載しているデバイスの場合、デバイスの開発者向けオプションで [RTL レイアウト方向を使用] を有効にできます。この設定により、RTL モードで、英語テキストなどの LTR 記述法を使用するテキストが表示されるようになります。
アプリロジックを更新する
このセクションでは、複数のテキスト方向を処理するようにアプリを適応させる際に更新するアプリロジックの特定の側面について説明します。
プロパティの変更
レイアウト方向、レイアウト パラメータ、パディング、テキスト方向、テキストの配置、ドローアブルの配置など、RTL 関連プロパティの変更を処理するには、onRtlPropertiesChanged()
コールバックを使用します。このコールバックを使用すると、現在のレイアウト方向を取得し、それに基づいてアクティビティの View
オブジェクトを更新できます。
ビュー
ダイアログやトーストのような UI 要素など、アクティビティのビュー階層に直接含まれない UI ウィジェットを作成する場合は、コンテキストに応じた適切なレイアウト方向を設定します。次のコード スニペットは、そのプロセスを示しています。
Kotlin
val config: Configuration = context.resources.configuration view.layoutDirection = config.layoutDirection
Java
final Configuration config = getContext().getResources().getConfiguration(); view.setLayoutDirection(config.getLayoutDirection());
View
クラスのメソッドに関して、いくつか注意事項があります。
onMeasure()
- ビュー測定は、テキスト方向に応じて変化することがあります。
onLayout()
- 独自のレイアウト実装を作成する場合は、使用しているバージョンの
onLayout()
内でsuper()
を呼び出して、RTL 記述法をサポートするようにカスタム ロジックを最適化する必要があります。 onDraw()
- カスタムビューを実装する場合や、図形描画に拡張機能を追加する場合は、RTL 記述法をサポートするようにコードを更新する必要があります。ウィジェットが RTL モードかどうかを判別するには、次のコードを使用します。
Kotlin
// On devices running Android 4.1.1 (API level 16) and lower, // you can call the isLayoutRtl() system method directly. fun isLayoutRtl(): Boolean = layoutDirection == LAYOUT_DIRECTION_RTL
Java
// On devices running Android 4.1.1 (API level 16) and lower, // you can call the isLayoutRtl() system method directly. public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); }
ドローアブル
RTL レイアウト用にミラーリングが必要なドローアブルがある場合は、デバイスに搭載されている Android のバージョンに基づいて、以下のいずれかの手順を行います。
-
Android 4.3(API レベル 18)以前を搭載しているデバイスの場合、
-ldrtl
リソース ファイルを追加して定義します。 -
Android 4.4(API レベル 19)以降の場合、ドローアブルを定義する際に、
android:autoMirrored="true"
を使用します。これにより、RTL レイアウト ミラーリングが自動的に処理されるようになります。注:
android:autoMirrored
属性が機能するのは、双方向ミラーリングがドローアブル全体のシンプルなグラフィック ミラーリングとなるようなシンプルなドローアブルの場合に限られます。ドローアブル内に複数の要素が含まれる場合や、ドローアブルを反転すると解釈方法が変化するような場合は、手動でミラーリングを行うことができます。可能な場合はその言語に詳しい人に相談して、ドローアブルのミラーリングが意味が通じるものになっているかどうかを確認してもらいます。
Gravity
アプリのレイアウト コードで Gravity.LEFT
または Gravity.RIGHT
を使用している場合は、値をそれぞれ Gravity.START
、Gravity.END
に変更します。
Gravity.LEFT
プロパティまたは Gravity.RIGHT
プロパティに依存する Kotlin コードまたは Java コードがある場合は、layoutDirection
に合わせて absoluteGravity
を設定すると、この変更に対応できます。
たとえば、次のコードを使用しているとします。
Kotlin
when (gravity and Gravity.HORIZONTAL_GRAVITY_MASK) { Gravity.LEFT -> { // Handle objects that are left-aligned. } Gravity.RIGHT -> { // Handle objects that are right-aligned. } }
Java
switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: // Handle objects that are left-aligned. break; case Gravity.RIGHT: // Handle objects that are right-aligned. break; }
次のように変更します。
Kotlin
val absoluteGravity: Int = Gravity.getAbsoluteGravity(gravity, layoutDirection) when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) { Gravity.LEFT -> { // Handle objects that are left-aligned. } Gravity.RIGHT -> { // Handle objects that are right-aligned. } }
Java
final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: // Handle objects that are left-aligned. break; case Gravity.RIGHT: // Handle objects that are right-aligned. break; }
つまり、グラビティ値として start
と end
を使用している場合でも、左揃えの値や右揃えの値を処理する既存のコードはそのまま使用できます。
注: グラビティ設定を適用する場合は、layoutDirection
引数を含むオーバーロード バージョンの Gravity.apply()
を使用してください。
マージンとパディング
アプリで RTL 記述法をサポートするには、マージン値とパディング値に関する以下のベスト プラクティスに従うようにしてください。
-
方向別の属性である
leftMargin
とrightMargin
ではなく、それに相当するgetMarginStart()
とgetMarginEnd()
を使用します。 -
setMargins()
を使用する場合、アプリが RTL 記述法を検出したら、left
引数とright
引数の値を交換します。 -
アプリがカスタム パディング ロジックを持つ場合は、
setPadding()
とsetPaddingRelative()
をオーバーライドします。
アプリ別の言語設定のサポート
多言語ユーザーは、多くの場合、特定のアプリに対してシステムに設定した言語(英語など)とは別の言語(オランダ語、中国語、ヒンディー語など)を選択します。Android 13 では、このようなユーザーのエクスペリエンスを改善するために、複数の言語をサポートするアプリ向けに以下の機能を導入しています。
-
システム設定: ここから、ユーザーがアプリごとに使用する言語を一元的に選択できます。
アプリのマニフェストで
android:localeConfig
属性を宣言して、複数の言語に対応するようにシステムに指示する必要があります。詳しくは、リソース ファイルを作成してアプリのマニフェスト ファイルで宣言する手順をご覧ください。 -
その他の API:
LocaleManager
のsetApplicationLocales()
メソッドやgetApplicationLocales()
メソッドなど、公開 API を使用すると、アプリは実行時にシステム言語とは異なる言語を設定できます。カスタムのアプリ内言語選択ツールを使用するアプリでは、こうした API を使用して、ユーザーがどこで言語設定を選択するかにかかわらず、一貫したユーザー エクスペリエンスを提供できます。公開 API では、ボイラープレート コードの量を減らすことができ、分割 APK がサポートされます。また、アプリレベルのユーザー言語設定を保存するためのアプリの自動バックアップもサポートされます。
以前の Android バージョンとの下位互換性を維持するため、同等の API であれば AndroidX でも使用できます。Appcompat 1.6.0-beta01 以上を使用することをおすすめします。
詳しくは、新しい API の実装の手順をご覧ください。
関連ドキュメント
参考情報
旧式デバイスのサポートについて詳しくは、次のリソースをご覧ください。