Web アプリケーションで RTL 言語レイアウトをサポートする

> 本記事は元々 [blog.logto.io](https://blog.logto.io/ja/rtl-language-support) に掲載されたものです。

## はじめに

[**Logto**](https://logto.io) は、カスタマーアイデンティティおよびアクセス管理 (CIAM) ソリューションのより良い選択肢です。これはオープンソースであり、活気のあるコミュニティに支えられています。最近、私たちのコミュニティ（クレジット [@zaaakher](https://github.com/zaaakher)）が Logto 管理コンソールと Logto サインインエクスペリエンスの両方へのアラビア語の翻訳を貢献し、アラビア語を話すユーザーにとってよりアクセスしやすくしました。

しかし、翻訳があるだけでは十分ではないことを理解しています。UI レイアウトが右から左 (RTL) 言語用に最適化されていることを確認する必要があります。このチュートリアルでは、RTL 互換性における一般的な課題について説明し、それらを Web アプリケーションでどのように解決するかを説明します。

## RTL Web アプリケーションはどのように見えるのか？

左から右 (LTR) の Web アプリケーションでは、レイアウトは画面の左側から始まるように設計されています。コンテンツは左から右に流れ、スクロールバーは画面の右側にあります。対照的に、RTL Web アプリケーションは画面の右側から始まります。コンテンツは右から左に流れ、スクロールバーは画面の左側にあります。

Logto コンソールのスクリーンショットを例として示します：
![Logto Console in RTL mode](https://uploads.strapi.logto.io/367272534_31cef491_e3b4_44b0_9276_379384a7f3e2_5eb46fab99.png)

## RTL 互換性における課題

LTR Web アプリケーションを RTL フレンドリーに変換する際には、以下のような課題に直面することがあります：

1. **テキストの配置**：RTL モードではテキストの配置を右揃えにする必要があります。
2. **コンテンツの方向**：RTL モードではコンテンツの方向を右から左にする必要があります。例：ナビゲーションサイドバー、チャートなど。
3. **スクロールバーの位置**：RTL モードではスクロールバーは画面の左側にあるべきです。
4. **アイコンと画像**：一部のアイコンや画像は RTL モードで反転する必要があります。例：シェブロンアイコンなど。
5. **日付と数字のローカライズ**：日付のフォーマットと "アラビア・インディック数字 (٠١٢٣٤٥٦٧٨٩)" を RTL モードで使用し、"西洋アラビア数字 (0-9)" の代わりに利用します。
6. **間隔、位置付け、その他**： フォーカスする必要がある他の細かい調整には、マージンとパディング、ボーダー半径、絶対位置付け、アニメーションなどが含まれます。

## 上記の課題を克服するにはどうすればよいか？

ここに、Logto を RTL フレンドリーにするために実施したいくつかのヒントとコツを示します：

### HTML 属性 `dir="rtl"` を使用する

現在の言語がアラビア語または他の RTL 言語である場合、Web アプリケーションのルート要素に HTML 属性 `dir="rtl"` を適用します。

```html
<html lang="ar" dir="rtl">
```

これはブラウザにコンテンツが RTL モードで表示されるべきことを理解させ、最初の 3 つの課題（テキストの配置、コンテンツの方向、スクロールバーの位置）を自動で調整します。ただし、カスタムスクロールバーを使用している場合は、位置を手動で調整する必要があるかもしれません。

### RTL モードでアイコンを反転させるコンポーネントを実装する。

言語が RTL の場合、CSS の `transform: scaleX(-1);` を使用してアイコンを水平に反転させることができます。これは React と TypeScriptで記述された例です：

```tsx
import classNames from 'classnames';
import { cloneElement, isValidElement, type ReactElement } from 'react';
import { useTranslation } from 'react-i18next';

import styles from './index.module.scss';

type Props = {
  readonly children: ReactElement<HTMLElement>;
};

function FlipOnRtl({ children }: Props) {
  const { i18n } = useTranslation();
  const isRtl = i18n.dir() === 'rtl';

  if (!isValidElement(children)) {
    return children;
  }

  return cloneElement(children, {
    className: classNames(children.props.className, isRtl && styles.flip),
  });
}

export default FlipOnRtl;
```

```scss
.flip {
  transform: scaleX(-1);
}
```

このコンポーネントを使用してアイコンコンポーネントを包むと、RTL モードでアイコンを自動的に反転させます。

 ```tsx
 <FlipOnRtl>
   <Icon />
 </FlipOnRtl>
 ```

### 日付、時刻、数字をローカライズする。

JavaScript の `toLocalString` 関数を使用して日付、時刻、数字をローカライズします。例：

#### 日付と時刻のローカライズ

```js
const date = new Date();

// 日付のローカライズ
const localizedDate = date.toLocaleDateString('ar', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
});
console.log(localizedDate); // Output: "١٤٤٥/١٠/١٢" (例の出力で、異なる場合あり)

// 時刻のローカライズ
const localizedTime = date.toLocaleTimeString('ar', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
});
console.log(localizedTime); // Output: "٠١:٣٠:٠٠ م" (例の出力で、異なる場合あり)

// 日付と時刻の両方のローカライズ
const localizedDateTime = date.toLocaleString('ar', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
});
console.log(localizedDateTime); // Output: "١٤٤٥/١٠/١٢، ٠١:٣٠:٠٠ م" (例の出力で、異なる場合あり)
```

また、[date-fns](https://date-fns.org/) のようなライブラリを使用して日付と時刻のローカライズを行うこともできます。

#### 数字のローカライズ

数字の場合、`ar-u-nu-arab` オプションで `toLocaleString` メソッドを使用してアラビア・インディック数字を表示することもできます。

```js
const number = 1234567890;
const easternArabicNumber = number.toLocaleString('ar-u-nu-arab');
console.log(easternArabicNumber); // ١٢٣٤٥٦٧٨٩٠
```

説明
- **ar**: アラビア語を示します。
- **u**: 拡張が可能な Unicode を表します。
- **nu-arab**: アラビア・インディック数字の使用を指定しています。

### 間隔、位置付け、その他を調整する。

マージン、位置付け、ボーダー半径、その他の CSS スタイルを適切に調整する必要があるかもしれません。一般的なケースを以下に示します：

### マージンとパディング

`margin-left`、`margin-right`、`padding-left`、`padding-right` を書く代わりに、`margin-inline-start`、`margin-inline-end`、`padding-inline-start`、`padding-inline-end` を使用して LTR および RTL モードの両方に対処することができます。

```scss
.element {
  // margin-left: 10px;
  margin-inline-start: 10px;

  // margin-right: 10px;
  margin-inline-end: 10px;

  // margin: 10px 20px 30px 40px;
  margin-inline: 40px 20px;
  margin-block: 10px 30px;

  // padding-left: 10px;
  padding-inline-start: 10px;

  // padding-right: 10px;
  padding-inline-end: 10px;

  // padding: 10px 20px 30px 40px;
  padding-inline: 40px 20px;
  padding-block: 10px 30px;
}
```

### 絶対位置指定

絶対位置指定を使用する際には、`left` と `right` の代わりに `inset-inline-start` と `inset-inline-end` を使用することができます。

```scss
.element {
  position: absolute;
  top: 0;
  // left: 0;
  inset-inline-start: 0;
  // right: 0;
  inset-inline-end: 0;
}
```

### ボーダー半径

ボーダー半径を使用する際には、`border-start-start-radius`、`border-start-end-radius`、`border-end-start-radius`、`border-end-end-radius` を使用して LTR および RTL モードの両方に対処することができます。

```scss
.element {
  // border-radius: 10px 20px 30px 40px;
  border-start-start-radius: 10px;
  border-start-end-radius: 20px;
  border-end-start-radius: 30px;
  border-end-end-radius: 40px;
}
```

### その他の雑多なもの

上記の方法で対応できない場合があるかもしれません。そのような場合、`:dir()` 擬似クラスを使用してテキストの方向に基づいた異なるスタイルを適用できます。

```scss
.element {
  // デフォルトのスタイル
  color: black;

  // RTL 用のスタイル
  &:dir(rtl) {
    color: red;
  }
}
```

## まとめ

このチュートリアルでは、RTL フレンドリーな UI を実装する際の課題について説明し、Logto コンソールおよび Logto サインインエクスペリエンスでそれらにどのように対処したかを共有しました。上記のヒントとコツを適用することで、Web アプリケーションを RTL 言語ユーザーにとってよりアクセスしやすくすることができます。

<LogtoCloudButton />