メインコンテンツへスキップ

React Nativeアプリの右から左へのレイアウトサポート

·8分で読めます
Mengjue (Mandy) Wang
Facebook ソフトウェアエンジニアインターン

アプリをApp Storeにリリースした後、国際化はオーディエンスの拡大に向けた次のステップです。世界中の20カ国以上、そして多くの人々が右から左へ記述する(RTL)言語を使用しています。したがって、彼らのためにアプリがRTLをサポートすることは不可欠です。

React NativeがRTLレイアウトをサポートするように改善されたことを発表できることを嬉しく思います。これは本日、react-nativeのマスターブランチで利用可能であり、次のRCであるv0.33.0-rcで利用可能になります。

これには、RNで使われているコアレイアウトエンジンであるcss-layoutやRNのコア実装、そして特定のOSS JSコンポーネントをRTLに対応させるための変更が含まれています。

本番環境でRTLサポートを実戦テストするため、最新バージョンの Facebook Ads Manager アプリ(初のクロスプラットフォーム100% RNアプリ)が、アラビア語とヘブライ語で、iOSAndroidの両方でRTLレイアウトをサポートして利用可能になりました。これらのRTL言語での表示は次のとおりです。

RTLサポートにおけるRNの概要変更点

css-layoutには、すでにレイアウトのstartendの概念があります。左から右へ記述する(LTR)レイアウトでは、startleftを意味し、endrightを意味します。しかし、RTLでは、startrightを意味し、endleftを意味します。これは、RNがstartendの計算に依存して、positionpaddingmarginを含む正しいレイアウトを計算できることを意味します。

さらに、css-layoutは、すでに各コンポーネントの方向をその親から継承するようにしています。これは、ルートコンポーネントの方向をRTLに設定するだけで、アプリ全体が反転することを意味します。

以下の図は、変更点の概要を示しています。

これらには以下のものが含まれます。

このアップデートにより、アプリでRTLレイアウトを許可すると、

  • すべてのコンポーネントのレイアウトが水平方向に反転します
  • RTL対応のOSSコンポーネントを使用している場合、一部のジェスチャーやアニメーションも自動的にRTLレイアウトになります
  • アプリを完全にRTL対応にするために、最小限の追加作業が必要になる場合があります

アプリをRTL対応にする

  1. RTLをサポートするには、まずRTL言語のバンドルをアプリに追加する必要があります。

    • iOSAndroidの一般的なガイドを参照してください。
  2. ネイティブコードの冒頭でallowRTL()関数を呼び出すことで、アプリのRTLレイアウトを許可します。このユーティリティは、アプリの準備ができたときにのみRTLレイアウトを適用するために提供されています。以下に例を示します。

    iOS

    // in AppDelegate.m
    [[RCTI18nUtil sharedInstance] allowRTL:YES];

    Android

    // in MainActivity.java
    I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
    sharedI18nUtilInstance.allowRTL(context, true);
  3. Androidの場合、AndroidManifest.xmlファイルの<application>要素にandroid:supportsRtl="true"を追加する必要があります。

これで、アプリを再コンパイルし、デバイスの言語をRTL言語(例:アラビア語やヘブライ語)に変更すると、アプリのレイアウトは自動的にRTLに変わるはずです。

RTL対応コンポーネントの記述

一般的に、ほとんどのコンポーネントはすでにRTL対応です。例えば、

  • 左横書きレイアウト
  • 右横書きレイアウト

ただし、注意すべきいくつかのケースがあり、それらにはI18nManagerが必要になります。I18nManagerには、アプリのレイアウトがRTLであるかどうかを判断する定数isRTLがあり、レイアウトに応じて必要な変更を加えることができます。

方向性のある意味を持つアイコン

コンポーネントにアイコンや画像がある場合、RNはソース画像を反転させないため、LTRレイアウトとRTLレイアウトで同じように表示されます。したがって、レイアウトスタイルに応じてこれらを反転させる必要があります。

  • 左横書きレイアウト
  • 右横書きレイアウト

方向に応じてアイコンを反転させる方法は2つあります。

  • 画像コンポーネントにtransformスタイルを追加する

    <Image
    source={...}
    style={{transform: [{scaleX: I18nManager.isRTL ? -1 : 1}]}}
    />
  • または、方向に応じて画像ソースを変更する

    let imageSource = require('./back.png');
    if (I18nManager.isRTL) {
    imageSource = require('./forward.png');
    }
    return <Image source={imageSource} />;

ジェスチャーとアニメーション

AndroidおよびiOS開発において、RTLレイアウトに変更すると、ジェスチャーとアニメーションはLTRレイアウトと逆になります。現在、RNでは、ジェスチャーとアニメーションはRNコアコードレベルではなく、コンポーネントレベルでサポートされています。幸いなことに、SwipeableRowNavigationExperimentalなど、これらのコンポーネントの一部はすでにRTLをサポートしています。ただし、ジェスチャーを持つ他のコンポーネントは、手動でRTLをサポートする必要があります。

ジェスチャーRTLサポートを説明する良い例はSwipeableRowです。

ジェスチャーの例
// SwipeableRow.js
_isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
// ...
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return (
this._isSwipingRightFromClosed(gestureState) &&
gestureStateDx > RIGHT_SWIPE_THRESHOLD
);
},
アニメーションの例
// SwipeableRow.js
_animateBounceBack(duration: number): void {
// ...
const swipeBounceBackDistance = IS_RTL ?
-RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
this._animateTo(
-swipeBounceBackDistance,
duration,
this._animateToClosedPositionDuringBounce,
);
},

RTL対応アプリのメンテナンス

RTL互換アプリの初期リリース後も、新しい機能について繰り返し開発を行う必要があるでしょう。開発効率を向上させるために、I18nManagerは、テストデバイスの言語を変更することなく、より高速なRTLテストのためにforceRTL()関数を提供します。アプリにこのための簡単なスイッチを提供したいかもしれません。以下はRNTesterのRTL例からの例です。

<RNTesterBlock title={'Quickly Test RTL Layout'}>
<View style={styles.flexDirectionRow}>
<Text style={styles.switchRowTextView}>forceRTL</Text>
<View style={styles.switchRowSwitchView}>
<Switch
onValueChange={this._onDirectionChange}
style={styles.rightAlignStyle}
value={this.state.isRTL}
/>
</View>
</View>
</RNTesterBlock>;

_onDirectionChange = () => {
I18nManager.forceRTL(!this.state.isRTL);
this.setState({isRTL: !this.state.isRTL});
Alert.alert(
'Reload this page',
'Please reload this page to change the UI direction! ' +
'All examples in this app will be affected. ' +
'Check them out to see what they look like in RTL layout.',
);
};

新機能に取り組む際、このボタンを簡単に切り替えてアプリをリロードすることでRTLレイアウトを確認できます。利点は、テストのために言語設定を変更する必要がないことですが、次のセクションで説明するように、一部のテキスト配置は変更されません。したがって、リリース前にRTL言語でアプリをテストすることは常に良いアイデアです。

制限事項と将来の計画

RTLサポートはアプリのUXのほとんどをカバーするはずですが、現時点ではいくつかの制限があります。

  • テキストの配置の挙動がAndroidとiOSで異なる
    • iOSでは、デフォルトのテキスト配置はアクティブな言語バンドルに依存し、常に一方の側に揃えられます。Androidでは、デフォルトのテキスト配置はテキストコンテンツの言語に依存し、例えば英語は左揃え、アラビア語は右揃えになります。
    • 理論的には、これはプラットフォーム間で一貫性を保つべきですが、アプリを使用する人によっては、一方の挙動を好む場合があります。テキスト配置のベストプラクティスを見つけるためには、さらなるユーザーエクスペリエンス調査が必要かもしれません。
  • 「真の」左/右が存在しない

    前述の通り、JS側のleft/rightスタイルをstart/endにマッピングしており、RTLレイアウトの場合、コード内のすべてのleftは画面上で「右」になり、コード内のrightは画面上で「左」になります。これは、製品コードをあまり変更する必要がないため便利ですが、コード内で「真の左」または「真の右」を指定する方法がないことを意味します。将来的には、言語に関係なくコンポーネントが自身の方向を制御できるようにすることが必要になるかもしれません。

  • ジェスチャーとアニメーションのRTLサポートをより開発者フレンドリーにする

    現在、ジェスチャーとアニメーションをRTL互換にするには、まだプログラミングの労力が必要です。将来的には、ジェスチャーとアニメーションのRTLサポートをより開発者フレンドリーにする方法を見つけることが理想的です。

試してみる!

RTLサポートについてさらに理解するには、RNTester内のRTLExampleをチェックして、どのように機能するか教えてください!

最後までお読みいただきありがとうございます!React NativeのRTLサポートが、国際的なオーディエンス向けにアプリを成長させる助けとなることを願っています!