Переключение языков

Fresh — современный фреймворк на базе Deno, ориентированный на рендеринг сервером и оптимизацию фронтенда с минимальной загрузкой клиентской стороны. Одной из часто востребованных задач является реализация мультиязычности и переключение языков. Этот процесс требует внимательного подхода к маршрутизации, хранению локализации и интеграции с компонентами.


Архитектура локализации

В Fresh рекомендуется использовать серверный рендеринг для выбора языка. Основные принципы:

  • Определение языка запроса Язык можно получать из заголовков Accept-Language, cookie или параметров URL. Например:
const locale = req.headers.get("accept-language")?.split(",")[0] ?? "en";
  • Хранение переводов Наиболее удобно использовать отдельные JSON-файлы для каждой локали. Структура:
/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] в маршруте для автоматической генерации страниц для каждой локали.


Сохранение выбранного языка

Для постоянного сохранения выбранного пользователем языка применяются:

  • Cookie:
const response = new Response();
response.headers.set("Set-Cookie", `lang=${lang}; Path=/; HttpOnly`);
  • LocalStorage на клиенте: подходит для SPA-логики внутри Fresh, когда необходимо менять язык без перезагрузки страницы.

Интеграция с компонентами

Компоненты 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" } });

Так уменьшается объем памяти и ускоряется рендеринг, особенно при большом количестве языков.


Рекомендации по SEO и ссылкам

Для многоязычных сайтов важно:

  • Указывать <link rel="alternate" hreflang="xx"> в <head>.
  • Структура URL должна отражать локаль (/ru/page, /en/page), что упрощает индексацию поисковиками.
  • При генерации ссылок использовать параметр локали, чтобы все внутренние ссылки сохраняли выбранный язык.

Обработка fallback и отсутствующих переводов

Если в выбранной локали нет перевода, применяют 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 строится на сочетании серверного рендеринга, динамических маршрутов и корректного хранения локализации. Такой подход обеспечивает высокую производительность и удобство масштабирования мультиязычных приложений.