Перейти к содержимому
XBSL (1C:Element)
Русский
Esc
navigateopen⌘Jpreview
На этой странице

Перевод проекта

Перевод исходников на английские написания: что отвечает по данным платформы, что по словарю проекта, как считается покрытие и по какому условию падает CI.

1С:Элемент двуязычен: у каждого ключевого слова, ключа метаданных, типа, члена и значения перечисления есть английское написание, и проект, написанный ими, компилируется так же, как русский. xbsl translate переписывает проект целиком: платформенную половину – по извлечённым данным платформы, собственную половину проекта – по словарю, который ведёт команда.

xbsl translate e1c/app                          # только отчёт: покрытие и что осталось
xbsl translate e1c/app --coverage               # плюс разбивка по объектам метаданных
xbsl translate e1c/app --missing gaps.yaml      # непереведённый остаток заготовкой словаря
xbsl translate e1c/app --out build/app-en       # записать переведённое дерево
xbsl translate e1c/app --out build/app-en --strict   # ненулевой выход, пока не полно

Что чем переводится

Платформенная половина – из данных, никогда не переводом. Ключевые слова берут форму того же регистра (Если -> If, если -> if); ключ yaml – английское написание, объявленное его классом метамодели, поэтому одно и то же слово в разных узлах может отличаться; значение перечисления ищется внутри своего перечисления (глобально одно русское слово отвечает нескольким английским); выражение типа сохраняет форму и фасеты (.Ссылка -> .Reference) – и в yaml, и в коде, всюду, где парсер читает тип (параметр, объявление, конструктор, приведение, аргумент типа), а то же слово после точки в другом месте – член: член-ссылка у приёмника, который держит фасет объекта проекта или чья цепочка дальше загружает запись, – тоже слово фасета (Reference), а свойство-ссылка надписи или картинки – Link; значение свойства с объединённым типом, совпадающее с членом объединения (Авто в Авто|Число), – этот член в написании платформенной пары типов; внутри блоков Запрос{ ... } отвечает словарь языка запросов, а не общий. Имя, которого данные не знают, остаётся как написано и попадает в отчёт пробелом данных – транслятор не угадывает.

Половина проекта – из словаря. Всё, что проект назвал сам: объекты, методы, реквизиты, компоненты форм, ключи словарей, файлы ресурсов, значение по умолчанию перечисления – голое или квалифицированное своим перечислением (Состояния.Открыт -> States.Open), – и каждая кириллическая строка комментария переводятся людьми. Три плана:

version: 1
language: en

# tokens: идентификатор целиком -> идентификатор целиком (файл ресурса – основой имени)
tokens:
    Задачи: Tasks
    Значок: Icon

# phrases: строка комментария -> её перевод
phrases:
    "Задача помечается выполненной.": "The task is marked done."

Отступ – четыре пробела, как пишет сам инструмент. Свой отступ словарь тоже сохраняет: писатель копирует его у соседних записей секции, поэтому файл, заведённый двумя пробелами, после правки из панели или --set остаётся валидным.

Третий план – literals – про строковые литералы кода:

literals:
    "Файл не загружен": "The file was not uploaded"
    "Не заполнено поле \"Наименование\"": "The \"Name\" field is empty"
    "Не разобрано тело: %{Описание}": "Could not parse the body: %{Описание}"

Литерал – данные, и переводчик их не угадывает: он заменяет ровно то, что команда перечислила. Ключ и значение пишутся так, как текст стоит в исходнике МЕЖДУ КАВЫЧКАМИ – с тем же экранированием, что и в коде; значение, которое не является правильным телом литерала, словарь отвергает при загрузке. Интерполяция в значении пишется как в исходнике: имя внутри неё переведёт сам движок. Литерал внутри Запрос{}, Образец{} и других разрешимых литералов не трогается – там это код, а не данные.

Два места, где текст и код перемешаны, план литералов обслуживает особо. Имя именованной группы образца ((?<Имя>...)) – это имя проекта: код читает группу обратно по нему (Группа("Имя")), и обе стороны берут написание из ОДНОГО источника – сначала план литералов, затем обычное разрешение имени. Разойдись они, вызов спросил бы группу, которой образец не объявлял. Шаблон представления вида события журнала – проза с выражениями внутри: выражения переименовываются как имена, а сам текст берётся из плана литералов по всему значению; чего план не назвал, попадает в отчёт пропусков, а не остаётся русским молча.

Суффиксы литерала длительности в коде переезжают на английские написания сами (300мс -> 300ms, 2д14ч30м5с6мс -> 2d14h30m5s6ms): состав русских суффиксов задан справкой типа, английский подтверждён компилятором платформы. Число, склеенное с любыми другими буквами, не трогается.

Тем же планом идёт всякое значение yaml, которое метамодель объявляет локализуемым текстом (Localizable): представления команд, прав доступа и перечислений человек читает на странице, поэтому они либо названы записью целиком, либо предъявлены пропуском. Исключение – свойство Описание: это документация разработчика, она остаётся данными и в пропуски не попадает.

Имена целиком, а не по словам: порядок слов английского имени обратный русскому, а части русского имени склоняются – склейка пословных переводов даёт кальку. Комментарии переводятся построчно: правка соседней строки не обесценивает запись, а одна запись обслуживает все повторы. Готовый блок комментария переразбивается по ширине проекта – той же, которой пользуется правило style/line-length: перевод, ставший длиннее исходника, иначе вылезал бы за предел. Рамки и разделители, списки, таблицы и образцы кода, а также строки, длинные уже в исходнике, остаются как были.

Словарь – каталог yaml-файлов (или один файл) с именем xbsl-translation, который ищется рядом с проектом и выше. Наполнение – это положить заполненную заготовку рядом с остальными; два файла, разошедшиеся в одном ключе, отвергаются при загрузке.

Квалифицированная запись (Словарь.Ключ: SignIn) действует только внутри одного пространства имён: ключу словаря локализованных строк может быть нужно написание, которого тому же слову нельзя дать в коде.

Имена, объявленные проектом, принадлежат проекту

Слово, которое знает и платформенный словарь, может быть тем, как проект назвал своё: значение перечисления, реквизит, метод, ключ словаря. На такие имена отвечает ТОЛЬКО словарь проекта. Без этого гейта обращение уезжало бы на английское написание, пока объявление ждёт записи в словаре, – yaml всё ещё объявлял бы русское значение, а модуль уже звал английское, и сборка отвергла бы дерево. С гейтом обе половины переезжают вместе – или вместе ждут одной записи.

Исключения остаются за платформой: встроенные элементы коллекций, которые диспетчеризуются по имени (стандартные код, наименование и владелец), и фасет после точки в выражении типа.

Что не трогается

Данные. Подписи, описания и любой другой текст, который читает пользователь, остаются как написаны – их переводит штатный механизм локализации, и переведённый проект несёт те же словари. Строковые литералы тоже остаются, кроме КОДА внутри их интерполяций: он разбирается заново и переводится как обычный код. Ид не меняются: переведённое дерево – тот же проект, а не его копия.

Строковый литерал, совпавший с переименованным именем, попадает в предупреждения: метод, который зовут по имени из строки, ломается молча, если переименовано только объявление.

Второе предупреждение того же рода – literal-data-value: литерал, совпавший со ЗНАЧЕНИЕМ из json-ресурса проекта, который запись плана литералов сдвинула. Такой литерал обычно сравнивается с этими данными (разбор начального заполнения), а данные перевод не трогает – после сдвига сравнение молча пустеет. Если литерал и правда данные, запись со значением, РАВНЫМ ключу, помечает это явно: покрытие засчитано, текст не движется, предупреждения нет. Отчёт печатает предупреждения списком – файл, строка, вид и текст.

Исключений два, и оба – имя, записанное ВТОРОЙ раз, вне кода.

Ключи json-ресурсов проекта, называющие поле его структуры. Структура читает json по имени поля, поэтому такой ключ и есть то же имя, записанное ещё раз: переименовать поле и оставить ключ значит не связать ничего. Молча – настройки чтения пропускают неизвестное свойство и инициализируют отсутствующее поле, поэтому проект компилируется, применяется и стартует с пустыми данными. Такие ключи переезжают тем же словарём, что и поля; значения и ключи, которых не объявляет ни одна структура (карта по содержимому, внешний контракт), остаются как написаны. Число переименованных ключей стоит в сводке прогона.

Литерал, который записывает ПУТЬ РЕСУРСА ("Значки/%Код.svg"). Дерево переименовывает файлы и каталоги ресурсов, и путь обязан следовать за ними, иначе платформа ресурс не находит. Литералом пути считается только то, что им и выглядит: оканчивается известным суффиксом ресурса, а каждый сегмент читается как имя файла. Регулярное выражение с косыми чертами и именованными группами под это не подходит и остаётся данными.

Словари локализации переворачиваются

Проект, у которого целевой язык уже лежит в секциях локализации, получает эти значения БАЗОВЫМИ (с переведёнными ключами), исходные значения уезжают в Локализация/<Код>/, а язык по умолчанию и язык разработки в дескрипторе следуют за этим. Ключ, у которого значения на целевом языке нет, сохраняет исходное и попадает в отчёт.

Покрытие и пробелы

--coverage печатает долю словаря по каждому объекту метаданных (семейство yaml/xbsl с общей основой имени) и по проекту целиком. --missing пишет остаток заготовкой словаря: записи по убыванию частоты, у каждой – число вхождений и первое место, значения пустые и готовы к заполнению.

Три счётчика разведены намеренно: покрытие СЛОВАРЯ (число, которое команда доводит до 100%), пробелы ДАННЫХ платформы (их не заклеивают записью в словаре) и кириллические скаляры, оставленные данными (перечислены, чтобы ревизор подтвердил: это и правда данные).

Словарь ищется рядом с проектом и выше – корень без словаря отвергается с перечнем мест поиска и словарём, найденным ниже корня, если он там есть; --dictionary называет его явно (файл или каталог), а --target – файл, в который лягут НОВЫЕ записи (по умолчанию 090-manual.yaml), а --comment – заголовок, с которым такой файл создаётся: там и место сказать, чему посвящена порция. --format json отдаёт весь отчёт машине. --no-localization-swap оставляет словари локализации как есть – это для проекта, который переводит исходники, но раскладку языков менять не хочет.

--strict даёт ненулевой выход, пока покрытие неполно или найдены проблемы, – это и нужно джобе CI перед выкладкой переведённой сборки. КОЛЛИЗИЯ имён – такая проблема: два разных имени одного пространства, переведённые в одно слово, ломают сборку (платформа отвергает повтор имени), и увидеть это может только транслятор.

В редакторе

Правило conventions/missing-translation (info, выключено по умолчанию, область – проект) показывает те же пробелы на их местах: имя или строка комментария, которых нет в словаре, – одна находка на первое вхождение в файле. Оно молчит, пока словарь не найден, поэтому говорит только в проекте, который переводит исходники. Включается --enable conventions/missing-translation (в редакторе – настройка Linter: Enable).

Находка несёт с собой ключ словаря, его вид и подсказку, поэтому лампочка предлагает записать перевод не выходя из файла: “Перевести как …” ставит платформенное написание одним щелчком, “Перевести …” спрашивает слово, а третьим действием открывается таблица словаря с отбором по этому ключу. После записи проект проверяется заново – перезапуск не нужен.

Таблица словаря (команда “XBSL: словарь перевода”) показывает записи рядом с тем, что исходники ещё не покрывают: вид, ключ, перевод, число вхождений, первое место и файл словаря – каждая запись на двух строках, поле перевода растянуто под остальным во всю ширину строки. Поле правится на месте, строка поиска и переключатель “только незаполненные” сужают таблицу, а подсказка – сперва платформенное написание, иначе догадка внешнего сервиса – стоит серым внутри пустого поля и подставляется щелчком или клавишей Enter. Таблица читает --table – записи, пропуски и покрытие за один проход по проекту – и пишет --set; после записи ячейки проект заново не обходится: перечитывается только словарь, а счётчики шапки сдвигаются на то, что изменила эта правка. Точные числа возвращает кнопка “Перечитать”. Панели нужен движок 0.72.0 или новее – --suggest, запрос машинного перевода за кнопкой предложений, появился только там. Кнопка “Запросить машинный перевод” в этой же панели заполняет пустые поля предложениями внешнего сервиса – подробности в разделе Машинный перевод ниже.

Наполнение словаря из инструментов

Словарь большого проекта – тысячи записей, и вычитывать эти файлы ради одного слова долго и чревато ошибками. Поэтому все поверхности работают СТРАНИЦАМИ поверх одного ядра движка.

CLI отвечает на те же вопросы, что задаёт панель:

xbsl translate e1c/app --gaps --kind token --limit 20      # чего не хватает, по убыванию частоты
xbsl translate e1c/app --entries --filter Задач            # что словарь уже говорит
xbsl translate e1c/app --table --limit 0                   # всё сразу: записи, пропуски, сводка
xbsl translate e1c/app --set правки.yaml                   # применить пачку из файла (ниже)
xbsl translate e1c/app --unused                            # пары, которых проект уже не использует

--table отвечает на три вопроса разом, одним проходом: он для того и заведён, что таблица редактора спрашивает именно их – порознь это два одинаковых обхода исходников в двух процессах и третье чтение того же словаря.

--set принимает пачку ФАЙЛОМ в любом из двух форматов: yaml самого словаря (секции tokens/phrases/literals, то же квотирование, что в файлах словаря; пустое значение снимает запись) либо список JSON [{key, value, kind}], который отдают скрипты. Пачка на сотни записей пишется так же, как сам словарь, а не инлайновым JSON.

--unused отвечает на обратный --gaps вопрос: не что нужно проекту, а чего нет в словаре, а что словарь всё ещё говорит, чего в проекте уже нет. Удаление кода оставляет за собой его имена и строки комментариев, и сказать об этом больше некому: --strict судит НЕПОКРЫТОЕ, а --entries показывает, где пара объявлена, а не используется ли она. --prune снимает ровно те строки, которые только что перечислил, поэтому --kind, --filter и страница действуют и на снятие; страницу, урезанную --limit, отчёт называет отдельно – снимать “всё”, глядя на пятьдесят строк из трёх тысяч, флаг только выглядит.

Чтение текстовое, и важна сторона его ошибки: имя, которое встречается ещё и в прозе, может быть сочтено используемым – это всего лишь оставит пару на месте, – но ЖИВАЯ пара сиротой не называется. Квалифицированный ключ (<Владелец>.<Имя>) судится по обоим концам: в исходниках они написаны порознь, и чтение точечного текста как одного имени объявило бы сиротой каждую такую пару.

--gaps показывает частоту, первые места употребления и suggestion – собственное написание платформы, если оно у неё есть. Подсказка – именно подсказка: имя, объявленное проектом, может намеренно требовать другого слова, а ВНУТРЕННЕЕ имя платформы (класс метаданных вроде CodeAttrMd) не предлагается вовсе.

Инструменты MCP – те же четыре, для агента, который наполняет словарь:

  • translate_status – покрытие и остаток, дешёвая проверка перед любым решением;
  • translate_gaps – непереведённое страницами (kind, filter, limit, offset), в ответе – прочитанный dictionary; compact отдаёт по строке только {key, kind, count} – форму рабочего списка переводчика, когда полные строки не влезают в ответ;
  • translate_entries – что уже в словаре, с файлом и строкой каждой записи, чтобы новое слово согласовывалось с принятыми;
  • translate_set – запись: добавить, исправить на месте или снять, обнулив значение; edits_file – пачка файлом в тех же двух форматах, что читает --set.

Новая запись попадает в 090-manual.yaml (или в файл из target), а уже существующая правится там, где живёт: писатель не плодит дублей, а дубль с другим значением словарь отвергает при загрузке.

Машинный перевод

Заполнить большой остаток вручную долго; --suggest добивает недостающее через внешний переводческий сервис – ПРЕДЛОЖЕНИЯМИ, а не записью: словарь не меняется, пока предложение не принято (в консоли – флагом --suggest-out, в редакторе – щелчком или клавишей Enter по подсказке в таблице).

xbsl translate e1c/app --suggest                                  # отчёт: что предложил сервис
xbsl translate e1c/app --suggest --provider yandex                # выбор сервиса явно
xbsl translate e1c/app --suggest --suggest-out 080-machine.yaml   # записать план рядом со словарём
xbsl translate e1c/app --suggest --plans tokens                   # добивать только имена

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

--suggest-out называет файл плана, в который лягут предложенные записи: каталог из пути отбрасывается, берётся только имя файла, и оно ложится внутрь каталога словаря. Если словарь – это ОДИН ФАЙЛ, отдельного плана не выходит: записи ложатся в него же.

Без --provider движок берёт единственный настроенный сервис. Если не настроен ни один, отказ называет переменные окружения, которых не хватает. Если настроено больше одного, отказ перечисляет сами сервисы (google, yandex) и просит выбрать флагом --provider – ни в одном из двух случаев движок не гадает молча.

Два сервиса, и они не взаимозаменяемы.

  • Yandex Translate – авторизация ключом сервисного аккаунта И идентификатором каталога (folder), нужны оба; порция – до 10000 символов за запрос; понимает глоссарий – список терминов проекта уходит вместе с текстом самого запроса.
  • Google Translate – авторизация одним ключом; порция – до 5000 символов; глоссария в его API нет вовсе – написание термина накладывается уже после ответа, когда движок собирает имя из вернувшейся прозы.

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

Ключи – в окружении, никогда в командной строке или в файле настроек. Три переменные, поимённые под каждый сервис:

  • XBSL_TRANSLATE_YANDEX_KEY, XBSL_TRANSLATE_YANDEX_FOLDER – ключ и каталог Yandex, нужны оба;
  • XBSL_TRANSLATE_GOOGLE_KEY – ключ Google, один.

В редакторе переменные окружения заводить не нужно: команда XBSL: Задать ключ машинного перевода (xbsl.translate.setKey) спрашивает, какой из трёх завести, и кладёт значение в защищённое хранилище (SecretStorage) расширения – оно передаёт его движку в окружении самого запуска --suggest, а не в настройке и не в командной строке. Настройка xbsl.translation.provider выбирает сервис, если настроены оба сразу; сам ключ ею не задаётся.

Кэш – файл machine-cache.json рядом со словарём (в каталоге xbsl-translation, если словарь – каталог, иначе рядом с единственным файлом словаря). В нём хранится СЫРОЙ ответ сервиса по паре (сервис, язык, отпечаток глоссария, текст) – не готовое имя: правило сборки имени и список терминов ещё изменятся, а переспрашивать сервис ради того же текста незачем. Формат – JSON, не yaml: словарь ищет *.yaml рекурсивно и принял бы файл кэша за ещё один план перевода с повторяющимися ключами. Запись остаётся в кэше независимо от того, приняли предложение в словарь или нет, – повторный --suggest не платит за один и тот же текст дважды, даже если прошлый ответ никто не принял.

Секция terms словаря – короткий список “русский термин -> английское написание”, не план перевода и не часть покрытия. Из неё берутся сразу две вещи: пары глоссария, которые уходят в запрос вместе с текстом (это умеет только Yandex), и написание для СБОРКИ ИМЕНИ. Вторая половина работает по ОТВЕТУ: собирая идентификатор, движок сверяет каждое английское слово ответа со списком терминов без учёта регистра и ставит словарное написание, где бы слово ни стояло во фразе. Русские формы слова при этом не разбираются вовсе, а термин, вернувшийся другим английским словом (множественным числом, синонимом), не опознаётся и остаётся как ответил сервис. На перевод строки комментария (phrases) термины не влияют – комментарий остаётся тем, что ответил сервис, целиком.

Имена собираются детерминированно, а не сервисом. Сервис отвечает прозой (“Site address”), а плану tokens нужен идентификатор: движок убирает служебные слова (a, the, of…), подставляет написание термина, где оно есть, и склеивает остаток – каждое слово с большой буквы. Имя, уже занятое другим ключом словаря, или прозу, из которой не вышло собрать идентификатор, --suggest отклоняет с названной причиной – сервис не угадывает и не переписывает поверх.

Литералы в сеть не ходят вовсе. --suggest посылает сервису только пробелы tokens и phrases (имена и строки комментариев целиком); строковый литерал заполняется отдельно и только тогда, когда его текст совпадает ЦЕЛИКОМ с уже принятым именем – локально, без единого запроса к сервису.

Что уходит наружу – без утайки. Сервис получает только текст пробела: имя как оно написано в проекте или строку комментария целиком. Ни путь файла, ни код вокруг, ни остальной проект сервис не видит. Без ключа команда не делает ни одного запроса ни к Yandex, ни к Google – сразу отказывает и называет переменную, которой не хватает.

Итог печатается тем же способом, что и у --set: cached – сколько ответов взято из кэша, requested – сколько запрошено заново, refused – сколько отклонено (с причиной у каждого).

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

Последнее обновление 6 сентября 2026 г.

Эта страница была полезной?