Next.js(Pages Router)v16で右から左へ(RTL)の言語をサポートする方法

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

問題

ほとんどのウェブレイアウトは左から右へのテキストフローを前提としています。ナビゲーションメニューは左側に配置され、コンテンツは左から右へ読まれ、間隔はmargin-leftpadding-rightなどのプロパティで適用されます。アプリケーションがアラビア語やヘブライ語に翻訳される場合、これらの言語は右から左へ読まれるため、視覚的なレイアウト全体がそれに合わせて反転する必要があります。反転がなければ、ユーザーは視覚的な階層が自然な読み方向と矛盾するという混乱したインターフェースを体験し、ナビゲーションが分かりにくく、アプリケーションが洗練されていないと感じます。

この課題はテキストの配置だけにとどまりません。leftrightmargin-leftpadding-rightなどの物理的なCSSプロパティは、コンテンツフローではなく固定された画面位置に結びついています。テキストの方向が変わっても、これらのプロパティは同じ物理的な端に固定されたままであり、レイアウトが自然に適応することを妨げています。

解決策

ドキュメントのテキスト方向属性を現在のロケールに合わせて設定し、ブラウザがRTL言語のレイアウトフローを自動的に反転できるようにします。物理的なCSSプロパティを、画面位置ではなくコンテンツフローを参照する論理的プロパティに置き換えます。margin-inline-startpadding-inline-endなどの論理的プロパティは、テキスト方向に基づいて自動的に正しい物理的端にマッピングされ、追加のコードや条件付きスタイリングなしでレイアウトを反転させることができます。

このアプローチにより、スタイルを複製したり複雑なロジックを追加したりすることなく、LTRとRTL両方の言語で正しく機能する方向に依存しないレイアウトが作成されます。

ステップ

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

論理的プロパティはテキスト方向に基づいて適応し、margin-inline-startはLTRコンテキストではmargin-leftに、RTLコンテキストではmargin-rightに相当します。ロケールをそのテキスト方向にマッピングするユーティリティ関数を構築します。

export function getDirection(locale: string): "ltr" | "rtl" {
  const rtlLocales = ["ar", "he", "fa", "ur"];
  return rtlLocales.includes(locale) ? "rtl" : "ltr";
}

この関数はRTL言語を識別し、HTMLとCSSで使用するための適切な方向値を返します。

2. ドキュメントの方向属性を設定する

カスタムDocumentクラスはrenderメソッドをオーバーライドして、langやdirなどの属性でHTML要素をカスタマイズできます。現在のロケールに基づいてルートHTML要素にdir属性を設定するカスタムDocumentを作成します。

import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
} from "next/document";
import { getDirection } from "../utils/direction";

class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const initialProps = await Document.getInitialProps(ctx);
    return initialProps;
  }

  render() {
    const locale = this.props.__NEXT_DATA__.locale || "en";
    const dir = getDirection(locale);

    return (
      <Html lang={locale} dir={dir}>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

Next.jsルーターは、localeプロパティを通じて現在アクティブなロケールを提供します。ドキュメントはNext.jsのルーティングデータからロケールを読み取り、対応する方向をHTML要素に適用し、ブラウザレベルのRTLサポートを有効にします。

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

margin-inline-start、padding-inline-startなどの論理プロパティとそのショートハンド形式であるmargin-inline、padding-inlineは、書き込みモードと方向に基づいて自動的に物理プロパティにマッピングされます。コンポーネントのスタイルをフロー相対プロパティを使用するように更新します。

export default function Navigation() {
  return (
    <nav className="nav">
      <ul className="nav-list">
        <li className="nav-item">Home</li>
        <li className="nav-item">About</li>
        <li className="nav-item">Contact</li>
      </ul>
    </nav>
  );
}
.nav {
  padding-inline: 1rem;
  border-inline-start: 4px solid blue;
}

.nav-list {
  display: flex;
  gap: 1rem;
  margin-block: 0;
  padding-inline-start: 0;
}

.nav-item {
  margin-inline-end: 1.5rem;
}

マージン、パディング、インセットの論理プロパティにより、異なる書き込みモード間で要素の配置がより簡単かつ効率的になります。これらのプロパティは、追加のスタイルやメディアクエリなしでRTLコンテキストで自動的に反転します。

4. 位置決めに論理的プロパティを使用する

絶対配置または相対配置された要素では、leftrightinset-inline-startinset-inline-endに置き換えます。

export default function Sidebar() {
  return (
    <aside className="sidebar">
      <button className="close-button">×</button>
      <p>Sidebar content</p>
    </aside>
  );
}
.sidebar {
  position: fixed;
  inset-inline-start: 0;
  inset-block-start: 0;
  inline-size: 250px;
  block-size: 100vh;
  padding-inline: 1rem;
}

.close-button {
  position: absolute;
  inset-inline-end: 0.5rem;
  inset-block-start: 0.5rem;
}

論理的プロパティの方法論では、width プロパティは inline-size に、height は block-size に置き換えられます。これらのプロパティにより、配置された要素が現在のテキスト方向に対して正しい側に表示されることが保証されます。

5. 論理的なテキスト配置を適用する

text-align: lefttext-align: righttext-align: starttext-align: endに置き換えます。

.content {
  text-align: start;
}

.metadata {
  text-align: end;
  margin-inline-start: auto;
}

論理的プロパティは、物理的なビューポートの寸法ではなく、コンテンツの流れに関連するボックスの端を参照します。startendのテキスト配置値は、テキストの方向に自動的に適応し、LTRとRTLの両方の言語でコンテンツを適切に配置します。