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 언어에 대해 레이아웃을 반전시키도록 지시합니다. Flexbox, 그리드 및 인라인 콘텐츠가 자동으로 미러링됩니다.

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와 같은 Flexbox 속성은 dir 속성에 의해 설정된 방향을 자동으로 존중합니다.