При разработке интернациональных или многоязычных конфигураций на платформе 1С:Предприятие 8 разработчики часто сталкиваются с необходимостью вывода текстовых сообщений, заголовков окон и предупреждений на языке текущего пользователя. Стандартный механизм платформы НСтр() отлично подходит для статических строк, зашитых в код. Однако, если перед нами стоит задача сделать словарь динамическим (с возможностью редактирования фраз в пользовательском режиме без изменения метаданных), стандартных функций становится недостаточно.
В этой статье мы подробно разберем, как спроектировать оптимальную архитектуру мультиязычного словаря, избежав при этом главной проблемы подобных решений — постоянных ресурсоемких обращений к серверу базы данных при выводе каждой строки на клиенте.
На первый взгляд, самое очевидное решение — создать справочник СловарьИнтерфейса с реквизитами для каждого поддерживаемого языка (например, КодСтроки, ТекстRU, ТекстEN) и получать оттуда значения по мере необходимости. Рассмотрим, почему классический подход "запрос к справочнику при каждом выводе сообщения" является крайне неэффективным:
ПолучитьСтрокуИзСловаря("ИмяКлюча") лишает разработчика возможности использовать стандартную проверку конфигурации. Если вы случайно удалите ключ или сделаете в нем опечатку, платформа не выдаст ошибку на этапе компиляции — сбой произойдет непосредственно у пользователя в процессе работы.Ниже мы подробно разберем три оптимальных сценария реализации мультиязычного словаря, которые сводят нагрузку на сервер к минимуму или полностью исключают серверные вызовы в процессе работы пользователя.
---
Наиболее производительный вариант — загрузить словарь фраз в оперативную память клиентского приложения один раз при старте системы. Для этого мы задействуем экспортную переменную в модуле управляемого приложения.
Рассмотрим по шагам, как реализовать этот механизм:
Шаг 1. Объявление глобальной переменной
Откроем Модуль управляемого приложения и объявим экспортную переменную, которая будет хранить структуру нашего словаря для текущей локали пользователя:
Перем глСловарьИнтерфейса Экспорт;
Шаг 2. Инициализация словаря при запуске
В этом же модуле в обработчике события ПриНачалеРаботыСистемы вызовем серверную функцию для получения подготовленного словаря. Сервер определит язык сеанса пользователя, соберет нужные строки и вернет на клиент уже готовую структуру:
Процедура ПриНачалеРаботыСистемы()
#Если Клиент Тогда
// Загружаем словарь фраз один раз при старте
глСловарьИнтерфейса = ПолучитьСловарьИнтерфейсаНаСервере();
#КонецЕсли
КонецПроцедуры
&НаСервере
Функция ПолучитьСловарьИнтерфейсаНаСервере()
Словарь = Новый Структура;
ТекущийКодЯзыка = ТекущийЯзыкСистемы(); // Например, "ru" или "en"
// В данном примере мы заполняем словарь программно,
// но здесь может быть чтение из Справочника или общего макета JSON
Если ТекущийКодЯзыка = "ru" Тогда
Словарь.Вставить("Приветствие", "Добро пожаловать в систему!");
Словарь.Вставить("ЗапросНомера", "Пожалуйста, введите номер документа:");
Словарь.Вставить("ОшибкаСохранения", "Не удалось сохранить изменения.");
Иначе // Английский по умолчанию
Словарь.Вставить("Приветствие", "Welcome to the system!");
Словарь.Вставить("ЗапросНомера", "Please enter the document number:");
Словарь.Вставить("ОшибкаСохранения", "Failed to save changes.");
КонецЕсли;
Возврат Словарь;
КонецФункции
Шаг 3. Использование на клиенте
Теперь в любой клиентской процедуре модуля формы (или общего клиентского модуля) мы можем мгновенно обращаться к нашему словарю без единого серверного вызова:
&НаКлиенте
Процедура ПоказатьДиалогВвода()
Оповещение = Новый ОписаниеОповещения("ПослеВводаДанных", ЭтотОбъект);
// Получаем строку из глобальной переменной на клиенте
ТекстЗаголовка = глСловарьИнтерфейса.ЗапросНомера;
ПоказатьВводСтроки(Оповещение, "", ТекстЗаголовка);
КонецПроцедуры
---
Хранить строки перевода прямо в программном коде — не всегда удобно. Использование макетов табличных документов (MXL), как часто предлагают на форумах, избыточно нагружает память сервера при парсинге ячеек. Современным стандартом является использование формата JSON, хранящегося в текстовом макете конфигурации.
Проанализируем этот подход подробнее:
Шаг 1. Создание макета
Создадим общий макет с типом Текстовый документ и назовем его СловарьИнтерфейсаJSON. Наполним его структурированными данными:
{
"ru": {
"Welcome": "Добро пожаловать!",
"EnterNumber": "Введите номер:",
"Error": "Произошла ошибка!"
},
"en": {
"Welcome": "Welcome!",
"EnterNumber": "Enter number:",
"Error": "An error occurred!"
}
}
Шаг 2. Чтение и парсинг макета на сервере
Для быстрого разбора JSON воспользуемся встроенными механизмами платформы. Напишем функцию в общем серверном модуле:
Функция ПолучитьЛокализованныйСловарь(КодЯзыка) Экспорт
Макет = ПолучитьОбщийМакет("СловарьИнтерфейсаJSON");
ТекстJSON = Макет.ПолучитьТекст();
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстJSON);
// Платформа 1С мгновенно преобразует JSON-строку в соответствие/структуру
ПолныйСловарь = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
// Возвращаем только ветку для текущего языка пользователя
Если ПолныйСловарь.Свойство(КодЯзыка) Тогда
Возврат ПолныйСловарь[КодЯзыка];
Иначе
// Возврат английского по умолчанию, если язык не найден
Возврат ПолныйСловарь["en"];
КонецЕсли;
КонецФункции
Этот способ позволяет легко редактировать тексты переводов прямо в конфигураторе, не затрагивая исполняемый bsl-код, и гарантирует высокую скорость чтения данных.
---
Если вы не хотите использовать глобальные переменные в модуле управляемого приложения, отличной альтернативой станет механизм модулей повторного использования значений (кэшируемые модули).
Выясним, как настроить этот вариант работы:
СловарьПовтИсп.На время сеанса.Напишем внутри модуля функцию формирования словаря:
Функция ПолучитьСловарьДляЛокали(КодЯзыка) Экспорт
Словарь = Новый Соответствие;
// Наполняем структуру словаря
Если КодЯзыка = "ru" Тогда
Словарь.Вставить("Btn_Save", "Сохранить");
Словарь.Вставить("Btn_Cancel", "Отмена");
Иначе
Словарь.Вставить("Btn_Save", "Save");
Словарь.Вставить("Btn_Cancel", "Cancel");
КонецЕсли;
Возврат Словарь;
КонецФункции
Теперь при вызове этой функции из клиентского кода:
&НаКлиенте
Процедура НастройкаКнопок()
КодЯзыка = ТекущийЯзыкСистемы();
// Первый вызов за сеанс выполнит тело функции,
// последующие вызовы будут мгновенно возвращать результат из кэша
Словарь = СловарьПовтИсп.ПолучитьСловарьДляЛокали(КодЯзыка);
Элементы.КнопкаЗаписать.Заголовок = Словарь.Получить("Btn_Save");
КонецПроцедуры
Важное преимущество данного подхода: Кэш "на время сеанса" автоматически уничтожается при завершении работы пользователя, не оставляя следов в оперативной памяти устройства, а повторные обращения к функции происходят за доли миллисекунды.
---
Если ваша самописная конфигурация разрабатывается с использованием Библиотеки Стандартных Подсистем (БСП), изобретать собственный велосипед не нужно. В БСП встроена готовая и мощная подсистема «Мультиязычные данные».
Она позволяет:
МультиязычностьСервер.Для перевода элементов интерфейса самой платформы (заголовков реквизитов, кнопок, стандартных команд) лучше использовать стандартный механизм локализации 1С через синонимы объектов метаданных и файлы локализации, редактируемые в среде разработки 1С:EDT (Enterprise Development Tools) с плагином LangTool.