Создание многоязычной платформы калькуляторов с Next.js 15
Подробный анализ создания высокопроизводительной многоязычной платформы калькуляторов с Next.js 15, React 19 и next-intl, включая реализацию интернационализации, SEO-оптимизацию и оптимизацию производительности.
Привет, коллеги-разработчики! 👋
Если вы когда-либо пытались создать веб-сайт, который должен работать для пользователей по всему миру, вы знаете, насколько это может быть сложно. Недавно я отправился в путешествие по созданию Free Calculators - платформы, которая обслуживает пользователей на 12 разных языках, и позвольте сказать, это было настоящее приключение!
В этом посте я хочу поделиться реальной историей создания этой многоязычной платформы калькуляторов. Не просто версией "вот что мы сделали", а версией "вот что действительно произошло, что пошло не так, и чему мы научились". Потому что, честно говоря, создание для глобальной аудитории - это не прогулка в парке.
Почему Мы Создали Это (И Почему Это Важно)
Представьте себе: Вы создаете приложение-калькулятор. Довольно просто, верно? Но затем вы понимаете, что кому-то в Японии может понадобиться японская версия, кому-то в Бразилии может понравиться португальский, а ваши немецкие пользователи определенно хотят, чтобы их числа форматировались по-немецки (1.234,56 вместо 1,234.56). Внезапно ваш простой калькулятор становится сложным международным проектом.
Именно это произошло с нами. Мы начали с базового калькулятора и в итоге создали платформу, которая обслуживает пользователей разных культур, языков и форматов чисел. И знаете что? Это стал одним из самых полезных проектов, над которыми я работал.
Решения Технологического Стека: Почему Мы Выбрали Эти Инструменты
Почему Next.js 15?
Честно говоря, когда Next.js 15 впервые вышел, мы немного колебались. В конце концов, "если не сломалось, не чини", верно? Но когда мы увидели новый App Router и интеграцию React 19, мы решили сделать прыжок. Это оказалось мудрым выбором.
// Вот как мы настроили нашу маршрутизацию
export const routing = defineRouting({
locales: [
'en',
'zh',
'ja',
'ko',
'fr',
'de',
'es',
'pt',
'ru',
'ar',
'hi',
'it',
],
defaultLocale: 'en',
localePrefix: 'as-needed', // Эта настройка спасла нас от множества головных болей
localeDetection: true,
});
React 19: Стоит Ли Обновляться?
Короткий ответ: Абсолютно! Длинный ответ: Мы столкнулись с некоторыми проблемами совместимости, но улучшения производительности новых функций значительны. Особенно улучшения в Suspense и конкурентных функциях сделали наш пользовательский опыт намного лучше.
TypeScript: Наш Спаситель
Я не могу представить управление файлами переводов для 12 языков без TypeScript. Безопасность типов дала нам уверенность при рефакторинге, особенно при работе со сложными вложенными объектами переводов.
// Вот как мы определяем наши типы переводов
interface TranslationKeys {
nav: {
siteName: string;
home: string;
calculators: string;
};
common: {
calculate: string;
clear: string;
result: string;
};
}
Реализация Интернационализации: Реальные Вызовы и Решения
Вызов 1: Сложность Определения Языка
Вы можете подумать: "Определение языка? Легко!" Но это не так. Пользователи могут заходить с разных устройств, использовать VPN или иметь специфические языковые предпочтения. Мы потратили много времени на совершенствование нашей логики определения языка.
// Наша конфигурация middleware
import createMiddleware from 'next-intl/middleware';
import { routing } from '@/i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)',
};
Вызов 2: Управление Файлами Переводов
Управление файлами переводов для 12 языков - это как управление 12 разными проектами. Мы разработали несколько скриптов автоматизации, чтобы помочь нам:
// Наш скрипт управления переводами
const addMissingTranslations = async () => {
const baseTranslations = await loadTranslations('en');
const allLocales = await getAllLocales();
for (const locale of allLocales) {
const existingTranslations = await loadTranslations(locale);
const missingKeys = findMissingKeys(baseTranslations, existingTranslations);
if (missingKeys.length > 0) {
console.log(`Missing keys in ${locale}:`, missingKeys);
await addMissingKeys(locale, missingKeys);
}
}
};
Вызов 3: Ловушка Форматирования Чисел
Это был один из самых интересных вызовов. Знаете ли вы, что в разных странах разные форматы чисел? Германия использует запятые как разделители десятичных знаков, США используют точки. Мы столкнулись со множеством проблем при работе с валютными расчетами, пока не реализовали правильное локализованное форматирование.
SEO Оптимизация: Заставляем Поисковые Системы Любить Нас
Сила Статической Генерации Сайтов
Мы решили использовать статическую генерацию сайтов (SSG), что может быть лучшим решением, которое мы когда-либо принимали. Страницы не только молниеносно быстрые, но и результаты SEO также отличные.
// Генерация статических страниц для каждого языка
export const dynamic = 'force-static';
export function generateStaticParams() {
return routing.locales
.filter(locale => locale !== routing.defaultLocale)
.map(locale => ({ locale }));
}
Искусство Генерации Метаданных
Генерация правильных метаданных для каждого языка требует некоторой тонкости. Мы разработали систему для автоматической генерации SEO-дружественных метаданных:
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Home' });
return {
title: t('title'),
description: t('description'),
openGraph: {
title: t('title'),
description: t('description'),
locale: locale,
},
};
}
Оптимизация Производительности: Делаем Пользовательский Опыт Плавным
Важность Оптимизации Изображений
Мы потратили много времени на оптимизацию загрузки изображений. Принятие форматов AVIF и WebP улучшило скорость загрузки наших страниц на 40%.
// Конфигурация изображений в next.config.ts
images: {
formats: ['image/avif', 'image/webp'],
domains: ['freecalculators.app'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
}
Стратегия Кэширования: Наше Секретное Оружие
Реализация правильной стратегии кэширования потребовала некоторых проб и ошибок. В итоге мы пришли к многоуровневому подходу кэширования:
async headers() {
return [
// Сильное кэширование для статических ресурсов
{
source: '/(.*).(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)',
headers: [{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
}],
},
// Согласованное кэширование для страниц
{
source: '/(.*)',
headers: [{
key: 'Cache-Control',
value: 'public, max-age=0, must-revalidate',
}],
},
];
}
Дизайн Пользовательского Опыта: Заставляем Всех Чувствовать Себя Желанными
Вызов Адаптивного Дизайна
Обеспечение того, чтобы наши калькуляторы идеально работали на всех устройствах, является постоянным вызовом. Мы приняли подход mobile-first:
// Адаптивный макет mobile-first
<div className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-1/2">
{/* Интерфейс калькулятора */}
</div>
<div className="w-full md:w-1/2">
{/* Отображение результатов */}
</div>
</div>
Доступность: Больше Чем Просто Соответствие
Мы поняли, что доступность - это не просто правовое требование - это правильное дело. Мы добавили соответствующие ARIA-метки к каждому интерактивному элементу:
<button
aria-label={t('common.calculate')}
aria-describedby="calculator-help"
className="bg-blue-600 text-white px-4 py-2 rounded"
>
{t('common.calculate')}
</button>
Инструментарий Разработки: Делаем Разработку Более Эффективной
Мониторинг Ошибок: Наша Сеть Безопасности
Мы интегрировали Sentry для мониторинга ошибок, что помогло нам обнаружить множество проблем, которых мы никогда не ожидали:
export default withSentryConfig(withNextIntl(nextConfig), {
org: 'my-projects-pd',
project: 'free-calculators',
widenClientFileUpload: true,
disableLogger: true,
automaticVercelMonitors: true,
});
Автоматизированное Управление Переводами
Мы разработали несколько скриптов для управления файлами переводов, и эти скрипты теперь являются неотъемлемой частью нашего рабочего процесса.
Уроки, Которые Мы Изучили
1. Думайте Об Интернационализации С Самого Начала
Если вы думаете, что вам может понадобиться многоязычная поддержка, проектируйте это с самого начала. Добавление интернационализации позже намного сложнее, чем проектирование ее с самого начала.
2. Пользовательский Опыт Превыше Технического Показухи
Мы потратили много времени на оптимизацию производительности, но в конечном итоге пользователи больше всего заботятся о том, интуитивен ли интерфейс и точны ли вычисления.
3. Автоматизация - Ключ
Ручное управление переводами для 12 языков нереально. Скрипты автоматизации - это спасательный круг нашего проекта.
4. Тестируйте, Тестируйте и Тестируйте Снова
Разные языки имеют разную длину текста, разные форматы чисел и разные культурные ожидания. Тщательное тестирование обязательно.
Будущие Планы
Мы рассматриваем возможность добавления больше языков, улучшения нашего процесса управления переводами и изучения возможностей ИИ-ассистированного перевода. Но самое главное, мы хотим продолжать предоставлять пользователям лучший опыт калькулятора.
Заключение
Создание многоязычного веб-приложения - это вызов, но также очень полезный опыт. Благодаря умным технологическим решениям и множеству тестов, мы успешно создали платформу, которая действительно обслуживает глобальных пользователей.
Если вы рассматриваете создание многоязычного приложения, я надеюсь, что эта статья даст вам некоторое вдохновение. Помните, каждый вызов - это возможность обучения, и каждая ошибка - это ступенька к успеху.
Ссылка на Проект: https://www.freecalculators.app Технологический Стек: Next.js 15, React 19, TypeScript, Tailwind CSS, next-intl