Перейти к содержимому

Файлы, форматы и экспорт

Строки проекта приходят файлами и уходят файлами. Эта страница — про оба направления.

ФорматРасширенияЧто сохраняется
JSON.jsonВложенность и массивы; формы множественного числа i18next (_one, _other…) становятся одним сегментом с множественным числом
Flutter ARB.arbМетаданные @key становятся описанием и записываются обратно целиком; @@locale сегментом не становится
YAML.yaml, .ymlВложенность, блочные скаляры, языковой корень Rails (en:), множественное число как one:/other:
gettext.po, .potЗначения, разбитые на несколько строк, msgctxt, msgid_plural с msgstr[n], комментарии #., fuzzy
Ресурсы Android.xml<string>, <plurals>, <string-array>, translatable="false", комментарии, разметка
Строки Apple.stringsКомментарии как описания, экранирование, пустые значения
Множественное число Apple.stringsdictОдна переменная множественного числа на ключ
Xcode String Catalog.xcstringsВсе языки каталога, комментарии, состояния, вариации множественного числа
XLIFF.xliff, .xlf1.2 и 2.0: resname, заметки, maxwidth, translate="no", состояния
CSV.csvЗаголовок называет колонки: ключ, описание, максимальная длина, по колонке на язык
Markdown.md, .mdxПо сегменту на блок, правится как Markdown

Формат берётся из расширения, а файл с расширением не из этого списка отклоняется с сообщением, где перечислены допустимые. Размер файла ограничен 10 МБ; проект крупнее переезжает импортом проекта.

Вложенные структуры превращаются в ключи через точку, поэтому такой файл:

json
{ "home": { "title": "Привет", "cta": "Начать" } }

даёт ключи home.title и home.cta. Именно их ищет ваш код, а экспорт записывает файл обратно вложенным, в том виде, в каком он пришёл.

Ключ уникален в пределах своего файла. И в auth.json, и в home.json может быть title; в одном файле дважды его быть не может. Запись gettext с контекстом получает ключ из контекста и текста вместе, поэтому Open в меню и Open на двери остаются двумя сегментами.

Загрузка исходного файла создаёт по сегменту на каждый ключ. Повторная загрузка того же файла — с тем же именем и в той же папке — это и есть способ отправлять изменения:

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

Загрузка записывается целиком или не записывается вовсе.

Загрузите файл, указав целевой язык, — и он читается как переводы, а не как источник: значения сопоставляются по ключу с сегментами одного файла и записываются в этот язык. Это файл, который вы выбрали, а иначе файл с тем же именем в той же папке, единственный файл с таким именем или тот, по которому названа загрузка, — de.yml находит en.yml, values-de/strings.xml находит values/strings.xml, auth.json.xliff находит auth.json. Если ничто из этого не решает вопрос, загрузка отклоняется, а не раскладывается по файлам с общим ключом.

Статус импортированного перевода берётся из файла, если файл его несёт: единица XLIFF с пометкой final или approved="yes" приходит утверждённой, запись gettext с fuzzy приходит на проверку. Без статуса новый перевод ждёт проверки. Перевод, у которого изменился текст, возвращается на проверку, даже если был утверждён; тот же самый текст свой статус не теряет. Утверждённые слова засчитываются в лимит тарифа, и загрузка, которая вышла бы за лимит, отклоняется целиком.

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

Файлы лежат в папках внутри проекта, и файл опознаётся именем и путём: locales/en.json и mobile/en.json — два разных файла.

  • Переименование папки одной операцией переносит все файлы под ней.
  • Слияние дубликатов убирает файлы с одинаковыми именем и путём, созданные дважды, — обычный итог того, что один и тот же файл отправили два пайплайна раньше, чем у любого из них появился идентификатор файла.

Экспорт записывает утверждённые переводы обратно в файл, в том формате и в той структуре, в которых файл был загружен: файл Android возвращается ресурсами Android, YAML для Rails — под запрошенным языком, вложенный JSON — вложенным.

ЧтоГде
Один файл, один языкВкладка «Файлы» → Экспорт либо GET /v1/workspaces/{id}/segments/export?fileId=…
Все файлы на всех языках ZIP-архивомВкладка «Файлы» → Экспорт всех либо GET /v1/workspaces/{id}/segments/export-zip
Прямо в репозиторийownlate pull или интеграция с репозиторием
Бандлом для рантаймаРелизы и OTA

Без файла экспорт сливает весь проект в один файл указанного формата (по умолчанию json). Файлы берутся в порядке пути и имени, и ключ, который есть в нескольких файлах, получает значение из последнего из них.

Сегменты с множественным числом записываются в синтаксисе формата: суффиксами i18next в JSON, msgid_plural в gettext, <plurals> в Android, вариацией в .stringsdict и .xcstrings.

Две настройки меняют то, что окажется в файле:

  • Подстановка источника — передайте исходный язык, и непереведённые ключи будут записаны с исходным текстом, а не пропущены. Пригодится, когда отсутствие ключа ломает приложение; выключите, когда лучше увидеть дырку.
  • Непереводимые сегменты всегда пишутся с исходным текстом, что бы вы ни просили.