без внешних зависимостей
В этой статье мы шаг за шагом создадим простое, но расширяемое многоязычное приложение Nextjs. В этом примере мы создадим приложение, которое поддерживает как английский, так и испанский языки.
Мы рассмотрим:
- Управление копиями на разных языках
- Создание компонента для отображения правильного языка
- Основные стили/интерполяция/переменные
Настройка приложения Nextjs
Мы будем использовать стандартное приложение Nextjs (если у вас нет опыта работы с Nextjs, рекомендую ознакомиться с его официальной документацией). Наше приложение будет состоять из страницы, пары компонентов и наших переводов.
Окончательный проект будет выглядеть так
src/ pages/ index.tsx components/ Translation.tsx lib/ translate.ts translations/ en.json es.json
Создание словаря
Для начала нам нужно место, где мы можем хранить нашу копию на нескольких языках. Цель здесь состоит в том, чтобы отделить объект копии, который приложение должно отображать, от фактического содержимого этого объекта, которое может быть на любом языке.
Мы будем называть эти объекты ключами перевода, а их содержимое переводом.
Самый простой способ создать этот словарь — использовать объект. В качестве ключей перевода мне нравится использовать следующий формат: {page}_{component}_{name}. Конечно, вы можете адаптировать его к своим потребностям, но мне нравится использовать его в качестве руководства, чтобы упростить работу с ключами в дальнейшем (имейте в виду, что реальное приложение может иметь тысячи ключей).
Начнем с создания нового файла в папке translations
и назовем его en.json.
{ "homepage_header_title": "My Awesome Website", "homepage_header_summary": "This is a test website." }
Давайте повторим процесс, но на этот раз назовем его es.json.
{ "homepage_header_title": "My Sitio Increíble", "homepage_header_summary": "Este es un sitio de prueba." }
Здесь вы должны отметить две вещи:
- Изменилось только содержание перевода, а не ключ перевода (это ключ!)
- Вы можете создать столько файлов, сколько языков вам необходимо поддерживать. Для их именования я использую коды ISO языка, но вы можете использовать все, что их идентифицирует (хотя я бы не рекомендовал этого)
Получение переводов
Для простоты мы будем импортировать переводы на все языки во время сборки и использовать то, что необходимо. Имейте в виду, что это может повлиять на производительность, поскольку мы загружаем переводы, которые не собираемся использовать, но, на мой взгляд, это будет проблемой только при наличии большого количества ключей перевода и языков.
В нашем случае мы создадим очень простую логику в translate.ts.
import en_translations from 'translations/en.json' import es_translations from 'translations/en.json' function getTranslations(lang: 'en'|'es') { if (lang === 'en') { return en_translations; } if (lang === 'es') { return es_translations; } }
Перевод контента
С нашим встроенным словарем перевод контента так же прост, как получение контента перевода, связанного с заданным ключом. Давайте создадим нашу функцию перевода в lib/translate.ts.
export default function translate(key: string): string { try { const translations = getTranslations('en') if (translations[key]) { return translations[key] } elst { throw 'Key not found' } } catch(error){ throw `Unable to get trasnslation content for ${key}` } }
Я думаю, что это помогает бросать в случае, если мы не можем найти словарь или ключ, потому что по крайней мере в 99% случаев отсутствие словаря языка или отсутствие ключа — это то, о чем приложение должно знать и правильно обрабатывать. Через мгновение мы воспользуемся машинописным текстом, чтобы отловить большинство этих ошибок во время сборки.
Создание компонента перевода
Метод translate выполняет свою работу, но мы можем пойти еще дальше и максимально упростить перевод содержимого. Давайте создадим наш компонент в файле components/Translation.tsx.
import translate from '../lib/translate' export default function Translation(key: string) { return <>{translate(key)}</> }
Создание нашего тестового веб-сайта
Давайте создадим очень простой веб-сайт, чтобы продемонстрировать, как использовать эту новую функциональность.
Давайте просто создадим страницу в pages/index.tsx.
export function default Homepage() { return ( <div> <header> <h1> <Translation key="homepage_header_title" /> </h1> <h2> <Translation key="homepage_header_summary" /> </h2> </header> <main>{/* <!--This Content here --> */}</main> </div> ) }
Вот и все! Очень легкий и в то же время мощный механизм для обработки нескольких языков в вашем приложении Nextjs (или любом другом приложении, основанном на реакции).
Отображение динамического содержимого
Давайте теперь скажем, что приветствовать ваших пользователей, отображая их имя. Конечно, нереально иметь ключ перевода для каждого существующего пользователя в вашей базе данных. Вместо этого мы можем справиться с этим сценарием, немного изменив наш код.
Во-первых, давайте начнем с изменения наших переводов. Для простоты сделаем это только в нашей английской версии (но в продакшене это надо делать на всех доступных языках)
{ "homepage_header_title": "Hello, {user_name}!", }
Как видите, мы добавили заполнитель вместо настоящего имени. Теперь давайте изменим наш метод перевода, чтобы он обрабатывал эти заполнители.
export default function translate(key: string, params?: {[key]: string: string}): string { try { const dictionary = getTranslations('en') if (translations[key]) { let translation = translations[key] Object.keys(params).forEach((key) => { translation = translation.replace(`{${key}}`, params[key]) }) } elst { throw 'Key not found' } } catch(error){ throw `Unable to get trasnslation content for ${key}` } }
Логика по-прежнему довольно проста: метод может получить объект с данными, и для каждого ключа мы просто заменяем его содержимое в переводе.
Последнее изменение, которое нам нужно сделать сейчас, это обновить наш компонент, чтобы он принимал эти параметры в качестве реквизита.
import translate from '../lib/translate' export default function Translation(key: string, params?: {[key:string]: string}) { return <>{translate(key, params)}</> }
Давайте соберем все вместе сейчас
export function default Homepage() { return ( <div> <header> <h1> <Translation key="homepage_header_title" params={{user_name: 'John Doe'}} /> </h1> </header> <main>{/* <!--This Content here --> */}</main> </div> ) }
Ну вот! Хотя этот пример очень прост, тот же метод можно использовать для интерполяции html-тегов для стилей, html-тегов для ссылок, чисел и так далее!
Подведение итогов
Это простой, но функциональный подход к работе с несколькими языками в вашем приложении без больших зависимостей.
В будущем я расскажу, как расширить его для поддержки более сложных сценариев в реальных приложениях, например:
- управление валютами
- управление множественным числом
- использование Typescript для проверки того, что используются только существующие ключи
Дополнительные материалы на PlainEnglish.io.
Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .
Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.