Как поддерживать языки с направлением справа налево (RTL) в Next.js (Pages Router) v16
Зеркальные макеты для арабского и иврита
Проблема
Большинство веб-макетов рассчитаны на поток текста слева направо. Меню навигации закреплены слева, контент читается слева направо, а отступы задаются с помощью свойств вроде margin-left или padding-right. Когда приложение переводится на арабский или иврит, эти языки читаются справа налево, и весь визуальный макет должен зеркально отражаться. Без зеркалирования пользователи сталкиваются с дезориентирующим интерфейсом, где визуальная иерархия противоречит их привычному направлению чтения, что делает навигацию запутанной, а само приложение — неаккуратным.
Проблема выходит за рамки выравнивания текста. Физические CSS-свойства, такие как left, right, margin-left и padding-right, привязаны к фиксированным краям экрана, а не к потоку контента. Когда направление текста меняется, эти свойства остаются привязанными к тем же физическим границам, из-за чего макет не может адаптироваться естественным образом.
Решение
Установите атрибут направления текста документа в соответствии с текущей локалью — это позволит браузеру автоматически менять направление макета для RTL-языков. Замените физические CSS-свойства на логические, которые ориентируются на поток контента, а не на положение на экране. Логические свойства, такие как margin-inline-start и padding-inline-end, автоматически соответствуют нужному краю в зависимости от направления текста, позволяя макетам зеркалироваться без дополнительного кода или условий в стилях.
Такой подход позволяет создавать макеты, не зависящие от направления текста, которые корректно работают и для LTR, и для RTL-языков без дублирования стилей и сложной логики.
Шаги
1. Создайте хелпер для определения направления текста по локали
Логические свойства адаптируются в зависимости от направления текста: margin-inline-start эквивалентно margin-left в LTR-контексте и margin-right в RTL-контексте. Напишите утилиту, которая сопоставляет локали с их направлением текста.
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, чтобы настраивать HTML-элемент с такими атрибутами, как lang и dir. Создайте свой Document, чтобы установить атрибут dir на корневом элементе HTML в зависимости от текущей локали.
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, автоматически сопоставляются с физическими свойствами в зависимости от режима письма и направления. Обновите стили компонентов, чтобы использовать flow-relative свойства.
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;
}
Логические свойства для margin, padding и inset упрощают и ускоряют позиционирование элементов в разных режимах письма. Эти свойства автоматически меняются местами в RTL-контекстах без дополнительных стилей или медиазапросов.
4. Используйте логические свойства для позиционирования
Для абсолютно или относительно позиционированных элементов замените left и right на inset-inline-start и inset-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: left и text-align: right на text-align: start и text-align: end.
.content {
text-align: start;
}
.metadata {
text-align: end;
margin-inline-start: auto;
}
Логические свойства описывают стороны блока относительно потока контента, а не физических размеров области просмотра. Значения выравнивания текста start и end автоматически подстраиваются под направление текста, корректно выравнивая контент для языков с направлением слева направо и справа налево.