TanStack Start v1で右から左へ(RTL)の言語をサポートする方法

アラビア語とヘブライ語のためのミラーレイアウト

問題

ほとんどのウェブレイアウトは、テキストが左から右へ流れることを前提としています。ナビゲーションメニューは左側に固定され、サイドバーは左側に表示され、コンテンツは左から右へ読まれます。アラビア語やヘブライ語のような右から左へ読む言語では、視覚的な流れが読む方向と矛盾するため、混乱を招く体験となります。ユーザーはナビゲーションが間違った側にあり、アイコンが不自然な位置にあり、レイアウトが逆に感じられます。適切なRTLサポートがなければ、RTL言語を話すユーザーにとってインターフェースは操作や理解が困難になります。

解決策

現在のロケールに基づいて文書のテキスト方向を動的に設定し、ブラウザがRTL言語のレイアウトを自動的に反転できるようにします。物理的な方向プロパティの代わりにCSSの論理プロパティを使用することで、追加のコードなしで間隔、位置、配置がテキスト方向に適応するようにします。このアプローチにより、レイアウトは方向に依存しなくなります:英語でコンテンツの「開始」に表示されるものは、アラビア語では正しく「開始」(右側)に表示され、ブラウザが変換を処理します。

ステップ

1. ロケールからテキスト方向を決定する

ブラウザの組み込み国際化APIを使用して、指定されたロケールのテキスト方向を返すヘルパー関数を作成します。

export function getTextDirection(locale: string): "ltr" | "rtl" {
  try {
    const localeObj = new Intl.Locale(locale);
    if (
      "getTextInfo" in localeObj &&
      typeof localeObj.getTextInfo === "function"
    ) {
      return localeObj.getTextInfo().direction;
    }
  } catch (e) {
    console.warn(`Could not determine direction for locale: ${locale}`);
  }

  const rtlLocales = ["ar", "he", "fa", "ur"];
  const lang = locale.split("-")[0];
  return rtlLocales.includes(lang) ? "rtl" : "ltr";
}

この関数は利用可能な場合はIntl.Locale.getTextInfo()を使用し、既知のRTL言語のリストにフォールバックします。ロケールに基づいて'ltr'または'rtl'のいずれかを返します。

2. html要素にdir属性を設定する

ルートルートで現在のロケールを取得し、対応する方向をドキュメントの<html>要素に適用します。

import {
  createRootRoute,
  Outlet,
  Scripts,
  HeadContent,
} from "@tanstack/react-router";
import { useIntl } from "react-intl";
import { getTextDirection } from "~/utils/text-direction";

export const Route = createRootRoute({
  component: RootComponent,
});

function RootComponent() {
  return (
    <RootDocument>
      <Outlet />
    </RootDocument>
  );
}

function RootDocument({ children }: { children: React.ReactNode }) {
  const intl = useIntl();
  const dir = getTextDirection(intl.locale);

  return (
    <html lang={intl.locale} dir={dir}>
      <head>
        <HeadContent />
      </head>
      <body>
        {children}
        <Scripts />
      </body>
    </html>
  );
}

<html>要素のdir属性は、RTL言語のレイアウトを反転するようブラウザに指示します。フレックスボックス、グリッド、インラインコンテンツは自動的にミラーリングされます。

3. 物理的なCSSプロパティを論理的プロパティに置き換える

固定された物理的方向ではなく、テキストの方向に応じて反応する論理的プロパティを使用するようにスタイルシートを更新します。

.sidebar {
  padding-inline-start: 1rem;
  margin-inline-end: 2rem;
  border-inline-start: 1px solid #ccc;
}

.icon {
  margin-inline-end: 0.5rem;
}

.card {
  inset-inline-start: 0;
  text-align: start;
}

padding-inline-startのような論理的プロパティは、LTRではpadding-leftに、RTLではpadding-rightにマッピングされます。ブラウザはdir属性に基づいて正しい物理的プロパティを適用するため、スタイルは重複することなく両方向で機能します。

4. 方向に依存しない配置値を使用する

CSSとインラインスタイルで物理的な配置キーワードを論理的なものに置き換えます。

export function Header() {
  return (
    <header
      style={{
        display: "flex",
        justifyContent: "space-between",
        alignItems: "center",
      }}
    >
      <nav style={{ display: "flex", gap: "1rem" }}>
        <a href="/">Home</a>
        <a href="/about">About</a>
      </nav>
      <div style={{ textAlign: "end" }}>
        <button>Menu</button>
      </div>
    </header>
  );
}

'right'の代わりにtextAlign: 'end'を使用することで、テキストが読み取り方向の終わりに揃うことが保証されます。justifyContentalignItemsなどのフレックスボックスプロパティは、dir属性によって設定された方向を自動的に尊重します。