Локализация React приложении

    Всем привет!


    До нового года остаются считанные дни. Наткнулся на свой список дел, которые собирался сделать в 2019-м, среди них оказалось и написать статью на Хабр. Самое время заскочить в уходящий вагон).


    Сразу оговорюсь, пиарю свой велосипед, если такое Вам не по душе, то можете смело пропускать статью.


    Что такое локализация?


    Под локализацией здесь и далее будем понимать процесс адаптации приложения под разные языки и регионы. Локализация это не только про перевод текста. Пользователям приятнее видеть привычный формат дат, денежных значений, даже разделители в числах могут играть роль.


    Мотивация


    Собственно зачем вообще нужно заниматься локализацией приложения? Локализация приложения это то самое пресловутое повышение доступности вашего интерфейса для пользователей, о котором в последнее время так много принято говорить. Локализация расширяет целевую группу на которое ориентировано ваше приложение.


    Что хотелось от библиотеки локализации


    1. Поддержка ICU грамматики
    2. Форматирование дат
    3. Форматирование чисел

    Что получилось


    Ссылка на GitHub.


    За основу была взята самая популярная библиотека (согласно звездочка анализу) для локализации React приложении react-intl. Под капотом react-intl используется пакет intl-messageformat-parser который строит AST дерево. Согласно bundlephobia значительный размер как раз и занимает парсер. Он не написан руками, а использует PEG.js. Я написал для этих целей свой, который весит в 6 раз меньше. Очень вероятно, что я мог упустить какой-то кейс, буду благодарен, если кто-нибудь подскажет.


    Благодаря отличной поддержке Internationalization API современными браузерами, форматирование дат и чисел решается само собой. Всё что нужно, это хранить где-то текущий язык пользователя, чтобы применять его к форматированию и предоставлять более удобное api, в виде formatSomething(value, options).


    Таким образом получился пакет @eo-locale/core который вообще ни привязан к какому либо фреймворку или библиотеке и может использоваться достаточно гибко.


    React


    Версия для React представляет из себя набор компонентов и хуков.


    Чтобы начать использовать eo-locale оберните приложение в Provider.


    import { EOLocale } from 'eo-locale';
    
    const locales = [
      {
        language: 'en',
        messages: {
          hello: 'Hello {name}!'
        }
      },
    ];
    
    <EOLocale.Provider language="en" locales={locales}>
      <span>
        <EOLocale.Text id="hello" name="World" /> // Helo World!
      </span>
    </EOLocale.Provider>

    Форматирование чисел доступно через проксирование стандартных свойств Intl.NumberFormat


    import { EOLocale } from 'eo-locale';
    
    <EOLocale.Number value={1000} />
    // 1,000
    
    <EOLocale.Number
      value={1000}
      currency="EUR"
      maximumFractionDigits={2}
      minimumFractionDigits={2}
      style="currency"
    />
    // €1,000.00

    Аналогичным образом реализовано форматирование дат:


    import { EOLocale } from 'eo-locale';
    
    <EOLocale.Date value={new Date(2019, 2, 19)} />
    // 3/19/2019
    
    <EOLocale.Date
      value={new Date(2019, 2, 19)}
      day="numeric"
      month="long"
      year="numeric"
      weekday="long"
     />
     // Tuesday, March 19, 2019

    Всё вышеперечисленное также можно сделать не через компоненты, а с помощью хука useTranslator:


    import { useTranslator } from 'eo-locale';
    
    function SomeComponent() {
      const translator = useTranslator();
    
      return <div>{translator.formatNumber(1000)}</div>;
    }

    Одним из важных преимуществ перед другими подобными библиотеками считаю возможность использовать компоненты в качестве props. Например, у вас есть стилизованный компонент Money, который показывает значение в одном цвете, а знак валюты в другом и вам нужно вставить некоторую сумму в локаль:


    import { EOLocale } from 'eo-locale';
    import { Money } from '...somewhere';
    
    const locales = [
      {
        language: 'en',
        messages: {
          total: 'Total price is {price}'
        }
      },
    ];
    
    <EOLocale.Text
      id="total"
      price={<Money amount={1000} />}
    />
    // Total price is €1,000.00

    Есть и возможность отображать plural значения. Она реализована с использованием Intl.PluralRules.


    import { EOLocale } from 'eo-locale';
    
    const locales = [
      {
        language: 'en',
        messages: {
          items: '{count, plural, one {You have one item} other {You have {count} items}}'
        }
      },
    ];
    
    <EOLocale.Text id="items" count={3} />
    // You have 3 items

    Preact


    Все те же возможности реализованы и для Preact в соответствующем пакете.


    Некоторые особенности


    Библиотека полностью написана на Typescript, поэтому тайпинги поставляются из коробки.


    Не так давно столкнулись с проблемой того, что при локализации кодов ошибок, которые присылает сервер отсутствовали переводы на клиенте. Для этого в Provider была добавлена возможность прокинуть обработчик onIdMissing, с помощью которого можно логировать такого рода ошибки.


    Заключение


    Очень надеюсь, что кому-то библиотека окажется полезной. Буду рад предложениям и здоровой критике. Не забывайте, что даже если сейчас ваш сайт ориентирован только на один язык, то со временем эта ситуация может измениться. Заложить поддержку мультиязычности заранее не требует особых временных затрат, при этом все используемые в приложении тексты будут структурированы.

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 10

      0
      Под локализацией здесь и далее будем понимать процесс адаптации приложения под разные языки и регионы

      Если именно «под разные», то это интернационализация. Локализация всегда под конкретный язык/культуру.
      текущий язык пользователя

      Скорее текущую локаль (Язык+регион) — форматы даты/времени зависят не только от языка, а ещё и от региона.
        0
        Спасибо за замечание!
          0
          Конкретно привязку к React, увы, оценить не могу.

          Кроме первого пункта «Поддержка ICU грамматики», парсер которой скрыт в недрах @eo-locale/core, и в npm ссылки на гитхаб нету, остальное есть в стандартном Intl (ну и коленка + 10 минут комментатора ниже :) ).

          Я не понимаю почему это важно, поскольку мне не известны профессиональные CAT утилиты, нормально обрабатывающие ICU формат. Ни SDL Trados, ни MemoQ этим похвастаться не могут, колупаться среди непереводимых select и plural не приносит переводчикам радости. В теории ICU форматтер это круто и гибко, но на практике несколько простых текстов для plural или select без разметки переводить проще и быстрее.

          Пока переводчик это коллега — программист и текстов мало — всё хорошо. Когда проект станет больше, например, несколько десятков тысяч слов которые нужно будет переводить на десятки языков работа с ICU грамматикой будет источником проблем.
            0
            Парсер вот тут github.com/eo-locale/core/tree/master/src/parser

            Про то, что остальное просто использование Intl в статье указано прямо)

            У нас не несколько простых текстов, много мультиязычных проектов. Используем в работе сервис lokalise.com переводы из него можно экспортировать в том числе и в ICU.
            Собственно переводчики там работают с интерфейсом сервиса, им не нужно знать в каком формате будет экспортироваться текст.

            select ни разу не приходилось использовать.

            При использовании ICU грамматики вы пишете код для plural один раз и потом можете добавлять/убирать сколько угодно языков. Вам не нужно думать о plural rules для конкретного языка. Если приведете пример кода, который на Ваш взгляд форматирует plural более удобным способом, мне будет интересно взглянуть)
          0

          Понравились определения, кажется от w3c: интернационализация — подготовка приложения к работе с разными локалями, а локализация — собственно создание новой локали.

          –1
          How about this? Набросал на коленке за 10 минут. codesandbox.io/s/upbeat-flower-v0455
            0
            Очень круто, но ни одну из объявленных выше задач не решает
            0

            Мимопроходил. Два года назад был опыт с Интернационализацией (i18n) и Локализацией (L10n) интерфейса Ideco UTM. (Вылилось в презентацию в Екатеринбурге: https://www.youtube.com/watch?v=UIUXbzk273s).


            tl;dr; Стоит выделить основные задачи:


            1. Как работать разработчику с кодом приложения на нескольких языков? (Удобство программирования i18n-ого приложения)
            2. Как быть бедному переводчику? (Удобство локализации. Большой проект рано или поздно покажет, что большой JSON с переводами — далеко не конечное решение)
            3. Что делать с переводами, когда мы обновили приложение?

            Мы использовали gettext, который отвечает на эти вопросы так:


            1. есть функция gettext, которой разработчик передает строки на СВОЁМ английском языке (при большом желании и на русском).
            2. AST-Парсер автоматически извлекает все строки в файл, который отдаем переводчик переводит текст
            3. Переводчик может, например с poedit, перевести строки в удобном приложении, которое ещё и проверит, что он все строки правильно написал

            Плюсы:


            • удобно писать проект, если нашли отлаженный AST-gettext экстрактор
            • удобно переводить переводчику
            • с помощью gettext Утилит можно мержить переводы, отслеживать какие строки появились, какие пропали, какие вернулись
            • Есть хорошая вероятность, что все идеи будут работать в другом языке X (Изначально C)

            Минус:



            p.s. это был один из последних серьезных проектов на фронте. Не знают, что сейчас поменялось. Так как вижу новые библиотеки интернационализации — ничего хорошего нового. Задачу локализации очень легко недооценить, несмотря на весь опыт gettext до сих пор 0.20.1 (https://www.gnu.org/software/gettext/), уж не знаю почему разработчики не считают эту версию законченной

              0
              Спасибо за комментарий, обязательно гляну доклад.

              В свою очередь попробую ответить на поставленные вопросы:

              1. Собственно разработчик работает с ключами, не зная ничего о конкретном языке или значении внутри ключа. С этим есть некоторые недостатки, но пока не вижу решения лучше.
              2. Как уже писал выше, для переводов есть достаточно классные сервисы, которые предоставляют переводчикам удобный интерфейс. Очевидно, что не нужно заставлять переводчиков редактировать json файл или вникать в синтаксис грамматики.
              3. Собственно так как переводы живут в отдельном сервисе, то всё что нужно это скачивать их перед каждой выкладкой на продакшн.
              0
              deleted

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

              Самое читаемое