Автор оригинала: FreeCodeCamp Community Member.
Этот пост будет использовать racted-intl
Чтобы помочь вам пойти от Create-React-App
Чтобы создать каркас для завершенного, переведенного веб-приложения!
Я совершил код, когда я написал этот пост, так что вы сможете посмотреть мою историю Compare, чтобы легко увидеть, как мой код превратился от начала до конца.
Что такое интернационализация?
Учитывая, что вы решили нажать на ссылку на этот пост, скорее всего, у вас есть некоторые идеи, какая интернационализация (I18N). Сразу сразу W3 сайт :
Как разработчик, вы хотите, чтобы ваш контент был легко читаемым и пригодным для использования всеми людьми по всему миру. Я думаю, что все согласны с этим. Но я знаю, что ты думаешь:
«Разработка веб-приложения для людей моей собственной культуры/региона/язык уже сложно! У меня нет времени или усилий для I18N!»
У тебя уже есть линго вниз, я вижу. Надеюсь, этот пост поможет вам реализовать, что настройка I18N для вашего проекта не так сложно или поглощение времени, как кажется.
Какой реагирование и не делает и не делает
Если вы новичок в I18N, у вас могут быть некоторые мысли о том, что вы думаете, что библиотека, такая как racted-intl
должен и не должен быть в состоянии сделать.
Оно делает:
- Помочь вам объединить все ваше рассеянное содержание, так что его можно легко перевести позже
- Помочь вам справиться с переводом текста в дополнение к датам, номерам и так далее
- Предоставьте простой способ переводов в свой приложение
Это не:
- Перевести свой контент для вас
- Расскажите, как узнать, что хочет локаль
- Исправить эту неродную ошибку, с которой вы имели дело за последние пару часов (Bummer, верно?)
Хорошо, так давайте поправаемся на это!
Настройка примера проекта
$ npx create-react-app i18n-example
Я собираюсь добавить React Router, чтобы показать, как racted-intl
Работает с несколькими страницами.
$ cd i18n-example && npm install react-router-dom
Мое примерное приложение будет иметь три компонента реагирования: одна главная страница, одна подстрапка и один компонент, который импортируется в подстраницу. Смотрите файловую структуру и страницы ниже:
/src /components Weather.js /pages Home.js Day.js
Состояние проекта до этого момента найдет этот момент здесь Отказ
Настройка React-INTL
Теперь веселье начинается. Мы установим racted-intl
и добраться до работы!
$ npm install react-intl
Главная цель позади racted-intl
Состоит в том, чтобы позволить поддержку I18N, минимизируя влияние на ваш обычный поток кодирования. Конечно, у вас есть контент во многих местах по всему вашему приложению. У вас есть текст, цифры и даты в пунктах, таблицах и заголовках.
Что бы вы сделали, если бы вам пришлось построить библиотеку I18N? Ну, у вас есть эти биты и кусочки контента по всему веб-приложению. И вы хотите, чтобы все было легко переведено. Если вы собираетесь дать вашему контенту переводчику, вы бы не дали им свой код и не скажу «удачи, добраться до работы».
Вы хотели бы найти способ поставить все свой контент в один файл, а затем дать им один файл. Они переведут его на другой язык, говорят с английского на испанское и дайте вам один файл со всем испанским контентом.
Ок, отлично. Таким образом, вы сделали это, но теперь вы должны взять испанский контент в том, что один файл и повторно распределите его обратно в исходное место. Как бы вы сделали это программно? Возможно, вы назначаете идентификаторы каждому биту содержимого, чтобы вы не теряете отслеживание исходного местоположения каждого долога контента.
И это в значительной степени!
Первый шаг – обернуть приложение в
Теперь вам нужно определить контент для racted-intl
Это в конечном итоге будет переведено. На главной странице моего приложения у меня есть следующий абзац:
It is a beautiful day outside.
Мне нужно сказать racted-intl
Что это содержимое, которое я хочу перевести и дать ему удостоверение личности, чтобы он мог отслеживать этот контент и его оригинальное местоположение:
По умолчанию текст будет выводится в Если мы хотим, чтобы это осталось абзацем.
Теперь я сделаю это для всего контента в моем веб-приложении.
Состояние проекта до сих пор можно найти здесь Отказ
Добавление Babel-Plugin-React-Intl
Теперь, когда у нас все настроено, вам может быть интересно, как мы можем легко содержать все это содержание в один файл. Однако для целей отладки может быть полезно иметь отдельные файлы JSON для каждого компонента реагирования. Угадайте, для этого есть плагин Babel!
$ npm install babel-plugin-react-intl
Этот плагин сделает копию вашего SRC
Справочник, но вместо того, чтобы иметь ваши файлы recopt Component, он будет иметь файлы JSON с содержимым сообщения и идентификатор. Один для каждого компонентного файла в вашем SRC
каталог. Это сделает это, когда вы запустите NPM запустить сборку
Отказ
Теперь нам нужно извлечь из создания-реактивного приложения, чтобы мы могли добавить наш новый плагин в нашу конфигурацию Babel. Обязательно выполните любые изменения, а затем выполните:
$ npm run eject
Теперь нам нужно добавить .babelrc
Файл в нашем корневом проекте со следующим содержимым:
{ "presets":["react-app"], "plugins": [ ["react-intl", { "messagesDir": "./public/messages/" }] ] }
Теперь, когда Babel может использовать наш причудливый новый плагин, который мы только что добавили, мы можем перейти на наш следующий шаг: генерируя эти файлы JSON.
$ npm run build
Как только вы запускаете это, вы должны заметить, что у вас есть Публичные/сообщения/SRC
Каталог, который, кажется, является клоном вашего оригинала SRC
Справочник, кроме всех ваших компонентных файлов на самом деле загружает файлы JSON.
/messages /src /components Weather.json /pages Home.json Day.json
Теперь давайте посмотрим содержимое одного из них, Home.json:
[ { "id": "Home.header", "defaultMessage": "Hello, world!" }, { "id": "Home.dayMessage", "defaultMessage": "It's a beautiful day outside." }, { "id": "Home.dayLink", "defaultMessage": "Click here to find out why!" } ]
Состояние проекта до сих пор можно найти здесь Отказ
Объединение файлов JSON
Это сделало только то, что мы думали, это бы. Может быть полезно иметь наш контент, организованный в этой структуре, но в конечном итоге мы захочу, чтобы он был в одном файле, и нам нужно, чтобы он включил какие-либо переводы, которые мы сделаем.
Теперь нам нужно сделать сценарий, который делает это для нас. К счастью, люди на racted-intl
дал нам хорошую отправной точкой с Этот скрипт Отказ
import * as fs from "fs"; import { sync as globSync } from "glob"; import { sync as mkdirpSync } from "mkdirp"; import last from "lodash/last"; const MESSAGES_PATTERN = "./public/messages/**/*.json"; const LANG_DIR = "./public/locales/"; const LANG_PATTERN = "./public/locales/*.json"; // Try to delete current json files from public/locales try { fs.unlinkSync("./public/locales/data.json"); } catch (error) { console.log(error); } // Merge translated json files (es.json, fr.json, etc) into one object // so that they can be merged with the eggregated "en" object below const mergedTranslations = globSync(LANG_PATTERN) .map(filename => { const locale = last(filename.split("/")).split(".json")[0]; return { [locale]: JSON.parse(fs.readFileSync(filename, "utf8")) }; }) .reduce((acc, localeObj) => { return { ...acc, ...localeObj }; }, {}); // Aggregates the default messages that were extracted from the example app's // React components via the React Intl Babel plugin. An error will be thrown if // there are messages in different components that use the same `id`. The result // is a flat collection of `id: message` pairs for the app's default locale. const defaultMessages = globSync(MESSAGES_PATTERN) .map(filename => fs.readFileSync(filename, "utf8")) .map(file => JSON.parse(file)) .reduce((collection, descriptors) => { descriptors.forEach(({ id, defaultMessage }) => { if (collection.hasOwnProperty(id)) { throw new Error(`Duplicate message id: ${id}`); } collection[id] = defaultMessage; }); return collection; }, {}); // Create a new directory that we want to write the aggregate messages to mkdirpSync(LANG_DIR); // Merge aggregated default messages with the translated json files and // write the messages to this directory fs.writeFileSync( `${LANG_DIR}data.json`, JSON.stringify({ en: defaultMessages, ...mergedTranslations }, null, 2) );
Нам нужно будет изменить его немного, потому что, как оно выделяется, этот сценарий будет генерировать поддельный перевод. Мы не хотим этого, потому что это не практично.
Мы лучше, чем это! Мы хотим, чтобы это прочитать настоящий перевод!
Сценарий, который мы будем использовать, чтобы сделать это ниже:
Нам нужно будет сохранить этот файл в нашем Сценарии
каталог, а затем отредактируйте Package.json
Так что это на самом деле запускает сценарий.
Прежде чем мы сделаем это, нам нужно будет делать пару вещей, так что наш ESNext Code можно понять. Сначала нам нужно будет добавить Babel-Cli
Чтобы убедиться, что скрипт транспортируется.
$ npm install --save-dev babel-cli
Далее нам нужно добавить env
предустановите наше .babelrc
Так что это выглядит так:
{ "presets":["react-app", "env"], "plugins": [ ["react-intl", { "messagesDir": "./public/messages/" }] ] }
Наконец, нам нужно редактировать наши Package.json
Так что это запускает наш скрипт:
{... "scripts": { "build:langs": "NODE_ENV='production' babel-node scripts/mergeMessages.js", "build": "npm run build:langs && node scripts/build.js", ... }, ... }
Обратите внимание, что мы запускаем сценарий Mergemessages до NPM запустить сборку
Отказ Это потому, что мы хотим создать наш окончательный data.json
Файл в /публики
каталог до нашего сценария сборки копирует его на /построить
Отказ
Хорошо, теперь, когда мы бежим NPM запустить сборку
Мы должны увидеть build/locales/data.json
который сочетает в себе все наши файлы JSON в одну.
Состояние проекта до сих пор можно найти здесь Отказ
Время начать перевод
Теперь, когда мы сделали скрипт, который состоит из наших сообщений по умолчанию и наши переводы в один файл, давайте сделаем несколько переводов! Для этого примера мы переведем на испанский язык. Наш скрипт, который мы только что создали, будет читать все * .json
Файлы из /public/locales
Поэтому нам нужно будет назвать наш новый файл перевода /public/locales/es.json
и добавьте содержимое ниже:
{ "Weather.message": "¡Porque es soleado!", "Day.homeLink": "Regresar a inicio", "Home.header": "¡Hola Mundo!", "Home.dayMessage": "Es un hermoso día afuera.", "Home.dayLink": "¡Haz clic aquí para averiguar por qué!" }
Теперь, когда мы бежим NPM запустить сборку
Наши Mergemessages Script создаст data.json
Файл в /public/locales
и тогда он будет скопирован на /build/locales
Отказ Наш финал data.json
Файл будет выглядеть так:
{ "en": { "Weather.message": "Because it is sunny!", "Day.homeLink": "Go back home", "Home.header": "Hello, world!", "Home.dayMessage": "It's a beautiful day outside.", "Home.dayLink": "Click here to find out why!" }, "es": { "Weather.message": "¡Porque es soleado!", "Day.homeLink": "Regresar a inicio", "Home.header": "¡Hola Mundo!", "Home.dayMessage": "Es un hermoso día afuera.", "Home.dayLink": "¡Haz clic aquí para averiguar por qué!" } }
Мы почти там! Последний шаг – динамически загружать испанскую версию текста, если настройки браузера пользователя испанны. Нам нужно редактировать index.js
Чтобы прочитать настройки языка браузера, а затем дайте эту информацию вместе с правильными переводами на
Наш финал index.js
Похоже, это выглядит:
import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./App"; import registerServiceWorker from "./registerServiceWorker"; import { BrowserRouter } from "react-router-dom"; import { IntlProvider, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import es from "react-intl/locale-data/es"; import localeData from "./../build/locales/data.json"; addLocaleData([...en, ...es]); // Define user's language. Different browsers have the user locale defined // on different fields on the `navigator` object, so we make sure to account // for these different by checking all of them const language = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage; // Split locales with a region code const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0]; // Try full locale, try locale without region code, fallback to 'en' const messages = localeData[languageWithoutRegionCode] || localeData[language] || localeData.en; ReactDOM.render(, document.getElementById("root") ); registerServiceWorker();
(Сильно скопированный код из Gist veethi Kasireddy здесь )
Еще одна маленькая вещь, которую нам нужно сделать, это редактировать наш веб-пакет Configs, чтобы разрешить импорт за пределами SRC
и node_modules
Отказ
Теперь, если мы изменим наши настройки браузера на испанские, мы должны увидеть наш контент, переведенный на испанский!
Окончательное состояние проекта можно найти здесь Отказ