# General Translation Knowledge Base: CSS で RTL（右から左）言語をサポートするには？
URL: https://generaltranslation.com/ja/kb/right-to-left-css.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: lang だけでは書字方向は変わらないため、html 要素に dir="rtl" と lang の両方を指定します。そのうえで、margin-left などの物理的な CSS を margin-inline-start などの論理プロパティに置き換えます。flex の行、グリッドの列、デフォルトのテキストの配置は自動的に反転しますが、translateX()、left、right は反転しません。
Category: ロケール別フォーマット
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

アラビア語、ヘブライ語、ペルシア語、ウルドゥー語は右から左に読む言語です。ドキュメントで書字方向を宣言しさえすれば、flex の行、グリッドの列、デフォルトのテキストの配置はブラウザが自動的に反転してくれます。RTL のバグの多くは、`left`、`right`、`margin-left`、`translateX` のように物理的な方向を指定する CSS に起因します。

## HTML で書字方向を設定する

ルート要素に書字方向と言語を指定します。

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

W3C の[テキストの方向に関するガイダンス](https://www.w3.org/International/questions/qa-html-dir)では、基本の書字方向を CSS の `direction` プロパティではなくマークアップで宣言するよう推奨しています。こうしておけば、スタイルが読み込まれなかった場合でも書字方向が保たれます。MDN の [`direction` リファレンス](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)も同じ立場です。`lang` 属性は、`ar-Arab` のように文字体系のサブタグを含めても書字方向を設定しないため、両方を指定する必要があります。ページ内のより深い要素に `dir` を追加するのは、アラビア語の記事内の英語の引用のように、ブロック単位で書字方向が切り替わる場合に限ってください。

JavaScript でロケールコードから `dir` を導き出すには、[`Intl.Locale` の `getTextInfo()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo) を使います。これは書字方向を返し、たとえば `new Intl.Locale('ar').getTextInfo().direction` は `'rtl'` になります。Chrome 130、Safari 17、Firefox 153 がこれをサポートしており、Chrome 99〜129 では同じデータが `textInfo` プロパティとして公開されています。古いブラウザ向けのフォールバックも用意しておきましょう。

## レイアウトを反転させるにはどの方法を使うべきか？

書字方向を設定したら、次はスタイルシートをそれに追従させます。一般的な方法は4つあります。

| 方法                                                          | レイアウトの反転方法                       | 保守の手間                       | 主なリスク                               |
| ----------------------------------------------------------- | -------------------------------- | --------------------------- | ----------------------------------- |
| `dir` の代わりに CSS の `direction: rtl` を使う                      | テキストと論理プロパティは反転するが、物理プロパティは反転しない | 少ない                         | `:dir()` に無視されるうえ、CSS がないと書字方向が失われる |
| RTL 用の別スタイルシート (手書き、または [RTLCSS](https://rtlcss.com/) で生成)  | 2つ目のスタイルシートで物理的な左右を入れ替える         | 2つのスタイルシートを配信し、常に同期させる必要がある | LTR のスタイルを変更するたびに上書き設定とのずれが生じる      |
| `dir` 属性と CSS 論理プロパティ                                       | 各プロパティが `dir` に基づいて正しい側に解決される    | スタイルシート1つで済む                | 後の変更で物理プロパティが再び紛れ込む                 |
| Tailwind CSS の論理ユーティリティと `rtl:`・`ltr:` バリアント                | ユーティリティが論理プロパティにコンパイルされる         | クラスのセット1つで済む                | 論理ユーティリティで十分な場面でバリアントを使ってしまう        |

新しいコードや手を入れるコンポーネントには、論理プロパティを (直接、または Tailwind 経由で) 使用してください。1つのスタイルシートで両方の書字方向に対応できます。大規模なレガシーのコードベースに `left` や `right` が多用されていて、リファクタリングより先に RTL に対応する必要がある場合は、生成した RTL スタイルシートを使用してください。

## 論理プロパティでレイアウトを記述する

[CSS 論理プロパティ](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)は、テキストの流れる方向を基準に辺を指定します。inline-start は LTR では左、RTL では右になります。論理プロパティは Baseline の「Widely available」に該当し、たとえば `inset-inline-start` は 2021 年 4 月から各ブラウザで利用できます。以下に、カードの変更前と変更後を示します。

```css
/* 変更前: 物理プロパティの辺は RTL でも入れ替わらない */
.card {
  margin-left: 16px;
  padding-right: 24px;
  text-align: left;
  border-left: 4px solid var(--accent);
  border-top-left-radius: 8px;
}
.card .badge {
  position: absolute;
  top: 8px;
  right: 8px;
}

/* 変更後: 論理プロパティの辺は dir に応じて反転する */
.card {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
  text-align: start;
  border-inline-start: 4px solid var(--accent);
  border-start-start-radius: 8px;
}
.card .badge {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 8px;
}
```

`dir="rtl"` が指定されたページでは、`margin-inline-start: 16px` は右側 16px のマージンとして算出され、`border-start-start-radius` は右上の角を丸めます。インライン方向の両側をまとめて設定するには、`margin-inline` と `padding-inline` を使用します。

### 自動的に反転するもの

* **Flexbox の行：** `flex-direction: row` の場合、最初のアイテムは右端に配置されます。
* **グリッドの列：** 1 列目が最も右側の列になります。
* **テキストの配置：** `text-align` の初期値は `start` であるため、スタイルを指定していないテキストは右揃えになります。
* **インラインコンテンツとテーブルの列：** どちらも基本の書字方向に従います。

### 反転しないもの

* 位置指定された要素の `left` と `right`：`left: 0` は左側に固定されたままです。
* `margin-left`、`padding-right`、`border-left`、`float: left`、`text-align: left`。
* `transform: translateX()`：正の値は常に右方向への移動になるため、スライドインするドロワーやカルーセルが逆向きに動いてしまいます。
* `background-position: left`、`linear-gradient(to right, …)`、`box-shadow` の水平オフセット。これらには論理的な指定方法がないため、`:dir(rtl)` で上書きしてください。

transform の場合は、カスタムプロパティで符号を反転させます：

```css
:root { --inline-sign: 1; }
:root:dir(rtl) { --inline-sign: -1; }

/* インライン開始側の端から外へ隠れるドロワー */
.drawer[data-state="closed"] {
  transform: translateX(calc(-100% * var(--inline-sign)));
}
```

### Tailwind CSS

Tailwind CSS v4.3 の論理ユーティリティには、`ms-*` と `me-*` (margin) 、`ps-*` と `pe-*` (padding) 、`inset-s-*` と `inset-e-*`、`text-start`、`border-s-*`、`rounded-s-*` があります。これらは [margin](https://tailwindcss.com/docs/margin) など、各ユーティリティのページに記載されています。[`rtl:` および `ltr:` バリアント](https://tailwindcss.com/docs/hover-focus-and-other-states#rtl-support)は `:dir()` を含むセレクターにコンパイルされるため、`ltr:` は `dir="ltr"` を設定していないページにもマッチします。`ltr:ml-3 rtl:mr-3` ではなく `ms-3` を使い、バリアントは `rtl:-scale-x-100` のような変形やアイコンに限って使用してください。

## アイコン、書字方向が混在するテキスト、フォントの扱い

### 方向を示すアイコンのみを反転する

RTL ではアイコンを左右反転します:

```css
.icon-directional:dir(rtl) {
  transform: scaleX(-1);
}
```

[`:dir()` 疑似クラス](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir)は要素が継承した書字方向にマッチしますが、`[dir="rtl"]` は属性が指定された要素自体にしかマッチしません。`:dir()` は2023年12月から主要ブラウザすべてで利用でき、現在は Baseline widely available に分類されています。古いブラウザをサポートする必要がある場合は、代わりに `[dir="rtl"] .icon-directional` を使用してください。

Apple の[右から左の表示に関するガイドライン](https://developer.apple.com/design/human-interface-guidelines/right-to-left)は、Web でもそのまま参考になるルールです。

* **反転するもの：** 戻る・進む矢印、次へ・前へのコントロール、プログレスバーやスライダー、テキスト行を表すアイコン、前進する動きを表すアイコン。
* **反転しないもの：** ロゴ、チェックマークなどの普遍的な記号、時計などの物理的な物体、右利き用に描かれた道具、写真やイラスト。
* **数字の並びは決して逆にしない：** 電話番号や541という数字は、どの言語でも同じ順序で読まれます。

### 双方向テキストを分離する

RTL と LTR のテキストが同じ行に混在すると、スペース、ハイフン、数字などの中立文字が誤った側に結びつくことがあります。W3C の [inline bidi markup guide](https://www.w3.org/International/articles/inline-bidi-markup/) では、この現象を「spillover」と呼んでいます。MDN の [`<bdi>` の例](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)では、アラビア語の名前の後に &quot;- 1st place&quot; が続くと、&quot;1&quot; が反対側の端にレンダリングされてしまいます。周囲のテキストと書字方向が異なる値は、それぞれラップしてください。

```html
<!-- データベースから取得した名前（書字方向は不明） -->
<li><bdi>اَلأَعْشَى</bdi> - 1st place</li>

<!-- アラビア語テキスト内でも常に LTR で表示する値 -->
<p>اتصل بنا على <span dir="ltr">+1 (555) 010-2030</span></p>

<!-- ユーザーが入力するテキスト（書字方向は不明） -->
<textarea dir="auto"></textarea>
```

`<bdi>` は `dir="auto"` と同じように動作し、最初の強い方向性を持つ文字から書字方向を決定します。電話番号、メールアドレス、URL、注文 ID など、常に左から右に表示される値には `dir="ltr"` を指定してください。CSS プロパティ `unicode-bidi: isolate` を使えばスタイルシート側でも同じ分離を行えますが、MDN では [`unicode-bidi` を上書きしないよう注意喚起されている](https://developer.mozilla.org/en-US/docs/Web/CSS/unicode-bidi)ため、マークアップで `dir` と `<bdi>` を使うことをおすすめします。

### フォント

* **文字体系に対応したフォントを使う。** アラビア文字やヘブライ文字のグリフを含むフォントは、言語単位で適用範囲を限定して指定します：`:lang(ar) { font-family: "Noto Sans Arabic", system-ui, sans-serif; }`
* **アラビア文字には字間を設定しない。** アラビア文字は文字同士が連結します。[CSS Text 仕様](https://www.w3.org/TR/css-text-3/)でも、ブラウザ間で表示結果に互換性がないため、筆記体の文字体系には `letter-spacing` を使用しないよう推奨しています。
* **大文字とのバランスを取る。** アラビア文字やヘブライ文字には大文字がないため、隣に並ぶ大文字のみのラテン文字が大きく見えてしまいます。Apple は、RTL フォントのサイズを約 2 ポイント大きくすることを推奨しています。

## RTLでよくあるバグとその対処法

### ページは翻訳されているのに、左から右に表示される

**原因：** 翻訳後のページで `lang="ar"` は設定されているものの `dir` が設定されていないか、内側のラッパー要素に `direction: rtl` を設定しています。**対処法：** サーバーでレンダリングされた HTML も含め、すべてのレンダリングでアクティブなロケールに基づいて `<html>` に `dir="rtl"` を設定してください。

### アイコンとラベルの間の余白が逆側に付く

**原因：** アイコンとテキストの間に `margin-left` や `padding-right` を使用している。**修正方法：** `margin-inline-end` または Tailwind の `me-*` に切り替えるか、そもそも左右の区別がない `gap` を flex コンテナで使用します。

Chrome 155 で、ラベルの前に 16px のアイコンを配置した inline-flex のボタンを計測したところ、物理プロパティのマージンでは、右から左のテキストでアイコンとラベルの間の余白がなくなり、8px 分の余白が外側の端に移ってしまいました。一方、論理プロパティのマージンはラベル側に正しく移動しました：

```text title="Output"
.icon { margin-right: 8px }        ltr: 8px gap   rtl: 0px gap
.icon { margin-inline-end: 8px }   ltr: 8px gap   rtl: 8px gap (computed as margin-left)
```

### バッジ、ツールチップ、ドロップダウンが逆側の端から開く

**原因:** `position: absolute` を `left` または `right` と組み合わせて使用している、あるいは JavaScript の位置計算ライブラリが左端を基準に位置を計算している。**対処法:** `inset-inline-start` と `inset-inline-end` を使用します。位置計算ライブラリが書字方向の指定に対応している場合は、書字方向も渡してください。

### ドロワーやカルーセルが逆方向にスライドする

**原因:** `translateX()` は物理的な方向を基準にしています。**対処法:** 上記のドロワーの例のように、書字方向に応じた符号 (±1) を掛けたうえで、RTL でのスワイプ操作をテストしてください。

### 「戻る」矢印が前を向いている、またはロゴが左右反転している

**原因:** アイコンを一律に反転するか、まったく反転しないかのどちらかになっている。**対処法:** 方向を示すアイコンだけを反転させます。

### 句読点や数字が行の反対側に移動してしまう

**原因：** ユーザー名、製品名、電話番号など、周囲と書字方向が異なる値が挿入されていることです。**対処法：** その値を `<bdi>` でラップします。左から右であることがわかっている場合は、`dir="ltr"` を指定した要素でラップしてもかまいません。

### アラビア文字がつながらずに表示される

**原因：** ラテン文字の見出し向けに設定したグローバルな `letter-spacing` が適用されている。**対処法：** `:lang(ar)` をはじめとする筆記体の文字体系では、`letter-spacing` を 0 にリセットします。

翻訳が揃う前にこうしたバグを見つけるには、右から左の[疑似ロケール](/kb/pseudo-localization)でアプリを一通り操作してみてください。また、リリース前には[多言語アプリのリリースチェックリスト](/kb/multilingual-app-launch-checklist)にある右から左 (RTL) の項目を確認してください。

## General Translationでアクティブなロケールからdirを設定する

Next.js App Routerアプリでは、`gt-next`が`gt-next/server`から[`getLocaleDirection`](/docs/react/nextjs/reference/functions/get-locale-direction)を提供しているため、ルートレイアウトでサーバー側から`lang`と`dir`を設定できます。この例では、`app/[locale]/layout.tsx`をルートレイアウトとして想定しています。Next.jsでは、ルートレイアウトで[`<html>`と`<body>`の両方をレンダリングする必要があります](https://nextjs.org/docs/app/api-reference/file-conventions/layout#root-layout)。

```tsx title="app/[locale]/layout.tsx"
import { getLocale, getLocaleDirection } from 'gt-next/server';
import type { ReactNode } from 'react';

export default async function LocaleLayout({ children }: { children: ReactNode }) {
  const [locale, direction] = await Promise.all([
    getLocale(),
    getLocaleDirection(),
  ]);
  return (
    <html lang={locale} dir={direction}>
      <body>{children}</body>
    </html>
  );
}
```

`gt-react`、`gt-next`、`gt-react-native` のコンポーネントでは、[`useLocaleDirection`](/docs/react/reference/hooks/use-locale-direction) フックがアクティブなロケールに応じて `'ltr'` または `'rtl'` を返します。Web では、ツリー内のいずれかのコンポーネントでこの値を `document.documentElement.dir` に設定します。React Native には `document` がありません。ネイティブのレイアウト方向は React Native の [`I18nManager`](https://reactnative.dev/docs/i18nmanager) で決まり、`allowRTL` や `forceRTL` による変更は次回のアプリ起動時まで反映されません。Vue、`gt-tanstack-start`、サーバーテンプレートなどその他のスタックでは、`generaltranslation` に含まれる Core ライブラリの [`getLocaleDirection`](/docs/platform/core/reference/utility-functions/locales/get-locale-direction) を使用します。この関数は APIキーなしでローカルで動作し、`ar-SA`、`he-IL`、`fa` などのコードに対して `'rtl'` を返します。いずれの方法でも、CSS 側には前述の論理プロパティとアイコンのルールが別途必要です。

## 重要なポイント

* `lang` と `dir="rtl"` は `<html>` に指定します。`lang` だけでは書字方向は変わりません。また、書字方向の指定に CSS の `direction` プロパティを使うべきではありません。
* Flex の行、グリッドの列、デフォルトのテキスト配置は自動的に反転しますが、`left`、`right`、`margin-left`、`translateX()`、`background-position`、影のオフセットは反転しません。
* 物理プロパティは論理プロパティ (`margin-inline-start`、`inset-inline-end`、`text-align: start`、`border-start-start-radius`) 、または Tailwind の `ms-*`、`ps-*`、`inset-s-*` に置き換えてください。
* 矢印や進捗インジケーターは `:dir(rtl)` で反転させます。ただし、ロゴ、チェックマーク、時計は反転させないでください。また、書字方向が混在する値は `<bdi>` または `dir="ltr"` でラップしてください。
* General Translation の SDK では、[`getLocaleDirection`](/docs/react/nextjs/reference/functions/get-locale-direction) (Next.js のサーバーコードまたは Core ライブラリ) または `useLocaleDirection` (React) を使って `dir` を設定します。

## Related questions

- [多言語アプリのローカライゼーションテスト チェックリスト](https://generaltranslation.com/ja/kb/multilingual-app-launch-checklist.mdx): 本番環境のビルドで、サポート対象のすべてのロケールについて次の9項目をテストしてください：エラー状態やメールを含むジャーニー全体、言語の切り替え、複数形、通貨とタイムゾーン、テキストの伸長、右から左のレイアウト、アクセシビリティ、翻訳の配信、クロール可能な言語別URL。各項目には明確な合格条件を設けてください。
- [疑似ローカライゼーションとは？](https://generaltranslation.com/ja/kb/pseudo-localization.mdx): 疑似ローカライゼーションは、原文をアクセント記号付きで約40%長く水増しし、括弧で囲んだ改変版に置き換えるテストです。これにより、ハードコードされた文字列、切り詰め、フォントの表示崩れ、文の連結といった問題を、翻訳を用意する前に発見できます。Android にはこの用途向けに en-XA と ar-XB の疑似ロケールが用意されています。
- [React で複数形を扱うにはどうすればよいですか？](https://generaltranslation.com/ja/kb/how-to-handle-plurals-in-react.mdx): React で複数形を扱うには、各言語の複数形ごとに文を 1 つずつ用意し、その言語の複数形規則に従って選択します。英語の複数形は 2 種類ですが、ロシア語は 4 種類、アラビア語は 6 種類あるためです。gt-react と gt-next に含まれる General Translation の `<Plural>` コンポーネントに翻訳元の形を渡せば、各対象言語に必要な形は General Translation が生成します。
- [翻訳ファイルなしでi18nは実現できますか？](https://generaltranslation.com/ja/kb/i18n-without-translation-files.mdx): はい。General Translationの gt-react と gt-next ライブラリを使えば、翻訳キーも、言語ごとに管理するJSONファイルもなしで、ReactとNext.jsのi18nを実現できます。文言は `<T>` コンポーネント内のJSXにインラインで記述したままで、gt translate コマンドがコードから得た文脈をもとに、ビルド時に各言語の翻訳を生成します。さらにLocadexを使えば、プルリクエストを通じて翻訳を常に最新の状態に保てます。

## Previous and next questions

- Previous question: [AI翻訳は本番環境のアプリで使えるほど正確か？](https://generaltranslation.com/ja/kb/is-ai-translation-accurate.mdx)
- Next question: [hreflang とは？設定方法を解説](https://generaltranslation.com/ja/kb/what-is-hreflang.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ja/kb/sitemap.md) for every article.
