Fresh — это современный фреймворк на JavaScript/TypeScript, ориентированный на серверный рендеринг и высокую производительность. Одной из ключевых задач при разработке многоязычных приложений является организация системы переводов. В Fresh это достигается с помощью серверных модулей и асинхронной загрузки ресурсов.
Переводы в Fresh обычно хранятся в виде JSON-файлов, где ключи представляют уникальные идентификаторы строк, а значения — переведённый текст. Типичная структура проекта может выглядеть так:
locales/
en.json
ru.json
fr.json
Пример содержимого ru.json:
{
"greeting": "Привет",
"farewell": "До свидания",
"welcome_message": "Добро пожаловать на наш сайт"
}
Ключи должны быть уникальными и интуитивно понятными, чтобы облегчить поддержку и расширение проекта.
Для оптимизации загрузки страниц важно загружать переводы только для текущего языка. Fresh позволяет использовать динамический импорт JSON-файлов на сервере.
Пример функции для загрузки переводов:
import type { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(req) {
const url = new URL(req.url);
const lang = url.searchParams.get("lang") || "en";
let translations;
try {
translations = await import(`../locales/${lang}.json`);
} catch {
translations = await import("../locales/en.json");
}
return new Response(JSON.stringify(translations.default), {
headers: { "Content-Type": "application/json" },
});
},
};
Особое внимание уделяется обработке ошибок: если запрашиваемый язык не найден, используется fallback на английский. Это предотвращает появление пустых строк и сбоев интерфейса.
Fresh использует Island Architecture, где часть компонентов рендерится на клиенте. Для передачи переводов из сервера в Island-компоненты можно использовать пропсы:
// components/Greeting.tsx
interface GreetingProps {
translations: Record<string, string>;
}
export default function Greeting({ translations }: GreetingProps) {
return <h1>{translations.greeting}</h1>;
}
На серверной странице:
import Greeting from "../components/Greeting.tsx";
import enTranslations from "../locales/en.json";
export default function Home() {
return <Greeting translations={enTranslations} />;
}
Для динамических страниц язык можно передавать через URL или cookie, загружая соответствующий JSON на сервере.
При работе с переводами критически важно минимизировать повторные запросы к файловой системе. В Fresh можно использовать WeakMap или глобальные объекты для кэширования JSON:
const cache = new Map<string, Record<string, string>>();
async function loadTranslations(lang: string) {
if (cache.has(lang)) return cache.get(lang)!;
const translations = await import(`../locales/${lang}.json`);
cache.set(lang, translations.default);
return translations.default;
}
Такой подход снижает нагрузку на сервер и ускоряет отклик страницы.
Иногда требуется выводить контент на разных языках одновременно. В этом случае каждое отдельное выражение загружается через универсальную функцию:
async function t(key: string, lang: string) {
const translations = await loadTranslations(lang);
return translations[key] || key;
}
Использование этой функции позволяет формировать многоязычные интерфейсы без дублирования логики рендеринга.
Fresh не навязывает способ определения языка, но чаще всего используется:
Accept-Language запроса?lang=ru)Пример обработки через заголовок:
const acceptLang = req.headers.get("accept-language")?.split(",")[0] || "en";
const translations = await loadTranslations(acceptLang);
Это обеспечивает удобную интеграцию с браузерами и повышает UX многоязычных приложений.
Для TypeScript рекомендуется описывать типы переводов:
interface Translations {
greeting: string;
farewell: string;
welcome_message: string;
}
Затем JSON можно импортировать с типами:
import ruTranslations from "../locales/ru.json" assert { type: "json" };
const translations: Translations = ruTranslations;
Это предотвращает ошибки при использовании ключей и упрощает рефакторинг.
Такой подход позволяет создавать масштабируемые, производительные и легко поддерживаемые многоязычные приложения на Fresh.