如何在 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 属性
在你的根路由中,获取当前 locale,并将相应的方向属性应用到文档的 <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 这样的逻辑属性,在 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>
);
}
使用 textAlign: 'end' 替代 'right',可确保文本根据阅读方向对齐到末尾。像 justifyContent 和 alignItems 这样的 Flexbox 属性会自动遵循 dir 属性设置的方向。