Назад

gt-next@6.8.0

Ernest McCarter avatarErnest McCarter
gt-reactgt-nextgtx-cliv6.8.0statictranslationi18n

Обзор

Мы часто замечаем: чем зрелее становится кодовая база, тем более фрагментированным оказывается контент. Предложения оказываются разбросаны по функциям, утилитам, логике и сервисам.

function getSubject(gender) { 
  return gender === "male" ? "мальчик" : "девочка" 
}
function getObject(toy, gender) { 
  return toy === "мяч" ? "мяч" : getSubject(gender)
}

function Component({ gender, toy }) {
  return (
    <>
      <p>
        Красив{getSubject(gender) === "мальчик" ? "ый" : "ая"} {getSubject(gender)} играет с {getObject(toy, gender) === "мяч" ? "мячом" : getObject(toy, gender) === "мальчик" ? "мальчиком" : "девочкой"}.
      </p>
    </>
  )
}

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

В gt-next 6.8.0 мы ещё сильнее укрепляем убеждение, что мощная библиотека i18n должна адаптироваться под кодовую базу, а не наоборот. Хотя gt-next уже умеет переводить контент inline, ему не хватает двух фундаментальных возможностей такой библиотеки: (1) поддержки фрагментации предложений и (2) возможности повторного использования контента при сохранении согласования слов, спряжения и изменений порядка слов.

Мы представляем компонент <Static>, который позволяет вызывать статические функции непосредственно внутри переводов.

function getSubject(gender) { 
  return gender === "male" ? "мальчик" : "девочка" 
}
function getObject(type, gender) { 
  return type === "мяч" ? "мяч" : getSubject(gender)
}

function Component({ gender, toy }) {
  return (
    <T>
      <p>
        Красив{genderAgreement} <Static>{getSubject(gender)}</Static> играет с <Static>{getObject(toy, gender)}</Static>.
      </p>
    </T>
  )
}

Важные замечания

При этом мы настоятельно рекомендуем использовать компонент <Static> аккуратно и взвешенно. Компонент <Static>, несмотря на свою мощь, может привести к значительному увеличению числа переводческих записей. При неправильном использовании это может негативно сказаться на времени загрузки приложения.

Как использовать <Static>

Как и компоненты <T> и <Var>, компонент <Static> — это флаг, который указывает CLI‑инструменту, где нужно, а где не нужно искать переводимый контент. Он говорит CLI‑инструменту разыменовать вызов функции внутри тегов <Static> и занести в каталог все возможные варианты контента, возвращаемого этой функцией. Относитесь к каждому оператору return так, как будто он обёрнут в компонент <T>.

После того как CLI‑инструмент найдёт все возможные результаты вызова функции, он создаёт отдельную запись перевода для каждого возможного результата. Например, следующий код создаёт две отдельные записи перевода: "The boy is beautiful" и "The girl is beautiful". Поскольку для каждого возможного результата существует своя запись перевода, мы можем корректно обрабатывать согласование, спряжение и порядок слов во фрагментированном предложении: "El niño es hermoso" и "La niña es hermosa".

function getSubject(gender) { 
  return gender === "male" ? "boy" : "girl" 
}
function Component({ gender }) {
  return (
    <T>
      <Static>{getSubject(gender)}</Static> красив(а/о).
    </T>
  )
}

Ограничения и планы по улучшению

Умножение

Как уже упоминалось, если слишком активно использовать компонент <Static>, он может сгенерировать большое количество записей переводов, что негативно скажется на времени загрузки. Например, рассмотрим компонент с двумя компонентами <Static>, каждый из которых оборачивает вызов функции с двумя возможными вариантами результата. В этом случае создаются четыре записи перевода. Каждый дополнительный компонент <Static> умножает количество переводов на число возможных вариантов результата каждого вызова функции.

function getSubject(gender) { 
  return gender === "male" ? "мальчик" : "девочка" 
}
function getObject(toy) { 
  return toy === "ball" ? "мяч" : "карандаш"
}
function Component({ gender, toy }) {
  return (
    <T>
      <Static>{getSubject(gender)}</Static> играет с <Static>{getObject(toy)}</Static>.
    </T>
  )
}

Синтаксис

Важно рассматривать каждый оператор return так, как будто он обёрнут в компонент <T>. Любые переменные или вызовы функций, которые являются динамическими, по‑прежнему должны быть обёрнуты в компоненты <Var>.

В данный момент <Static> поддерживает только вызовы функций в качестве дочерних элементов, но в будущем он сможет работать с более сложными выражениями. Начиная с gt-next 6.8.0, для статических функций поддерживается следующий синтаксис:

function getExamples(key) {
  switch (key) {
    case "string, number, and boolean literals":
      if (condition1) {
        return "Мальчик"
      } else if (condition2) {
        return 22
      } else {
        return true
      }
    case "jsx expressions":
      return (
        <>
          Привет, <Static>{getTitle(title)}</Static>. Как у тебя дела, <Var>{name}</Var>?
        </>
      );
    case "ternary operators":
      return condition ? "Мальчик" : "Девочка"      
    case "function calls":
      return otherStaticFunction();
  }
}

Заключение

gt-next 6.8.0 представляет компонент <Static> как решение проблемы фрагментации предложений без потери полноты перевода и корректного грамматического согласования.