Как поддерживать языки с письмом справа налево (RTL) в TanStack Start v1
Отражение макетов для арабского и иврита
Проблема
Большинство веб-макетов предполагают, что текст читается слева направо. Навигационные меню располагаются слева, боковые панели появляются слева, а контент читается слева направо. Для языков, таких как арабский и иврит, которые читаются справа налево, это создает дезориентирующий опыт, когда визуальный поток противоречит направлению чтения. Пользователи видят навигацию с неправильной стороны, значки расположены неудобно, а макеты кажутся перевернутыми. Без надлежащей поддержки 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(`Не удалось определить направление для локали: ${locale}`);
}
const rtlLocales = ["ar", "he", "fa", "ur"];
const lang = locale.split("-")[0];
return rtlLocales.includes(lang) ? "rtl" : "ltr";
}
Эта функция использует Intl.Locale.getTextInfo(), если он доступен, и в противном случае опирается на список известных языков с направлением чтения справа налево. Она возвращает либо 'ltr', либо 'rtl' в зависимости от локали.
2. Установите атрибут dir на элементе html
В вашем корневом маршруте получите текущую локаль и примените соответствующее направление к элементу <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>
);
}
Атрибут dir на элементе <html> сообщает браузеру, что нужно изменить макет для языков с направлением текста справа налево (RTL). Flexbox, grid и встроенное содержимое автоматически зеркалируются.
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, соответствуют padding-left в LTR и padding-right в RTL. Браузер применяет правильное физическое свойство на основе атрибута 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>
);
}
Использование textAlign: 'end' вместо 'right' гарантирует, что текст выравнивается по концу направления чтения. Свойства Flexbox, такие как justifyContent и alignItems, автоматически учитывают направление, установленное атрибутом dir.