Рубрики
Без рубрики

Как настроить интернационализацию в реакции от начала до конца

Этот пост будет использовать REVING-INTL, чтобы помочь вам перейти от приложения Create-raction-App для настройки фреймворка в завершенное, переведенное веб-приложение! Я совершил код, как я написал этот пост, так что вы сможете посмотреть мою историю совершения, чтобы легко увидеть, как мой код эволюции от начала

Автор оригинала: 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? Ну, у вас есть эти биты и кусочки контента по всему веб-приложению. И вы хотите, чтобы все было легко переведено. Если вы собираетесь дать вашему контенту переводчику, вы бы не дали им свой код и не скажу «удачи, добраться до работы».

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

Ок, отлично. Таким образом, вы сделали это, но теперь вы должны взять испанский контент в том, что один файл и повторно распределите его обратно в исходное место. Как бы вы сделали это программно? Возможно, вы назначаете идентификаторы каждому биту содержимого, чтобы вы не теряете отслеживание исходного местоположения каждого долога контента.

И это в значительной степени!

Первый шаг – обернуть приложение в ER> Компонент:


  

Теперь вам нужно определить контент для racted-intl Это в конечном итоге будет переведено. На главной странице моего приложения у меня есть следующий абзац:

It is a beautiful day outside.

Мне нужно сказать racted-intl Что это содержимое, которое я хочу перевести и дать ему удостоверение личности, чтобы он мог отслеживать этот контент и его оригинальное местоположение:

По умолчанию текст будет выводится в An>, поэтому нам нужно будет обернуть это в оригинале

Если мы хотим, чтобы это осталось абзацем.

Теперь я сделаю это для всего контента в моем веб-приложении.

Состояние проекта до сих пор можно найти здесь Отказ

Добавление 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 Отказ

Теперь, если мы изменим наши настройки браузера на испанские, мы должны увидеть наш контент, переведенный на испанский!

Окончательное состояние проекта можно найти здесь Отказ