Fresh — современный фреймворк на базе Deno, ориентированный на рендеринг сервером и оптимизацию фронтенда с минимальной загрузкой клиентской стороны. Одной из часто востребованных задач является реализация мультиязычности и переключение языков. Этот процесс требует внимательного подхода к маршрутизации, хранению локализации и интеграции с компонентами.
В Fresh рекомендуется использовать серверный рендеринг для выбора языка. Основные принципы:
Accept-Language, cookie или параметров URL.
Например:const locale = req.headers.get("accept-language")?.split(",")[0] ?? "en";
/locales
en.json
ru.json
es.json
Каждый файл содержит ключи и строки перевода:
{
"greeting": "Привет",
"farewell": "До свидания"
}
import en from "../locales/en.json" assert { type: "json" };
import ru from "../locales/ru.json" assert { type: "json" };
const translations = { en, ru };
const t = translations[locale];
Fresh использует маршруты в виде файлов в папке routes.
Для поддержки разных языков создается структура с параметром локали:
routes/
[lang]/
index.tsx
Пример кода для маршрута с динамической локалью:
import { Handlers, PageProps } from "$fresh/server.ts";
import en from "../. ./locales/en.json" assert { type: "json" };
import ru from "../. ./locales/ru.json" assert { type: "json" };
const translations = { en, ru };
export const handler: Handlers = {
GET(req, ctx) {
const lang = ctx.params.lang;
const t = translations[lang] ?? translations.en;
return ctx.render({ t });
},
};
export default function Page({ data }: PageProps) {
const { t } = data;
return (
<div>
<h1>{t.greeting}</h1>
<p>{t.farewell}</p>
</div>
);
}
Ключевой момент — использование параметра [lang] в
маршруте для автоматической генерации страниц для каждой локали.
Для постоянного сохранения выбранного пользователем языка применяются:
const response = new Response();
response.headers.set("Set-Cookie", `lang=${lang}; Path=/; HttpOnly`);
Компоненты Fresh могут принимать объект переводов через пропсы. Это позволяет поддерживать мультиязычность без глобальных состояний:
function Header({ t }: { t: Record<string, string> }) {
return (
<header>
<nav>
<a href={`/${t.lang}/home`}>{t.home}</a>
<a href={`/${t.lang}/about`}>{t.about}</a>
</nav>
</header>
);
}
Для больших проектов часто создается утилита:
export function useTranslation(lang: string) {
const translations = { en, ru };
return translations[lang] ?? translations.en;
}
Чтобы не грузить все локали сразу, можно применять динамический
import:
const t = await import(`../locales/${lang}.json`, { assert: { type: "json" } });
Так уменьшается объем памяти и ускоряется рендеринг, особенно при большом количестве языков.
Для многоязычных сайтов важно:
<link rel="alternate" hreflang="xx"> в
<head>./ru/page,
/en/page), что упрощает индексацию поисковиками.Если в выбранной локали нет перевода, применяют fallback:
const t = translations[lang] ?? translations.en;
Можно также создавать утилиту для логирования отсутствующих ключей, что облегчает поддержку больших проектов.
Даже при серверном рендеринге удобно предоставлять пользователю селектор языка:
<select
value={lang}
onCha nge={(e) => window.location.href = `/${e.target.value}${window.location.pathname}`}
>
<option value="en">English</option>
<option value="ru">Русский</option>
</select>
Этот подход совместим с маршрутизацией Fresh и сохраняет SEO-дружелюбную структуру URL.
Переключение языков во Fresh строится на сочетании серверного рендеринга, динамических маршрутов и корректного хранения локализации. Такой подход обеспечивает высокую производительность и удобство масштабирования мультиязычных приложений.