Редактор переводов¶
Эндпоинты, лежащие в основе редактора переводов Admin Next. Они позволяют просматривать все строки перевода, поставляемые ядром Grav, плагинами и темами, видеть полноту каждого языка и переопределять любую строку для этого сайта. Переопределения хранятся отдельно для каждого языка в user/languages/<lang>.yaml.
Эти маршруты находятся под /i18n, а не под /translations, и требуют аутентификации. Публичный эндпоинт Получение переводов (GET /translations/{lang}) — это другое: он представляет собой собственный словарь админки, отдаётся без аутентификации и заполняется из английского, поэтому не может отличить отсутствующую строку от строки, переведённой теми же словами. Редактор читает источники напрямую, чтобы отсутствующая строка выглядела как отсутствующая.
Разрешения: api.translations.read для чтения, api.translations.write для сохранения переопределений, машинного перевода и импорта. Демо-учётные записи получают 403 при любой записи.
Состояния ячеек¶
У каждого ключа в редакторе есть ячейка для каждого языка с полем value, значением shipped (что даёт источник, или null) и полем state:
| Состояние | Значение |
|---|---|
shipped |
Значение приходит из файла языка плагина, темы или ядра |
overridden |
У этого сайта есть собственное значение в user/languages/<lang>.yaml |
missing |
Ни один источник не поставляет ключ на этом языке и переопределения нет, поэтому посетители видят запасное значение |
Поле known строки равно false, когда ключ вообще не поставляется ни одним источником и назван только переопределением сайта, например из-за опечатки или строки из удалённого плагина. Такие строки выводятся, чтобы их можно было найти и исправить.
Коды языков¶
Коды языков должны иметь вид en, fr, pt-BR или zh-Hant. На маршрутах /i18n/overrides/{lang} несоответствующий код даёт 422. Параметры запроса, такие как lang и source_lang, вместо этого откатываются к языку редактора по умолчанию: к языку сайта по умолчанию, если его поставляет какой-либо источник, иначе к en, иначе к первому языку, который поставляет какой-либо источник.
Переход с translation-strings¶
Сайты, использовавшие плагин translation-strings, могут перенести его переопределения с помощью Состояния импорта и Импорта строк перевода. Пока этот плагин включён, его значения по-прежнему важнее user/languages, поэтому сначала выполните импорт, проверьте результат, а затем выключите плагин.
Список языков¶
GET /i18n/languages
Все коды языков, которые поставляет какой-либо источник, каждый с флагом наличия файла переопределений для него на этом сайте, плюс язык редактора по умолчанию. Требуется api.translations.read.
Ответ
{"data": {"default": "en", "languages": [{"code": "de", "has_overrides": false}, {"code": "en", "has_overrides": true}, {"code": "fr", "has_overrides": false}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Языки возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
Список источников¶
GET /i18n/sources
Каждый источник (ядро Grav, плагины, темы и собственные переопределения этого сайта), поставляющий хотя бы одну строку на запрошенном языке, с пространствами имён ключей, которые он добавляет. Это питает панель просмотра редактора. Требуется api.translations.read.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
lang |
string | Нет | Язык, на котором считать ключи. Неверный или отсутствующий код откатывается к языку редактора по умолчанию. |
Ответ
{"data": {"lang": "en", "providers": [{"id": "theme:quark", "kind": "theme", "slug": "quark", "label": "Quark", "enabled": true, "key_count": 42, "namespaces": [{"name": "THEME_QUARK", "key_count": 42}]}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Источники возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
id источника — system:core, plugin:<slug>, theme:<slug> или user:overrides, а kind — system, plugin, theme или user. enabled сообщает, включён ли плагин или является ли тема активной; для ядра и переопределений сайта он всегда true. Передайте id в параметре provider для Списка ключей, чтобы увидеть строки этого источника.
Полнота перевода¶
GET /i18n/coverage
Для каждого языка — сколько ключей исходного языка он имеет (поставляемых или переопределённых) и сколько отсутствует. Требуется api.translations.read.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
source_lang |
string | Нет | Опорный язык. Неверный или отсутствующий код откатывается к языку редактора по умолчанию. |
Ответ
{"data": {"source_lang": "en", "coverage": [{"code": "fr", "total": 1868, "translated": 1702, "missing": 166, "overridden": 3}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Полнота возвращена. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
total — число ключей на исходном языке, а translated и missing считаются только по этому набору ключей, поэтому устаревший ключ, который несёт лишь старый перевод, не учитывается. overridden — число переопределений сайта на этом языке для любых ключей.
Список ключей¶
GET /i18n/keys
Матрица редактора: одна строка на ключ, отсортированная по ключу, со значением на исходном языке и ячейкой для каждого запрошенного языка. Ключи, существующие только как переопределения сайта, включаются, чтобы их можно было исправить. Требуется api.translations.read.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
source_lang |
string | Нет | Опорный язык. Неверный или отсутствующий код откатывается к языку редактора по умолчанию. |
langs |
string | Нет | Коды языков через запятую для включения в виде ячеек, например en,fr,de. Коды, которые не поставляет ни один источник, игнорируются; по умолчанию только исходный язык. |
q |
string | Нет | Поисковый текст, сопоставляемый без учёта регистра с ключом и значением на исходном языке. |
provider |
string | Нет | Только ключи, поставляемые этим id источника, например theme:quark (см. Список источников). |
namespace |
string | Нет | Только ключи, равные этому префиксу с точками или находящиеся под ним, например PLUGIN_ADMIN. |
status |
string | Нет | all (по умолчанию), shipped, overridden, missing или unknown. Строка подходит, когда ячейка любого запрошенного языка имеет это состояние; unknown совпадает с ключами, которые не поставляет ни один источник. |
page |
integer | Нет | Номер страницы (по умолчанию 1). |
per_page |
integer | Нет | Строк на странице (по умолчанию 20, максимум 500). |
Ответ
{"data": [{"key": "THEME_QUARK.SEARCH", "namespace": "THEME_QUARK", "source_value": "Search", "providers": ["theme:quark"], "owner": "theme:quark", "known": true, "values": {"en": {"value": "Search", "state": "shipped", "shipped": "Search"}, "fr": {"value": "Rechercher", "state": "overridden", "shipped": "Recherche"}}}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 1, "total_pages": 1}}, "links": {"self": "/api/v1/i18n/keys?page=1&per_page=20"}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Строки возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
q сопоставляется со значением так же, как с ключом, потому что люди знают слова на экране, а не ключи за ними. source_value — переопределение сайта на исходном языке, если оно есть, иначе поставляемое значение. providers перечисляет все источники, поставляющие ключ, а owner — тот, чьё значение побеждает во время выполнения. Состояния ячеек см. во введении к разделу.
Ссылки links для постраничной разбивки сохраняют все отправленные параметры запроса (поиск, фильтры, языки), поэтому переход по одной из них остаётся в том же отфильтрованном представлении.
Получение ключа¶
GET /i18n/keys/{key}
Один ключ по всем языкам, которые поставляет какой-либо источник. Неизвестный ключ всё равно возвращает 200 с known: false и ячейками missing. Требуется api.translations.read.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
key |
string | Да | Ключ перевода с точками (параметр пути), например THEME_QUARK.SEARCH. Маршрутизируются только буквы, цифры, _, . и -; всё остальное даёт 404. |
source_lang |
string | Нет | Опорный язык для source_value. Неверный или отсутствующий код откатывается к языку редактора по умолчанию. |
Ответ
{"data": {"key": "THEME_QUARK.SEARCH", "namespace": "THEME_QUARK", "source_value": "Search", "providers": ["theme:quark"], "owner": "theme:quark", "known": true, "values": {"de": {"value": "Suche", "state": "shipped", "shipped": "Suche"}, "en": {"value": "Search", "state": "shipped", "shipped": "Search"}, "fr": {"value": "Rechercher", "state": "overridden", "shipped": "Recherche"}}}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Ключ возвращён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
404 |
Ключ содержит символы за пределами маршрутизируемого набора. |
Получение переопределений¶
GET /i18n/overrides/{lang}
Файл переопределений сайта для одного языка (user/languages/<lang>.yaml) в виде YAML — для расширенного редактора. Без namespace это необработанный текст файла или пустая строка, когда файла нет. С namespace возвращаются только переопределения под этим префиксом, заново выгруженные как вложенный YAML, поэтому «редактировать как YAML» охватывает только то, что на экране. Требуется api.translations.read.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
lang |
string | Да | Код языка (параметр пути), например fr. |
namespace |
string | Нет | Префикс ключей с точками, которым ограничивается вывод, например THEME_QUARK. |
Ответ
{"data": {"lang": "fr", "scoped": true, "namespace": "THEME_QUARK", "count": 1, "yaml": "THEME_QUARK:\n SEARCH: Rechercher\n"}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Переопределения возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
422 |
Неверный код языка. |
scoped равно true, когда был передан namespace, а count — число включённых переопределений. Сохраните отредактированный текст обратно через Замену переопределений, передав тот же namespace.
Обновление переопределений¶
PATCH /i18n/overrides/{lang}
Встроенные правки переопределений одного языка. set записывает значения, а unset удаляет переопределения, чтобы снова отображалось поставляемое значение. Отправляет X-Invalidates: i18n:update, translations:update. Требуется api.translations.write; демо-учётные записи получают 403.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
lang |
string | Да | Код языка (параметр пути), например fr. |
source_lang |
string | Нет | Параметр запроса: опорный язык для source_value в возвращаемых строках. Неверный или отсутствующий код откатывается к языку редактора по умолчанию. |
set |
object | Нет | Объект «ключ с точками → новое значение». Должен быть объектом: JSON-массив даёт 422. Числовые ключи внутри объекта игнорируются. |
unset |
array | Нет | Ключи, чьё переопределение следует удалить. Элементы, не являющиеся строками, игнорируются. |
Запрос
Ответ
{"data": {"written": {"THEME_QUARK.SEARCH": "Rechercher"}, "removed": ["PLUGIN_ADMIN.TITLE"], "reverted": [], "unknown": [], "rows": [{"key": "THEME_QUARK.SEARCH", "namespace": "THEME_QUARK", "source_value": "Search", "providers": ["theme:quark"], "owner": "theme:quark", "known": true, "values": {"fr": {"value": "Rechercher", "state": "overridden", "shipped": "Recherche"}}}, {"key": "PLUGIN_ADMIN.TITLE", "namespace": "PLUGIN_ADMIN", "source_value": "Title", "providers": ["plugin:admin"], "owner": "plugin:admin", "known": true, "values": {"fr": {"value": "Titre", "state": "shipped", "shipped": "Titre"}}}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Переопределения сохранены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.write или это демо-учётная запись. |
422 |
Неверный код языка, set не является объектом, unset не является массивом, либо оба пусты. |
Значение set, равное тому, что поставляет источник, сохраняется как удаление, а не как переопределение, поэтому файл содержит только реальные отличия и продолжает следовать будущим формулировкам источника. Такие ключи перечислены в reverted. Ключи, которые не поставляет ни один источник, всё равно записываются и перечисляются в unknown.
written содержит ключи, сохранённые как переопределения, с их значениями, а removed — ключи, чьё существующее переопределение было удалено. rows содержит свежую строку матрицы для каждого затронутого запросом ключа, только с ячейкой этого языка, чтобы редактор мог обновить её на месте.
Замена переопределений¶
PUT /i18n/overrides/{lang}
Сохранение в расширенном режиме. Разбирает yaml (отображение, вложенное или с ключами с точками) и заменяет им переопределения языка. Отправляет X-Invalidates: i18n:update, translations:update. Требуется api.translations.write; демо-учётные записи получают 403.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
lang |
string | Да | Код языка (параметр пути), например fr. |
yaml |
string | Да | YAML-отображение ключей в строки. YAML-список даёт 422. Пустая строка удаляет все переопределения в области действия. |
namespace |
string | Нет | Префикс ключей с точками, которым ограничивается замена. Опустите его или передайте пустую строку, чтобы заменить весь файл. |
Запрос
Ответ
Коды ответов
| Код | Описание |
|---|---|
200 |
Переопределения сохранены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.write или это демо-учётная запись. |
422 |
Неверный код языка, отсутствует yaml, YAML не разбирается или не является отображением, либо сохранение с областью действия называет ключи вне пространства имён. |
С namespace заменяются только переопределения под этим префиксом, а всё остальное в файле сохраняется. Именно это делает безопасным сохранение отфильтрованного представления: сохранение строк одной темы без области действия удалило бы все остальные переопределения. Ключ вне пространства имён проваливает всё сохранение с 422, называющим ключи вне области действия (до пяти), а не с ошибкой разбора.
Значения, равные поставляемому, не сохраняются и перечисляются в dropped. Ключи, которые не поставляет ни один источник, сохраняются и перечисляются в unknown, а не отклоняются, поэтому опечатка будет замечена, а не проигнорирована. count — число переопределений, сохранённых из этого YAML, а removed — на сколько меньше переопределений в области действия стало.
Состояние перевода¶
GET /i18n/translate
Доступен ли машинный перевод через плагин AI Translate и, если нет, почему. Редактор скрывает свои действия перевода, когда он недоступен. Требуется api.translations.read.
Ответ
{"data": {"available": false, "installed": true, "enabled": true, "reason": "not_configured", "max_keys": 200}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Состояние возвращено. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
reason равен null, когда available равно true, иначе not_installed, not_enabled или not_configured (у плагина нет настроенного провайдера), чтобы клиент мог подсказать владельцу сайта, что делать. max_keys — максимальное число ключей, принимаемое одним запросом Перевода ключей.
Перевод ключей¶
POST /i18n/translate
Отправляет исходные значения до 200 ключей в плагин AI Translate и возвращает предложенные переводы. Ничего не записывается: просмотрите предложения и сохраните нужные через Обновление переопределений. Требуется api.translations.write, так как это расходует кредиты провайдера; демо-учётные записи получают 403.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
target_lang |
string | Да | Язык, на который переводить, например fr. |
keys |
array | Да | Ключи для перевода, от 1 до 200. Элементы, не являющиеся строками, игнорируются. |
source_lang |
string | Нет | Язык, с которого переводить. По умолчанию язык редактора по умолчанию и должен отличаться от target_lang. |
Запрос
Ответ
{"data": {"source_lang": "en", "target_lang": "fr", "proposals": [{"key": "THEME_QUARK.SEARCH", "source": "Search", "value": "Rechercher", "ok": true, "reason": null}, {"key": "PLUGIN_ADMIN.TITLE", "source": "Title", "value": "Titre", "ok": true, "reason": null}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Предложения возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.write или это демо-учётная запись. |
422 |
Машинный перевод недоступен, отсутствует target_lang или keys, ключей нет или их больше 200, исходный и целевой языки совпадают, либо вызов провайдера не удался. |
Заполнители маскируются перед отправкой текста и проверяются, когда он возвращается. Предложения приходят в том порядке, в котором были отправлены ключи. Предложение с ok: false имеет null в value и одно из следующих значений reason:
| Причина | Значение |
|---|---|
no_source |
У ключа нет значения на исходном языке |
icu_needs_human |
Сообщение ICU, которое должен перевести человек |
nothing_to_translate |
В значении нет переводимого текста, только заполнители или разметка |
provider_returned_nothing |
Провайдер вернул пустой результат |
placeholders_mangled |
Провайдер изменил заполнитель, поэтому предложение отклонено |
Первые три определяются до вызова провайдера, поэтому ничего не стоят.
Состояние импорта¶
GET /i18n/import/translation-strings
Что настроено в старом плагине translation-strings и что даст его импорт для каждого языка. Только чтение. Требуется api.translations.read.
Ответ
{"data": {"present": true, "plugin_enabled": true, "config_path": "/var/www/grav/user/config/plugins/translation-strings.yaml", "pending": 4, "total": 6, "languages": [{"code": "fr", "total": 6, "new": 3, "already": 1, "conflict": 1, "shipped": 1, "unknown": 0, "keys": [{"key": "THEME_QUARK.SEARCH", "status": "new", "unknown": false, "current": null, "value": "Rechercher"}]}]}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Предпросмотр импорта возвращён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.read. |
422 |
В конфигурации плагина есть код языка, который редактор не может сохранить. |
present равно true, когда в плагине настроено хотя бы одно переопределение, а plugin_enabled важен, потому что пока плагин включён, его значения по-прежнему важнее user/languages. pending считает ключи, которые запишет импорт (new плюс conflict). config_path — абсолютный путь к файлу конфигурации плагина; демо-учётные записи получают вместо него (hidden in demo mode).
Для каждого языка ключи new ещё не имеют переопределения, ключи already переопределены тем же значением, ключи conflict переопределены другим значением, которое импорт заменяет значением плагина, а ключи shipped равны поставляемому значению, поэтому импорт ничего не сохраняет. Счётчики точны, но keys перечисляет не более 50 ключей на язык. В каждом перечисленном ключе current — существующее переопределение сайта (или null), а value — значение плагина.
Импорт строк перевода¶
POST /i18n/import/translation-strings
Объединяет все переопределения, настроенные в плагине translation-strings, с user/languages/<lang>.yaml. Переопределения других ключей сохраняются; там, где оба называют один и тот же ключ, побеждает значение плагина, потому что именно его сайт показывает сейчас. Отправляет X-Invalidates: translations:update. Требуется api.translations.write; демо-учётные записи получают 403.
Ответ
{"data": {"imported": 4, "reverted": 1, "unknown": [], "languages": [{"code": "fr", "written": 4, "reverted": 1, "unknown": 0, "path": "/var/www/grav/user/languages/fr.yaml"}], "plugin_enabled": true}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Импорт завершён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.translations.write или это демо-учётная запись. |
422 |
В плагине не настроено ни одного переопределения, либо в его конфигурации есть код языка, который редактор не может сохранить. |
Каждый код языка в конфигурации плагина проверяется до любой записи, поэтому плохой код проваливает весь импорт с 422, называющим его, и ни один язык не остаётся наполовину импортированным.
Плагин не выключается за вас. Его выключение — изменение конфигурации с собственным разрешением, поэтому сделайте это потом через Обновление конфигурации; plugin_enabled сообщает его текущее состояние. imported считает переопределения, записанные по всем языкам, reverted — значения, пропущенные из-за совпадения с поставляемым, а unknown перечисляет импортированные ключи, которые не поставляет ни один источник.