Оглавление
- Локализация решений 1С для других стран
- Из чего состоит продукт 1C:Restaurant management
- Блок-схема процесса локализации
- Что удаляем из конфигурации‑источника
- Ритмичность получения локализованной конфигурации
- Вариант локализации № 1: Разработка на EnScript
- Вариант локализации № 2: Использование плагина LangTool для EDT
- Общая схема этапов локализации
- Этап 1: Подготовка конфигурации
- Этап 2: Подготовка словарей с помощью LangTool и машинного обучения
- Подготовка проекта к переводу
- Составление словарных пар LangTool и получение необходимых мест для перевода
- Концепт перевода и машинное обучение
- Преобразование из CamelCase в предложения СОКом
- Обучение нейросетевой модели на основе предложений с учетом контекста
- Перевод с помощью модели
- Обратное преобразование в CamelCase
- Этап 3: Перевод с помощью Language Tool
- Осторожно! Сложности
- Заключение
Локализация решений 1С для других стран
1С:Предприятие — это удобный и надежный инструмент, который позволяет автоматизировать учет, аналитику, документооборот, управление персоналом и многие другие процессы. Поэтому решения на платформе 1С широко используются в России, СНГ и за рубежом. Для возможности использования программных продуктов от 1С вне российского рынка необходимо перевести их на языки пользователей из других стран — выполнить «локализацию» решения.
Локализация включает в себя перевод текстов интерфейса, документации и сообщений пользователю на нужный язык, поддержку различных национальных стандартов представления дат, чисел, валют — то, что можно объединить в понятие «локализация интерфейса».
Локализовать можно также и коды модулей, запросы динамических списков, СКД и т. п. Это необходимо для того, чтобы поддерживать и дорабатывать конфигурацию могли специалисты, не владеющие русским языком, но знакомые с англоязычным синтаксисом языка 1С. Платформа 1С:Предприятие позволяет работать с операторами встроенного языка как в русскоязычном написании, так и в англоязычном (далее — EnScript). Если открыть синтакс‑помощник, то можно увидеть англоязычную запись в скобках напротив описания метода или платформенных конструкций на русском языке:
Оба написания можно использовать одновременно в одном исходном тексте, система будет правильно воспринимать операторы, написанные на обоих языках. Подобную локализацию на EnScript можно назвать «локализация кода».
В случае выхода решения на международный рынок одним из требований заказчиков, как правило, является локализация и кода, и интерфейса — «полная локализация». Причиной тому, в первую очередь, является желание иметь возможность работы с кодом конфигурации иностранным коллегам‑разработчикам.
Ряд типовых продуктов 1С уже полностью локализованы на английский язык, например, 1C:Drive или Company Management (аналоги «1С:Управление нашей фирмой», далее — УНФ).
Однако, запросы бизнеса на международном рынке зачастую не удается покрыть возможностями типовых продуктов. Так, появилась необходимость в полной локализации отраслевого решения «1С:Управление предприятием общепита» для вьетнамского рынка.
Из чего состоит продукт 1C:Restaurant management
«1С:Управление предприятием общепита» (УПО) разработан на основе типовой конфигурации "1С:Управление нашей фирмой, ред. 3«с сохранением основных возможностей и механизмов типового решения. Таким образом, конфигурацию «1С:Управление предприятием общепита» можно представить следующим образом:
Где «УНФ (ru)» — конфигурация поставщика «Управление нашей фирмой», ред. 3.
А «отраслевые доработки УПО (ru)» — это объекты метаданных и участки кода, добавленные к конфигурации поставщика для автоматизации процессов управления деятельностью предприятий общественного питания.
Поскольку конфигурация «1С:Управление нашей фирмой» уже локализована для международного рынка, то возьмем в качестве конфигурации поставщика локализованную версию, в таком случае останется локализовать только отраслевые доработки решения. Это значительно ускорит процесс локализации, в том числе по той причине, что локализованное решение уже учитывает особенности законодательства и ведения бизнеса за рубежом.
Разработчики 1Сi и 1С:Vietnam представили несколько решений локализованной УНФ — это 1C:Drive и Company Management (далее — СМ). Мы за основу возьмем именно CM, поскольку он уже имеет вьетнамский интерфейс и необходимый набор базовых объектов.
Отталкиваясь от этого, локализованное решение будет выглядеть как:
И если первая часть слагаемого уже известна — это конфигурация поставщика CM, то отраслевые доработки пока только предстоит локализовать. Далее развернуто рассмотрим этот процесс. Англоязычная конфигурация УПО носит название Restaurant management (далее — RM).
Блок‑схема процесса локализации
В целом, процесс локализации представляет собой несколько последовательных этапов, которые можно отразить в виде блок-схемы следующего вида:
Что удаляем из конфигурации‑источника
Локализация программы 1С — это в первую очередь адаптация программы к особенностям конкретного рынка. Она должна учитывать особенности учета и законодательства определенной страны и региона, а также соответствовать местным нормам и стандартам качества. По этой причине часть блоков и подсистем, присущих конфигурации УПО, для конфигурации RМ избыточны и требуют удаления. Так, например, подсистема ЕГАИС или ВЕТИС в рамках вьетнамского законодательства отсутствует и должна быть исключена из конфигурации RM. В конфигурации СМ участки кода и модули, поддерживающие работу таких подсистем, уже редуцированы, однако, отраслевые доработки УПО разрабатывались с учетом подобных государственных систем учета. Поэтому одной из задач в процессе локализации будет обнаружение «лишних» объектов и участков кода и последующее их устранение.
Ритмичность получения локализованной конфигурации
В основе разработки алгоритма локализации отраслевого решения УПО стояла необходимость оперативно получать готовый релиз конфигурации RM, максимально близкий к сроку выхода очередного релиза УПО. В идеале, не дольше нескольких дней, а не недель или месяцев, как требовалось бы для перевода подобного рода решения без автоматизированных составляющих.
Вариант локализации № 1: Разработка на EnScript
Одним из вариантов решения задачи локализации кода может являться параллельная разработка решения сразу на 2-х языках — русском и английском. Например, разработчик последовательно вносит изменения сперва в русскоязычную конфигурацию, а затем те же изменения, но в решение для международного рынка на EnScript. Однако, для компании это очень затратно, т. к. будет либо замедляться разработка отраслевого решения, что безусловно негативно скажется на продукте, либо придется нанимать группу разработчиков‑"синхронистов«, которые будут преобразовывать на EnScript в отдельном хранилище исправления и доработки, выполненные основной группой разработчиков. Но есть и другой способ решения задачи.
Вариант локализации № 2: Использование плагина LangTool для EDT
Разработчики EDT представили удобный инструмент для локализации программных продуктов 1С — плагин Language Tool (далее — LT или LangTool).
Что такое LangTool
1C:Language Tool — это дополнительный инструмент (расширение или плагин) для среды разработки 1C:EDT, который предназначен для автоматизации перевода интерфейса на дополнительные языки, а также исходного кода и метаданных конфигураций на платформе «1С:Предприятие 8» на альтернативный язык, например с русского на английский.
Версия, установка, настройка, словари
На текущий момент актуальная версия плагина 1.3.0 (для версии EDT 2023.1).
Это финальная версия плагина, и стоит отметить, что очень важно использовать именно актуальные релизы решений, поскольку с каждой версией устраняются найденные ранее ошибки, прорабатываются замечания пользователей предыдущих версий плагина. Language Tool поставляется в виде репозитория Eclipse с сайта разработчиков, из которого производится дальнейшая установка. Подробная справка о нем представлена на сайте 1С:ИТС в разделе «Плагины для 1С:EDT».
В списке версий плагина на сайте необходимо выбрать требуемую, подходящую к версии самого EDT.
Затем используя опубликованный репозиторий либо предварительно скачав архив репозитория, установить его в EDT — «Справка» — «Установить новое ПО»:
В открывшемся окне следует нажать «Добавить», после чего указать расположение р2‑репозитория либо выбрать ранее скачанный архив:
После чего выбрать галочками инструменты для установки и следовать привычному алгоритму установки:
В первую очередь рекомендуем ознакомиться со справкой к плагину, которую можно открыть через меню «Справка» — «Оглавление справки». В разделе 1С:Language Tool подробно описаны сценарии работы с плагином, принцип работы и полезные рекомендации:
После установки плагина в интерфейсе EDT появятся такие инструменты работы с ним, как: настройка переводов в редакторе проекта, контекстная панель перевода, двухпанельный редактор модулей и другие.
Главным инструментом работы с плагином LT являются словари или «хранилища переводов». Словари представляют собой пары конструкций — «конструкция на переводимом языке» = «переведенная на целевой язык конструкция», помещенные в файлы с форматами:
- *.dict (общий словарь, содержащий исходное слово или фразу и ее перевод).
- *.lsdict (общий словарь интерфейсных текстов, содержащий исходный текст и его перевод).
- *.lstr (контекстные переводы интерфейса объектов метаданных).
- *.nstr (контекстные переводы интерфейсов в текстах модулей, заданных в функции НСтр()).
- *.trans (контекстные переводы модельных свойств).
Мы будем работать с общими словарями, которые задаются для всей конфигурации в целом. Пример содержания общего файла‑словаря в формате CamelCase:
И файла переводов конструкций Non‑CamelCase (например, синонимы, комментарии в модулях), в котором небуквенные символы (пробелы, знаки препинания, табуляции и т. п.) эскейпируются обратным слешем:
Настраивать словари и работать с переводом кода и интерфейса конфигурации можно несколькими способами:
- С помощью редактирования отдельных переводов объекта. Для этого необходимо воспользоваться перспективой LT. Если после установки плагина она не появится, ее можно открыть через «Окно» — «Перспектива» — «Открыть перспективу» — «Другое»:
И выбрать перспективу Translation:
При выделении объекта конфигурации или работы с модулем в перспективе будут отображаться все конструкции к переводу. Например, укажем имя подсистемы УОП_ВсеОбъекты для локализованной конфигурации — RM_AllObjects:
При этом, нажав на «...» можно выбрать, в какой словарь будет сохранен указанный перевод:
- Можно работать с переводами не только в ручном режиме, но и используя автопереводы от Google, Yandex и Microsoft. Для этого необходимо перейти в подменю «Окно» — «Параметры» и настроить один из сервисов автоперевода:
- И конечно, со словарями проекта можно работать напрямую через файл со словарными конструкциями. Такой способ удобнее и предпочтительнее, поскольку позволяет ускорить процесс локализации (непереведенные конструкции могут исчисляться десятками тысяч), собрать полные словари и воспользоваться ими для перевода. Для этого необходимо настроить файл описания состава словарей, расположить словари в каталоге src проекта EDT и запустить полный перевод проекта. Этот процесс детальнее рассмотрим в нашей статье:
Общая схема этапов локализации
Возвращаясь к составу продукта RM, напомню, что англоязычная конфигурация УПО (RM) представляет собой объединение уже локализованной Company Management и отраслевых доработок УПО, переведенных на английский язык. Исходя из этого, процесс локализации можно разделить на следующие этапы:
- Подготовка конфигурации к переводу. Добавление к конфигурации Company Management отраслевых доработок УПО, на первом этапе русскоязычных. Получение объединенной конфигурации.
- Получение словарей.
- Перевод полученной конфигурации на английский язык. По сути, перевести останется только отраслевые доработки. Этот этап выполняем с помощью LT.
Этап 1: Подготовка конфигурации
Конфигурация УПО доработана в сравнении с типовой УНФ в значительной мере: в ней присутствуют полностью новые отраслевые метаданные и модифицированы типовые объекты УНФ, их свойства, модули, макеты СКД, типизация и проч., как, например, документ «Сборка запасов», у которого добавлены новые реквизиты, команды и табличные части, а также доработаны свойства и модули:
Поэтому определение доработок, характерных только для УПО и накладывание их на конфигурацию СМ — процесс непростой.
Через стандартное «Сравнение и объединение» в конфигураторе это делать трудоемко. Во‑первых, таких объектов много. Во‑вторых, при сравнении модулей СМ и УПО мы не сможем увидеть соответствие доработанной процедуры «ПриСозданииНаСервре», т. к. в конфигурации-приемнике она будет называться «OnCreateAtServer», и восприниматься инструментом как новая процедура, а исходная как отсутствующая:
Можно было бы воспользоваться сторонними программами для объединения модулей, как например, KDiff3, однако многие коллизии в модулях ей также объединить не под силу:
Поэтому данный процесс было решено автоматизировать с помощью решения «1С‑Рарус: Сценарный обработчик конфигураций» (СОК). Это довольно гибкий инструмент для работы с конфигурациями. На примере пошаговой работы обновления конфигураций мы создали сценарий подготовки цельной конфигурации «CM(en)+УПО(ru)» для последующей локализации ее с помощью LT.
Объединение Company Management с «Управление предприятием общепита» СОКом
Сценарий автоматизации подготовки конфигурации состоит из нескольких последовательных шагов. Если рассматривать процесс в целом, то сперва мы добавим только новые объекты и их свойства, затем объединим модули, макеты, измененные свойства типовых объектов. А в завершении приведем конфигурацию в соответствие требованиям международного рынка:
1. Вычисляем добавленные объекты УПО к УНФ
В первую очередь мы определяем, какие объекты метаданных появились в УПО, в сравнении с конфигурацией поставщика (УНФ). Для этого воспользуемся платформенным отчетом о сравнении объединении двух конфигураций. Пользователь может его получить вручную — в форме сравнения/объединения двух конфигураций следует нажать «Действия» — «Отчет о сравнении конфигураций»:
Также файл сравнения можно получить автоматически — командой /CompareCfg.
/CompareCfg –FirstConfigurationType <тип конфигурации> [-FirstName <имя конфигурации>] [-FirstFile <путь к файлу>] [-FirstVersion <номер версии>] –SecondConfigurationType <тип конфигурации> [-SecondName <имя конфигурации>] [-SecondFile <путь к файлу>] [-SecondVersion <номер версии>] [-MappingRule <правило>] [-Objects <имя файла>] -ReportType <тип отчета> [-IncludeChangedObjects] [-IncludeDeletedObjects] [-IncludeAddedObjects] -ReportFormat <тип формата> -ReportFile <имя файла>
Подробнее о параметрах команды описано на сайте 1С:ИТС (its.1c.ru/db/v8324doc/bookmark/adm/TI000000527).
Теперь проанализируем созданный отчет. Структура отчета о сравнении известна, добавленные объекты маркируются определенным образом. А значит, можем написать алгоритм распознавания таких объектов и свойств:
На примере видно, что символ стрелка влево «<—» указывает на добавленные объекты, присутствующие только в конфигурации УПО. Необходимо выделить список таких объектов и после этого добавить их в англоязычную конфигурацию СМ. Вручную мы бы запускали процедуру сравнения‑объединения конфигурации СМ с конфигурацией УПО, проходили бы по дереву метаданных, выставляя необходимые галочки на нужных объектах и свойствах. Но обработка сравнения‑объединения также позволяет воспользоваться заранее подготовленным файлом настроек объединения, загрузив его в меню «Действия» — «Загрузить настройки из файла»:
А значит, для автоматизации процесса нам требуется создать соответствующий файл настроек для объединения MergeSettings.xml. На сайте ИТС структура файла довольно подробно расписана (its.1c.ru/db/v839doc/bookmark/adm/TI000000713). Согласно этой структуре сгенерируем файл настроек автоматически.
Файл будет содержать перечень метаданных для объединения, а именно перечень добавленных объектов, определенных ранее с помощью файла сравнения конфигураций. Для этого выполним алгоритм шага, который проходится по файлу сравнения‑объединения УНФ с УПО, выявляет добавленные объекты, а затем согласно им заполняет файл настроек объединения:
В сценарии этот шаг выполняется за несколько минут.
Здесь показываем детальнее, как работает скрипт по выявлению добавленных метаданных на языке python
from report_comparison_new import ReportBlocks import sys import xml.etree.ElementTree as ElementTree from xml.dom import minidom def prettify(elem): """Return a pretty-printed XML string for the Element. """ rough_string = ElementTree.tostring(elem, 'utf-8') parsed = minidom.parseString(rough_string) pretty_str = parsed.toprettyxml(indent="\t") result = '' for tmp_str in pretty_str.split('\n'): if tmp_str.strip() != '': result += tmp_str + '\n' result = result.replace('ns0:', '').replace(':ns0', '') return result def init_name_translation(translation_method=0): metadata_name = dict() dict_metadata_name = dict() if translation_method == 0: dict_metadata_name = metadata_name dict_metadata_name['Подсистема'] = 'Subsystem' dict_metadata_name['ОбщийМодуль'] = 'CommonModule' dict_metadata_name['ПараметрСеанса'] = 'SessionParameter' dict_metadata_name['Роль'] = 'Role' dict_metadata_name['ОбщийРеквизит'] = 'CommonAttribute' dict_metadata_name['ПланОбмена'] = 'ExchangePlan' dict_metadata_name['КритерийОтбора'] = 'FilterCriterion' dict_metadata_name['ПодпискаНаСобытие'] = 'EventSubscription' dict_metadata_name['РегламентноеЗадание'] = 'ScheduledJob' dict_metadata_name['ФункциональнаяОпция'] = 'FunctionalOption' dict_metadata_name['ПараметрФункциональнойОпции'] = 'FunctionalOptionsParameter' dict_metadata_name['ОпределяемыйТип'] = 'DefinedType' dict_metadata_name['ХранилищеНастроек'] = 'SettingsStorage' dict_metadata_name['ОбщаяФорма'] = 'CommonForm' dict_metadata_name['ОбщаяКоманда'] = 'CommonCommand' dict_metadata_name['ГруппаКоманд'] = 'CommandGroup' dict_metadata_name['ОбщийМакет'] = 'CommonTemplate' dict_metadata_name['ОбщаяКартинка'] = 'CommonPicture' dict_metadata_name['Картинка'] = 'Picture' dict_metadata_name['XTDOПакет'] = 'XDTOPackage' dict_metadata_name['WebСервис'] = 'WebService' dict_metadata_name['HTTPСервис'] = 'HTTPService' dict_metadata_name['WsСсылка'] = 'WsСсылка' dict_metadata_name['ЭлементСтиля'] = 'StyleItem' dict_metadata_name['Стиль'] = 'Style' dict_metadata_name['Язык'] = 'Language' dict_metadata_name['Константа'] = 'Constant' dict_metadata_name['Справочник'] = 'Catalog' dict_metadata_name['Документ'] = 'Document' dict_metadata_name['ЖурналДокументов'] = 'DocumentJournal' dict_metadata_name['Перечисление'] = 'Enum' dict_metadata_name['Отчет'] = 'Report' dict_metadata_name['Обработка'] = 'DataProcessor' dict_metadata_name['ПланВидовХарактеристик'] = 'ChartOfCharacteristicTypes' dict_metadata_name['ПланСчетов'] = 'ChartOfAccounts' dict_metadata_name['ПланВидовРасчета'] = 'ChartOfCalculationTypes' dict_metadata_name['РегистрСведений'] = 'InformationRegister' dict_metadata_name['РегистрНакопления'] = 'AccumulationRegister' dict_metadata_name['РегистрБухгалтерии'] = 'AccountingRegister' dict_metadata_name['РегистрРасчета'] = 'CalculationRegister' dict_metadata_name['БизнесПроцесс'] = 'BusinessProcess' dict_metadata_name['Задача'] = 'Task' dict_metadata_name['ВнешнийИсточникДанных'] = 'ExternalDataSource' dict_metadata_name['Команда'] = 'Command' dict_metadata_name['Макет'] = 'Template' dict_metadata_name['Реквизит'] = 'Attribute' dict_metadata_name['ТабличнаяЧасть'] = 'TabularSection' dict_metadata_name['Форма'] = 'Form' dict_metadata_name['Измерение'] = 'Dimension' dict_metadata_name['Модуль'] = 'Module' dict_metadata_name['Ресурс'] = 'Resource' dict_metadata_name['Таблица'] = "Template" dict_metadata_name['Поле'] = "Field" dict_metadata_name['Справочная информация'] = "Help" dict_metadata_name['Периодичность'] = "NumberPeriodicity" dict_metadata_name['Последовательность'] = "Sequence" dict_metadata_name['Документы, входящие в последовательность'] = "Documents" dict_metadata_name['Соответствие реквизитам документов'] = "DocumentMap" dict_metadata_name['Индексировать'] = "Indexing" dict_metadata_name['Модуль приложения'] = "ManagedApplicationModule" dict_metadata_name['Клиент'] = 'ClientManagedApplication' dict_metadata_name['Командный интерфейс конфигурации'] = "CommandInterface" dict_metadata_name['Поставщик'] = "Vendor" dict_metadata_name['Модуль сеанса'] = "SessionModule" dict_metadata_name['Модуль обычного приложения'] = "OrdinaryApplicationModule" dict_metadata_name['Краткая информация'] = "BriefInformation" dict_metadata_name['Подробная информация'] = "DetailedInformation" dict_metadata_name['Заставка'] = "Splash" dict_metadata_name['Адрес информации о конфигурации'] = "ConfigurationInformationAddress" dict_metadata_name['Включать в содержание справки'] = "IncludeHelpInContents" dict_metadata_name['Поле графической схемы'] = "Flowchart" dict_metadata_name['Основная форма выбора'] = 'DefaultChoiceForm' dict_metadata_name['Основная форма элемента'] = 'DefaultElementForm' dict_metadata_name['Основная форма документа'] = 'DefaultDocumentForm' dict_metadata_name['Основная форма списка'] = 'DefaultListForm' dict_metadata_name['Запрет незаполненных значений'] = 'DenyIncompleteValues' dict_metadata_name['Форма редакт записи'] = 'DefaultRecordForm' dict_metadata_name['Состав'] = 'Content' dict_metadata_name['Синоним'] = 'Synonym' dict_metadata_name['Имя'] = 'Name' dict_metadata_name['Комментарий'] = 'Comment' dict_metadata_name['Тип параметра команды'] = 'CommandParameterType' dict_metadata_name['Значение заполнения'] = 'FillValue' dict_metadata_name['Подсказка'] = 'ToolTip' dict_metadata_name['Пояснение'] = 'Explanation' dict_metadata_name['Источник'] = 'Source' dict_metadata_name['Конфигурация'] = 'Configuration' dict_metadata_name['UriПространстваИмен'] = 'Namespace' if translation_method == 1: for current_elem in dict_metadata_name.items(): metadata_name[current_elem[1]] = current_elem[0] return metadata_name def init_list_excluded_by_mergesettings(mergefilename, donotmerge_list): listescl = [] encoding = 'utf-16-le' # Split report on blocks blocks_index = ['Global'] blocks_object = ReportBlocks(mergefilename, encoding) blocks_object.split_file_on_blocks(blocks_index) metadata_name = init_name_translation() for key in blocks_index: block = blocks_object.blocks_file[key] if block.action == 'Удален': #слева в данном случае конфа поставщика, а справа-основная, поэтому смотрим чего ет в основной full_name = edit_name_element(block.name, blocks_object.block_types[block.block_type], metadata_name) listescl.append(full_name) append_objects_to_excluded(listescl, donotmerge_list) return listescl def append_objects_to_excluded(list_escl, donotmerge_list): with open(donotmerge_list, 'r') as donotmerge_listfile: for line in donotmerge_listfile.read().splitlines(): list_escl.append(line) return list_escl def edit_name_element(old_name, block_type, metadata_name): # Уберем лишние символы old_name = old_name.replace("(", "").replace(")", "").replace(" %СлужебныйКлюч%", "").replace(". ", " ") # Уберем префикс типа объекта и разобьем на части. arr_name = old_name[(len(block_type.name) if old_name.startswith(block_type.name) else 0):len(old_name)].split('.') len_arr_name = len(arr_name) # Сначала заменит тип объекта if (len_arr_name > 0) and (arr_name[0] in metadata_name): arr_name[0] = metadata_name[arr_name[0]] # Если пришел не объект, а например форма, то заменим ее. if (len_arr_name > 2) and (arr_name[2] in metadata_name): if arr_name[0] == "ExternalDataSource" and arr_name[2] == "Таблица": arr_name[2] = "Table" elif arr_name[0] == "InformationRegister" and arr_name[2] == "Периодичность": arr_name[2] = "InformationRegisterPeriodicity" else: arr_name[2] = metadata_name[arr_name[2]] # Если пришла например ТЧ, то заменим и внутренние содержание. if (len_arr_name > 4) and (arr_name[4] in metadata_name): arr_name[4] = metadata_name[arr_name[4]] if block_type.name == "ConfigurationProperty" and (arr_name[1] in metadata_name): arr_name[1] = metadata_name[arr_name[1]] if block_type.type == "Object": new_name = ".".join(arr_name) else: new_name = arr_name[-1] # В случае если это имя свойства, а не объекта, то нужно убрать пробелы и написать с большой буквы. # Пример: Основая форма объекта → ОсновнаяФормаОбъекта if new_name.find(".") == -1 and len(new_name.split(" ")) > 1: new_name = "".join(map(lambda x: x.capitalize(), new_name.split(" "))) return new_name def main(argv=None): # Получение списка параметров. if argv is None: argv = sys.argv # Определение имен входных/выходных файлов # argv[0] содержит имя текущего исполняемого файла # argv[1] содержит имя файла отчета о сравнении СтараяНовая # argv[2] содержит имя файла отчета о сравнении СтараяОсновная # argv[3] содержит имя входного файла настроек объединения # argv[4] содержит имя выходного файла настроек объединения # argv[5] содержит имя файла со списком исключаемых к объединению объектов if len(argv) >= 5: report_name, report_excluded_name, settings_name, output_name, donotmerge_list = argv[1:6] else: return # Кодировка файлов encoding = 'utf-16-le' list_excl = init_list_excluded_by_mergesettings(report_excluded_name, donotmerge_list) # Split report on blocks blocks_index = ['Global'] blocks_object = ReportBlocks(report_name, encoding) blocks_object.split_file_on_blocks(blocks_index) metadata_name = init_name_translation() # Change XML settings file xml_settings = ElementTree.parse(settings_name) root = xml_settings.getroot() objects = root.find('Objects') if not objects: # objects is None: objects = ElementTree.SubElement(root, 'Objects') for key in blocks_index: block = blocks_object.blocks_file[key] if block.action == 'Добавлен': full_name = edit_name_element(block.name, blocks_object.block_types[block.block_type], metadata_name) if full_name.find('УОП_') > -1: skipname = 0 for word_excl in list_excl: if full_name.find(word_excl) > -1: skipname = 1 if skipname == 0: new_object = ElementTree.SubElement(objects, 'Object') new_object.set('fullNameInSecondConfiguration', full_name) new_rule = ElementTree.SubElement(new_object, 'MergeRule') new_rule.text = 'GetFromSecondConfiguration' # Output to the file prettified_xml_string = prettify(root) with open(output_name, 'w', encoding='utf-8-sig') as output_file: output_file.write(prettified_xml_string) if __name__ == "__main__": sys.exit(main())
Подготовленный файл выглядит следующим образом:
Мы видим, что для справочника «Номенклатура» указано, что в основную конфигурацию (СМ) из второй конфигурации (УПО) будут затянуты новые реквизиты «УОП_СрокХранения», «УОП_ТарнаяЕдиницаИзмерения» и другие.
Объединить конфигурации по известной настройке (файлу) MergeSettings.xml можно командой /MergeCfg (ее описание и параметры its.1c.ru/db/v8324doc/bookmark/adm/TI000000832):
/MergeCfg <имя cf-файла> -Settings <имя файла настроек> [-EnableSupport | -DisableSupport] [-IncludeObjectsByUnresolvedRefs | -ClearUnresolvedRefs] [-force]
В результате выполнения данного этапа сценария получим конфигурацию с добавленными отраслевыми объектами и свойствами. Сравнив с изначальной версией конфигурации, увидим, что желаемый результат нами достигнут:
2. Вычисляем модифицированные типовые процедуры и функции
Следующий важный этап — определить модифицированные участки кода. Для этого конфигурации УНФ и УПО с помощью шага сценария выгружаются в каталоги, так алгоритм сможет работать с модулями как с файлами формата *.bsl:
Кроме того, благодаря сформированному ранее файлу сравнения УНФ с УПО нам известен перечень модифицированных объектов. Такие объекты в файле отчета о сравнении отмечены «***»:
В таком случае мы можем обойти всю структуру файлов по каталогам выгруженных конфигураций СМ и УПО, и проанализировать соответствующие модули СМ — УПО только измененных объектов.
Для этого в первую очередь необходимо выстроить соответствие объектов конфигураций СМ и УПО в выгрузках, т. к. в выгрузках наименования объектов будут различными:
<каталог выгрузки УПО>\Catalogs\Кассы\Forms\ФормаЭлемента\Ext\Form\Module.bsl
Теперь мы можем сопоставить пары файлов модулей СМ и УПО. Далее необходимо обработать тексты модулей в зависимости от степени модифицированности. Это означает, что в УПО присутствуют как новые отраслевые процедуры или функции, так и модифицированы типовые.
Для этого представим тексты модулей в виде логических текстовых единиц — областей, процедур, функций, а также областей объявления переменных. Так мы сможем работать с текстами модулей не посимвольно или построчно, а с помощью привычной объектной модели древовидного типа. Например, модуль формы документа «ЗаказПоставщику» можно представить в виде дерева:
Дерево в данном случае будет состоять из логических единиц — областей: «ОписаниеПеременных», «ОбработчикиСобытийФормы», «ОбработчикиСобытийЭлементовШапкиФормы», и т. д. И вложенных в них процедур «ПриСозданииНаСервере», «ПриОткрытии», «ПриЗакрытии» и других.
В таком случае новые (добавленные отраслевые) процедуры и функции, отсутствующие в конфигурации‑поставщике мы найдем по разнице объектных структур, а модифицированные модули — по различной их наполненности. В первом случае логика автоматизации ясна — новые текстовые логические единицы модулей в УПО (в сравнении с УНФ) необходимо добавить в соответствующие модули конфигурации
СМ. А вот с модифицированными процедурами и функциями ситуация выглядит несколько сложнее.
3. Полная замена модифицированных процедур и функций на основании словаря
Определение участков модифицированности также весьма не простая задача. Причина сложности заключается в том, что необходимо исчислить позиции начала и конца изменений в процедуре, а в рамках одной процедуры таких изменений может быть несколько. Как например, текст процедуры «ИнициализироватьДанныеПоЗапасамСборка» модуля менеджера документа «СборкаЗапасов», где доработан в том числе и текст запроса, причем в нескольких местах:
Также тексты процедур и функций русскоязычной УНФ и локализованной СМ могут отличаться по наполненности, последовательности команд и их количеству. Поэтому в случае обнаружения модифицированной логической текстовой единицы, ее проще заменить полностью на русскоязычную из УПО, а затем перевести согласно словарю в LT вместе с другими отраслевыми объектами. Стоит обратить внимание, что на этом этапе должны быть известны переводы наименований процедур и функций на русском и на английском языках, чтобы установить между ними соответствие. В этом нам сильно помогли коллеги из 1C:Vietnam, предоставив словари переводов СМ:
Благодаря переводам нам известно, что в конфигурации СМ для процедуры «ИнициализироватьДанныеПоЗапасамСборка» аналогом является процедура «InitializeDataByInventoryBuild».
Описанная процедура определения изменений и доработок в модулях, а также замена ряда процедур и функций на русскоязычные аналоги выполняется автоматически с помощью соответствующего шага в СОКе:
Если сравнить конфигурации до и после выполнения шага, то можно увидеть наглядно, что доработанный метод OnWriteAtServer модуля формы справочника Companies (Организации) был заменен на полностью русскоязычную процедуру «ПриЗаписиНаСервере», кроме этого были добавлены новые отраслевые методы в модуль:
4. Удаление «лишних» участков кода и объектов (ВЕТИС, ЕГАИС, Маркировка и т. д.)
Ранее уже упоминалось о том, что конфигурация Company Management предназначена для внедрения на международных проектах. Поэтому для нее избыточен реализованный в УПО учет в рамках российских государственных систем таких как ЕГАИС, ВЕТИС, МОТП и т. п. Поэтому разработчики CM уже исключили работу с данными подсистемами из своего решения. Однако отраслевые доработки УПО все еще их содержат. Поэтому после добавления объектов и модулей УПО в СМ, их следует редуцировать и избавить от ненужных подсистем и работы с ними. Например, в текстах добавленных процедур и функций могут содержаться подобные вставки:
//ЕГАИС … // Конец ЕГАИС Или //ИнтеграцияГосИС … // Конец ИнтеграцияГосИС
Пример вставок в процедуре «ПослеЗаписиНаСервере» формы документа «ПриходнаяНакладная»:
Такие вставки кода можно обнаружить, зная их идентификаторы «ЕГАИС», «ВЕТИС», ... , а также строку их окончания — «Конец <Идентификатор вставки>». А значит, процедуру редуцирования также можем автоматизировать. Для этого в сценарии СОКа разработан шаг очистки модулей от подобных конструкций:
Скрипт, срабатывающий на данном этапе, будет проходиться по всем файлам выгрузки конфигурации, содержащим модули (*.bsl), и удалять код, заключенный в такого вида скобки.
Этап 2: Подготовка словарей с помощью LangTool и машинного обучения
С помощью сценария СОКа мы уже автоматизировали процесс получения конфигурации вида: CM (en) + отраслевые доработки (ru)
Теперь, когда конфигурация подготовлена к переводу, воспользуемся инструментом, предназначенным для удобной и быстрой локализации решений — плагином LangTool. Для этого в первую очередь загрузим подготовленную конфигурацию в новый проект EDT.
Подготовка проекта к переводу
Для того чтобы перевести конфигурацию с помощью плагина LT необходимо в первую очередь установить в свойствах переводимого проекта «Вариант встроенного языка». Если конфигурация переводится с русского на английский язык, то значение встроенного языка в исходном проекте должно быть «Русский»:
После этого в ветку «Языки» метаданных конфигураций необходимо добавить язык, на который она будет переведена. Для этого нужно воспользоваться контекстным меню «Создать» — «Translate Language»:
В случае, если нужный язык уже присутствует в дереве метаданных, добавлять новый не нужно, а существующий следует сконвертировать в т. н. «Переводимый язык». Это делается с помощью контекстного меню Translation — Convert to translation language:
При этом в открывшемся окне доступна настройка словарей, куда будут сохранены текущие интерфейсные переводы, есть возможность выбрать отдельный или оставить существующий проект для работы с интерфейсными переводами. В нашей работе мы используем текущий проект, как предложено по умолчанию:
Поскольку для сохранения существующих в конфигураций переводов мы выбрали Interface context translations, то после этой процедуры в каталогах объектов проекта будут созданы файлы с контекстными переводами объектов, в которых будут содержаться уже заданные в конфигурации интерфейсные переводы ru-en:
Составление словарных пар LangTool и получение необходимых мест для перевода
Плагин LT также позволяет массово «собрать» все непереведенные конструкции, что очень удобно при составлении словарей для перевода проекта. Для этого в контекстном меню проекта следует выбрать Translation — Generate translation strings.
В открывшемся окне можно выбрать, какие конструкции для перевода следует собрать (только интерфейсные, переводы текстов модулей или же оба варианта сразу), для каких языков, а также выбрать действия по заполнению пустых переводов для таких конструкций:
В завершении процесса сбора непереведенных конструкций, такие конструкции будут записаны в соответствующие файлы словарей в каталог src проекта. В результате этого мы сможем из сформированных файлов собрать все конструкции, подлежащие переводу. На примере ниже такие конструкции не содержат перевод после знака равенства:
Концепт перевода и машинное обучение
При локализации больших проектов возникает вопрос, каким инструментом перевести большое количество непереведенных конструкций конфигурации. Если не рассматривать вариант услуг аутсорс переводчиков, то в первую очередь можно обратить внимание на таких гигантов как Яндекс, Майкрософт или Гугл. Компании предоставляют сервисы перевода, которыми можно воспользоваться после авторизации и получения настроек подключения к сервису. При выборе инструмента перевода мы обращали внимание на несколько критериев:
- Грамотные переводы в контексте нашей предметной области.
- Скорость перевода большого количества данных.
- Бесплатное использование.
И если пунктом 3 можно было поступиться, то грамотные переводы были естественно в приоритете. К примеру, мы локализуем решение общепита, где одним из элементов КБЖУ являются «белки». При переводе этого слова и всех конструкций, где оно бы встречалось, необходимо было, чтобы перевод на английский был бы proteins, а никак не squirrels.
Иначе для фразы «белкИ в пироге» мы бы получили «squirrels in a pie»:
А для фразы «вскрытие тары» нам автопереводчик одного из сервисов перевел «вскрытие» как autopsy, вот почему контекст так важен при переводе.
Важно отметить, что переводы «технических» идентификаторов, представленных записью CamelCase, должны формироваться для EnScript в определенном склонении и порядке слов, что зачастую противоречит правилам построения фраз в разговорном английском языке.
Например, элемент формы с наименованием «ГруппаДополнительнаяКоманднаяПанельСтраницыДанныеКонтрагента» должен быть переведен как GroupAdditionalCommandBarPagesCounterpartyData. В таком случае, код будет интуитивно понятен и читаем для англоязычных коллег, занятых внедрениями проекта.
Однако если перевести эту фразу в Яндекс Переводчике, мы получим «Group Additional Command Panel Of the Counterparty Data Page», Гугл выдаст следующий результат — «GroupAdditionalCommandPanelPagesAccount Data». Подобные варианты не удовлетворяли нашим требованиям. Поэтому мы решили воспользоваться возможностями методов машинного обучения (Machine Learning, далее ML). Они позволяют обучить модель ML необходимым нам переводам на основе переданных данных и совершенствоваться при помощи опыта, а не работать на основе явно запрограммированных алгоритмов.
В процессе выбора модели машинного обучения мы провели ряд исследований для определения наиболее подходящей под наши задачи. Для сравнения мы выбрали 2 бесплатные модели ML — М2М100 и MBart. Для начала необходимо было обучить модели наборам фраз, затем попробовать эти же фразы перевести, и после проверить корректность перевода. В качестве тестируемой выборки использовались стандартные названия процедур для основных событий формы. Перед подачей в модель было выполнено преобразование из CamelCase в раздельную запись: «ПриОткрытии» -> «при открытии»:
Обучение выполнялось на 1, 6 и 10 эпох. Обучение на 10 эпох показывало переобучение на такой маленькой выборке.
Результаты модели М2М100
Результаты модели MBart
После сравнения эталонных переводов с модельными переводами после 6 эпох (по сути итераций) обучения видно, что лучшее качество перевода показала модель MBart. Поэтому мы решили использовать ее для наших задач локализации.
MBart — это модель машинного обучения, которая использует трансформеры для решения задач перевода и генерации текста на разных языках. Кроме того, она уже предобучена на десятки языков, в том числе, на английский и вьетнамский. Однако, нам еще предстояло доучить её той самой логике построения идентификаторов 1С, а также научить корректно переводить отраслевые понятия из области общепита. Всю работу с моделью мы также встроили в сценарий решения СОК — это и обучение, и перевод, и составление словарей.
Преобразование из CamelCase в предложения СОКом
Для того чтобы корректно обучить модель MBart переводам, необходимо передать ей обучающую выборку фраз с эталонными переводами. Причем передаваемые языковые единицы должны быть представлены отдельными словами, а не в форме CamelCase для лучшего качества обучения. Поэтому в первую очередь разобьем фразы, записанные в CamelCase, на отдельные слова.
Например:
«УОП_ТендернаяМатрицаКоличествоКПеремещению» -> «УОП тендерная матрица количество к перемещению».
Такое преобразование необходимо сделать для переводимого и переведенного слова в файле для обучения.
За это отвечает отдельный шаг в сценарии:
В результате, получим файл для обучения модели ML следующего вида:
Обучение нейросетевой модели на основе предложений с учетом контекста
Для того чтобы модель ML смогла корректно переводить отраслевые переводы, а также нужным образом выстраивать порядок слов в переведенной фразе, нам необходимо ее этому обучить. Поэтому на этом этапе мы подготавливаем для модели специальный файл‑выборку, из, как минимум, сотни фраз. Это будет набор эталонных переводов, поэтому стоит внимательно отнестись к процессу подготовки данных пар ru=en.
К примеру, в качестве эталонного перевода запишем:
«УОП тендерная матрица количество к перемещению» = «RM tender matrix quantity for transfer».
Данная фраза выстроена в определенном порядке, поэтому после обучения модель будет распознавать «УОП» в начале предложения как «RM» и соответствующим образом ее переводить. Для того чтобы модель обучилась достаточно, но при этом не «переобучилась» (этот термин означает, что модель слишком хорошо подстраивается под обучающие данные, но плохо работает на новых данных, которые не участвовали в обучении), следует отправлять ей на обучение эталонные данные определенное количество эпох. Методом анализа влияния количества эпох обучения на выходной результат, мы пришли к выводу, что оптимальным количеством эпох в нашем случае будет 6.
Как показывает практика, наибольшие затраты на обучение приходятся на выпуск первого релиза продукта на иностранном языке. В последующие разы можно бегло анализировать результат перевода новых конструкций и, в случае необходимости, доучивать модель, корректировать предыдущие переводы, создавая обучающий набор с новыми эталонными фразами:
При первом использовании модели ее обучение заняло около 3 дней. Последующие разы запуск обучения необходим в виде лишь дообучения при возникновении новых, ранее неизвестных модели терминов.
Перевод с помощью модели
После того, как модель обучилась с помощью эталонной выборки, можно приступать к переводу всех оставшихся конструкций. В первом релизе они исчисляются тысячами фраз и перевод такого количества фраз может занять несколько часов. С помощью шага в СОКе мы готовим файл в формате JSON с идентификаторами к переводу, представленными в виде фраз с пробелами и отправляем в сервис с опубликованной моделью.
Файл JSON должен содержать коды исходного и целевого языков, а также массив фраз к переводу, например:
Данный файл отправляем с помощью шага в СОКе на вход модели ML:
И через некоторое время (зависит от размера файла) получаем файл с переводами от модели:
Поскольку модель в выходном файле возвращает переводы в том же порядке, что и непереведенные фразы на входе, то мы легко можем «склеить» эти 2 файла воедино для создания файла‑словаря:
После выполнения шага в СОКе получим файл в формате *.dict следующего содержания:
Обратное преобразование в CamelCase
После успешного перевода исходную и переведенную фразы нам необходимо обратно преобразовать в CamelCase в случаях, когда это необходимо (для переводов интерфейсных эта процедура не требуется). Для этого также воспользуемся шагом работы со словарями с СОКе:
И получим в результате файл вида:
На этом работа с файлом словаря завершена, он полностью подготовлен для использования в процессе перевода проекта.
Этап 3: Перевод с помощью Language Tool
Настройка проекта для перевода
На этапе получения фраз для перевода мы уже создали проект в EDT, определили в нем переводимый язык, проверили свойства корня конфигурации, убедившись, что «Вариант встроенного языка» установлен в значение «Русский». Следующим действием является настройка хранилища переводов для проекта, а также настройка свойств проекта для полного перевода конфигурации.
В контекстном меню проекта выберем «Открыть редактор проекта»:
В открывшемся окне нас будет интересовать вкладка Translations. Она доступна в том случае, если для EDT установлен плагин LT:
Как видно, в табличной части «Языки» у нас появилась строка с английским языком. Это произошло после того, как мы сконвертировали английский, как переводимый язык. Откроем строку с языком для редактирования и изменим поле Option на значение Model and Interface для того, чтобы включить возможность полного перевода конфигурации, а не только интерфейсной ее части:
В окне справа на панели отображается список используемых словарей — хранилищ перевода. Для перевода текстов интерфейсов вместе с платформенными конструкциями установим галочки также на словари DbView Terms и Platform context definition:
При этом, после редактирования списка словарей, в папке проекта Settings появится файл настройки словарей с расширением *.yml:
Его также можно настраивать вручную. В справке к LT приводится подробное описание по его настройке и наполнению.
Загрузка переводов в проект и перевод LT
Ранее уже упоминалось, что файлы хранилищ переводов бывают общие и контекстные. Общие словари используются для конфигурации в целом, контекстные же относятся только к тем объектом, для которых они определены. Контекстные словари располагаются в ветке самого объекта в структуре каталогов проекта, как например файл интерфейсных контекстных переводов BankClassifier_en.lstr для объекта справочника классификатора банков:
Общие же словари располагаются в папке src проекта:
После изменений в файлах словарей, изменения могут не подтянуться в проект для перевода, в таком случае можно выполнить очистку проекта для обновления проекта и всех его файлов в рабочем пространстве EDT:
Перевод проекта
Для запуска полного перевода проекта можно либо добавить переводимый проект в панели Translations редактора проекта:
Либо в контекстном меню проекта выбрать Translation — Translate configuration:
В открывшемся окне следует выбрать язык скрипта, язык по умолчанию, а также указать языки для интерфейса:
После запуска перевода в навигатор EDT добавится переводимый проект с заданным названием и запустится процесс его перевода:
В зависимости от размера конфигурации, мощностей ПК и выделенной памяти Java‑машины в свойствах проекта перевод будет занимать от нескольких минут до нескольких часов:
Можем убедиться в успешном завершении процесса перевода, открыв в переведенном проекте отраслевые объекты и их модули:
Осторожно! Сложности
При переводе конфигураций также можно столкнуться с некоторыми неприятностями. Далее приведены несколько моментов, которые нам усложнили процесс перевода.
Внешние источники данных
В конфигурации УПО для интеграции с RKeeper добавлен объект метаданных «Внешние источники данных» с определенным набором таблиц и полей:
После перевода в переведенном проекте таблицы отсутствуют:
К сожалению, не помогает и добавленный перевод в словари для наименований таблиц и полей:
Восстанавливать таблицы приходится вручную. После экспорта проекта помогает отредактировать файлы выгрузки конфигурации проекта по аналогии с русскоязычной выгрузкой.
Порченные xml файлы форм
Ещё одной неприятностью стали битые xml-файлы форм после выгрузки проекта. Выглядели они следующим образом:
Xml сохранялся таким образом, что без редактирования файла формы конфигуратор не давал загружать конфигурацию из каталогов, ссылаясь на критическую ошибку. Для разрешения ситуации приходилось копировать текст xml из выгрузки русскоязычного проекта, переводить согласно словарю и корректировать битый файл формы. К счастью, таких объектов немного — порядка 6, однако подобная работа замедляет процесс локализации. Провести исследование и определить, почему так происходит и частично файлы выгружаются в таком виде, нам пока не удалось.
Неверный перевод даже при наличии правильной пары
Также мы столкнулись с такого рода ситуацией — в общих словарях определен перевод для ключа «Организация» = «Company». Однако, после перевода конфигурации перевод осуществляется совершенно иначе и вместо Company в наименования реквизитов и в тексты моделей уже попадает другой перевод — Organization.
Примечательно, что поиск по контекстным словарям проекта не дал ответа на вопрос, откуда берется подобного рода перевод и почему игнорируется определенный в общем словаре перевод «Организация» = «Company»:
Перевод запросов с заменяемыми вставками %ТекстЗамены%
Для возможности формирования различных вариаций одного текста запроса в зависимости от входных данных, часто используют в тексте запроса строковые конструкции, заключенные в символы, которые определяют начало и конец такой заменяемой части запроса. Например, символ «%». Перевод таких запросов после первого символа «%» воспринимается LT не как набор полей и таблиц запроса, а как строковый литерал и для перевода следующих за «%» строк запроса требуется задавать перевод полной такой строки через словарь. Рассмотрим для примера доработанный модуль объекта документа SalesInvoice, где запрос после перевода выглядит следующим образом:
Как видим, после строки %RM_KeywordPriceFilter%, которая далее заменяется на определенное условие отбора, все последующие строки запроса не переведены, поскольку перестают идентифицироваться как строки текста запроса.
Заключение
Давайте подведем итоги после такого длительного, но интересного приключения под названием «Локализация»:
- Следует различать локализацию интерфейса и локализацию кода.
- Идея параллельной разработки на русском и английском языке одного и того же решения так трудоемка, что ее следует отбросить.
- Локализация полная проще, чем если вы локализуете часть своей разработки.
- Не существует простого способа локализации — это сложная задача.
От экспертов «1С-Рарус»