---
name: bsp-multilang
description: "Use this skill when the agent needs to add multilingual attribute support to a configuration object, register form handlers that display values in the user's current language, generate dynamic-list queries filtered by language, derive the language suffix used in attribute names, fill predefined elements with translations, build a list-choice handler for input-by-string on a multilingual object, or open a single-attribute editor in multiple languages. Do not use for translating message strings with NStr, configuring print-form templates in different languages, configuring external translation services, or setting up regional application parameters."
metadata:
  scope: bsp-3.1.11
  version: "3.1.11"
  layer: L3
  related_subsystems:
    - Мультиязычность
    - Печать
    - ПереводТекста
  related_skills:
    - bsp-fundamentals
    - bsp-base-common
    - bsp-print-reports
    - bsp-comms
---

# BSP Multilang (Мультиязычность: многоязычные реквизиты, формы и запросы)

Узкоспециализированный скил по подсистеме «Мультиязычность». Цель — помочь агенту правильно встраивать в прикладные объекты и формы **штатный** механизм БСП для хранения и отображения значений на разных языках, не изобретая собственные таблицы переводов, суффиксы и обёртки над `НСтр`.

Скил не описывает перевод сообщений через `НСтр` (это платформенный механизм, не БСП) и не покрывает сервисы машинного перевода (`ПереводТекстаНаДругиеЯзыки` — отдельная подсистема, Фаза 3). Здесь — **только** API подсистемы `Мультиязычность`: реквизиты шапки с суффиксом `ЯзыкN`, табличная часть `Представления`, суффиксы, форма ввода на разных языках, переключение полей запроса, ввод по строке.

## When to use

- Нужно встроить в прикладной справочник/документ поддержку нескольких языков для строковых реквизитов (например, `Наименование` на русском и английском) и подключить к форме кнопку «ввод на разных языках».
- Нужно сформировать запрос динамического списка так, чтобы при смене языка интерфейса пользователь видел значения на своём языке, без дублирования текста запроса в форме.
- Нужно реализовать `ОбработкаПолученияПредставления` и `ОбработкаПолученияПолейПредставления` для объекта, чтобы ссылка в 1С отображалась на текущем языке пользователя.
- Нужно обработать ввод по строке / автоподбор (`ОбработкаПолученияДанныхВыбора`) с поиском по всем языковым представлениям объекта.
- Нужно при начальном заполнении предопределённых элементов заполнить колонки `ИмяРеквизита_КодЯзыка` из строки в формате `НСтр`.
- Нужно программно узнать суффикс текущего языка (`Язык1` / `Язык2` / `""`) или по коду языка получить его суффикс для построения имени реквизита.

## Не использовать, если

- Задача — перевод строк сообщений/интерфейса через `НСтр("ru = '...'; en = '...'")` (платформенный механизм, к БСП не относится) — никаких вызовов из `Мультиязычность*` здесь не нужно.
- Нужно вывести печатную форму на дополнительном языке или перевести макет печатной формы — это подсистема «Печать» (`УправлениеПечатьюМультиязычность*`), отдельный скил `bsp-print-reports` (фаза 1).
- Нужно подключить машинный перевод через Яндекс.Переводчик / Google Переводчик — это подсистема «ПереводТекста» (`ПереводТекстаНаДругиеЯзыки*`), скил не покрывает.
- Нужно настроить региональные параметры приложения (часовой пояс, основной язык, выбор языков печатных форм через форму `ОбщаяФорма.РегиональныеНастройки`) — это административный сценарий, реализуется через интерфейс и `МультиязычностьКлиент.ОткрытьФормуРегиональныхНастроек` (⚠️ служебный, не для прикладного вызова).

## Core concepts

### Архитектура подсистемы

Подсистема `Мультиязычность` — это набор общих модулей, обслуживающих **два способа** хранения переводов в прикладных объектах:

1. **В шапке объекта** через дублирующие реквизиты с суффиксом `ЯзыкN` (`Наименование`, `НаименованиеЯзык1`, `НаименованиеЯзык2`). Используется для одиночных строковых полей.
2. **В табличной части `Представления`** (реквизиты `КодЯзыка` + локализуемые реквизиты). Используется, когда набора `Язык1`/`Язык2` недостаточно или когда нужна поддержка переменного числа языков без миграции структуры.

Оба способа **взаимоисключающие** на уровне одного объекта: если у объекта есть реквизит `Представления` (ТЧ), переводы в шапке для этого объекта не ведутся, и наоборот.

### Суффиксы и нумерация языков

- Основной язык — `Язык0` (его код — `ОбщегоНазначения.КодОсновногоЯзыка()`). Суффикс для основного языка — пустая строка: реквизит называется просто `Наименование` (без `Язык0`).
- Дополнительные языки нумеруются **по порядку включения**: `Язык1`, `Язык2`, … до `Язык5` (по умолчанию 2 дополнительных; конкретное число задаётся метаданными и константами). Суффикс формируется через `МультиязычностьКлиентСервер.СуффиксЯзыка(ПорядковыйНомерЯзыка)`.
- Код дополнительного языка (`"en"`, `"de"`, …) — это **BCP-47**-совместимый код из метаданных `Языки.КодЯзыка`. Маппинг «код → порядковый номер» идёт через константы вида `Язык1`, `Язык2` и функциональные опции.

### Три ключевых модуля

| Модуль | Контекст | Назначение |
|---|---|---|
| `МультиязычностьСервер` | Сервер | Обработчики формы (`ПриСозданииНаСервере`, `ПриЧтенииНаСервере`, `ПередЗаписьюНаСервере`, `ПриЧтенииПредставленийНаСервере`, `ОбработкаПолученияДанныхВыбора`), преобразование запросов (`ИзменитьПолеЗапросаПодТекущийЯзык`), информация о языках (`СведенияОЯзыках`, `КоличествоДополнительныхЯзыков`, `СуффиксЯзыка`, `СуффиксТекущегоЯзыка`, `ИспользуетсяДополнительныйЯзык`) |
| `МультиязычностьКлиентСервер` | Клиент + Сервер | Универсальный суффикс по номеру (`СуффиксЯзыка(ПорядковыйНомерЯзыка)`), обработчики представления объекта/ссылки (`ОбработкаПолученияПредставления`, `ОбработкаПолученияПолейПредставления`) |
| `МультиязычностьКлиент` | Клиент | Обработчик `ПриОткрытии` поля формы, открывающий `ОбщаяФорма.ВводНаРазныхЯзыках` |

Также существуют:

- `МультиязычностьПовтИсп` — кешируемые справочники (сведения о языках, наличие ТЧ `Представления`, мультиязычные реквизиты объекта). В прикладном коде **напрямую не вызывается** — используется через обёртки `МультиязычностьСервер` / `МультиязычностьКлиентСервер`.
- `МультиязычностьПереопределяемый` — точка расширения: `ПриОпределенииНастроек(Настройки)` позволяет прикладной конфигурации подстроить поведение (например, отключить ввод на разных языках для определённых ролей).

### `НСтр` vs `МультиязычностьСервер.ЗаполнитьМультиязычныйРеквизит`

`НСтр` — платформенная функция, выбирает из строки вида `"ru = '...'; en = '...'"` подстроку по коду языка **текущего сеанса**. Используется для **интерфейсных строк** (сообщения, заголовки кнопок, подписи в макетах).

`МультиязычностьСервер.ЗаполнитьМультиязычныйРеквизит` — заполняет **реквизиты объекта** (колонки таблицы значений при начальном заполнении) по списку кодов языков. Используется при начальном заполнении предопределённых элементов, когда нужно **сохранить** переводы в ИБ.

> ❗ Подставлять перевод в **реквизит объекта** через `НСтр` в прикладном коде некорректно: `НСтр` зависит от текущего языка сеанса, а реквизит хранит данные — у разных пользователей будет разное содержимое.

### Fallback на основной язык

Если значение на дополнительном языке не заполнено, **все** методы подсистемы автоматически возвращают значение на основном языке. Это сделано в трёх местах:

- `ЗаполнитьМультиязычныйРеквизит` — при пустом `НСтр(ИсходнаяСтрока, КодЯзыка)` подставляет `НСтр(ИсходнаяСтрока, КодОсновногоЯзыка())`.
- `ОбработкаПолученияПредставления` — если на текущем языке пусто, возвращает основной; если и он пуст — оставляет стандартную обработку.
- `ПриЧтенииНаСервере` / `ПриЧтенииПредставленийНаСервере` — то же поведение при первичном заполнении формы.

Прикладной код **не должен** сам реализовывать fallback на основной язык — это дублирование логики и источник рассинхрона.

### BCP-47 и не-буквенные коды

Платформа 1С в `Метаданные.Языки.КодЯзыка` хранит **двухбуквенный** код (ISO 639-1), регистр — нижний (`"ru"`, `"en"`). Для региональных вариантов (`"en-US"`, `"pt-BR"`) БСП хранит их в константах `Язык1`, `Язык2` (тип `Строка`), а подсистема при подстановке в суффикс **не различает** региональные варианты одного языка (`"en-US"` и `"en"` оба мапятся на тот же суффикс). При работе с кодами языков в прикладном коде — нормализуйте ввод к нижнему регистру (`НРег(КодЯзыка)`) и не полагайтесь на региональный суффикс.

## Key methods

| Метод | Сигнатура | Сервер/Клиент | Назначение | Пример вызова |
|---|---|---|---|---|
| `МультиязычностьСервер.ПриСозданииНаСервере` | `ПриСозданииНаСервере(Форма, Объект = Неопределено, ИмяОбъекта = Неопределено)` | Сервер | Подключает кнопку открытия и обработчик `Подключаемый_Открытие` к полям формы, содержащим локализуемые реквизиты. Для форм списка — модифицирует текст запроса динамического списка | `МультиязычностьСервер.ПриСозданииНаСервере(ЭтотОбъект, Объект, "Объект");` |
| `МультиязычностьСервер.ПриЧтенииНаСервере` | `ПриЧтенииНаСервере(Форма, ТекущийОбъект, ИмяОбъекта = Неопределено)` | Сервер | Заполняет реквизиты формы значениями на текущем языке пользователя. Вызывается из `ПриЧтенииНаСервере` управляемой формы | `МультиязычностьСервер.ПриЧтенииНаСервере(ЭтотОбъект, Объект, "Объект");` |
| `МультиязычностьСервер.ПередЗаписьюНаСервере` | `ПередЗаписьюНаСервере(ТекущийОбъект)` | Сервер | Сохраняет значения на текущем языке, раздвигая их в реквизиты `ЯзыкN` или строки ТЧ `Представления`. Вызывается из `ПередЗаписьюНаСервере` формы или из модуля объекта при программной записи | `МультиязычностьСервер.ПередЗаписьюНаСервере(Объект);` |
| `МультиязычностьСервер.ПриЧтенииПредставленийНаСервере` | `ПриЧтенииПредставленийНаСервере(Объект)` | Сервер | Заполняет мультиязычные реквизиты объекта на текущем языке при **программном** чтении вне формы (например, в обработке заполнения). Принимает уже полученный `*Объект` | `МультиязычностьСервер.ПриЧтенииПредставленийНаСервере(СправочникОбъект);` |
| `МультиязычностьСервер.ОбработкаПолученияДанныхВыбора` | `ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Знач Параметры, СтандартнаяОбработка, ОбъектМетаданных)` | Сервер | Подменяет стандартный выбор по строке: ищет по всем языковым представлениям реквизита. Вызывается из одноимённого обработчика модуля менеджера | `МультиязычностьСервер.ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка, МетаданныеОбъекта);` |
| `МультиязычностьСервер.ИзменитьПолеЗапросаПодТекущийЯзык` | `ИзменитьПолеЗапросаПодТекущийЯзык(ТекстЗапроса, ИмяПоля)` | Сервер | Дописывает суффикс `ЯзыкN` к указанному полю в тексте запроса (поддерживает `ИмяПоля КАК Псевдоним`). Ничего не делает, если текущий язык — основной | `МультиязычностьСервер.ИзменитьПолеЗапросаПодТекущийЯзык(ТекстЗапроса, "Свойства.Заголовок");` |
| `МультиязычностьКлиентСервер.СуффиксЯзыка` | `СуффиксЯзыка(ПорядковыйНомерЯзыка = Неопределено)` | Клиент + Сервер | Возвращает суффикс (`"Язык1"`, `"Язык2"`, …) по порядковому номеру. Без параметра — базовая строка `"Язык"` | `Суффикс = МультиязычностьКлиентСервер.СуффиксЯзыка(1);` |
| `МультиязычностьСервер.СуффиксЯзыка` | `СуффиксЯзыка(КодЯзыка)` | Сервер | Обратная операция: по коду языка (`"en"`, `"de"`) возвращает суффикс. Пустая строка, если язык не используется в приложении | `Суффикс = МультиязычностьСервер.СуффиксЯзыка("en");` |
| `МультиязычностьКлиентСервер.ОбработкаПолученияПредставления` | `ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка, ИмяРеквизита = "Наименование")` | Клиент + Сервер | Подменяет представление ссылки/объекта на значение реквизита на текущем языке. Вызывается из одноимённого обработчика модуля менеджера. Если значение на текущем языке пустое — fallback на основной | `МультиязычностьКлиентСервер.ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка, "Наименование");` |
| `МультиязычностьСервер.ЗаполнитьМультиязычныйРеквизит` | `ЗаполнитьМультиязычныйРеквизит(Элемент, ИмяРеквизита, ИсходнаяСтрока, КодыЯзыков = Неопределено)` | Сервер | Заполняет колонки `ИмяРеквизита_КодЯзыка` строки таблицы значений из строки в формате `НСтр`. Если перевод отсутствует — берётся значение на основном языке. Используется при начальном заполнении предопределённых | `МультиязычностьСервер.ЗаполнитьМультиязычныйРеквизит(СтрокаЗаполнения, "Наименование", "ru = 'Пример'; en = 'Example'", КодыЯзыков);` |

## Patterns

### 1. Форма объекта с мультиязычными реквизитами

Минимальный набор вызовов в модуле формы:

```bsl
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    МультиязычностьСервер.ПриСозданииНаСервере(ЭтотОбъект, Объект, "Объект");
КонецПроцедуры

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    МультиязычностьСервер.ПриЧтенииНаСервере(ЭтотОбъект, ТекущийОбъект, "Объект");
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    МультиязычностьСервер.ПередЗаписьюНаСервере(ТекущийОбъект);
КонецПроцедуры
```

Параметр `ИмяОбъекта = "Объект"` — стандартное имя основного реквизита формы. Для форм записей регистров заменить на `"Запись"`, для форм списков — на `"Список"`. Если в форме используется нестандартное имя реквизита — передать его явно.

### 2. Модуль менеджера: представление и ввод по строке

```bsl
Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка) Экспорт
    МультиязычностьКлиентСервер.ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка);
КонецПроцедуры

Процедура ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка) Экспорт
    МультиязычностьКлиентСервер.ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка);
КонецПроцедуры

Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка) Экспорт
    МультиязычностьСервер.ОбработкаПолученияДанныхВыбора(
        ДанныеВыбора, Параметры, СтандартнаяОбработка, Метаданные());
КонецПроцедуры
```

Эти три обработчика подключают мультиязычное представление ссылки и поиск по строке сразу на все языки. Без них ссылка на элемент будет отображаться только на основном языке.

### 3. Динамический список с переключением языка

```bsl
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    МультиязычностьСервер.ПриСозданииНаСервере(ЭтотОбъект);
    // ПриСозданииНаСервере сам модифицирует ТекстЗапроса динамического списка "Список"
КонецПроцедуры
```

Для ручного управления (например, когда текст запроса строится программно):

```bsl
ТекстЗапроса = "ВЫБРАТЬ СправочникНоменклатура.Наименование, СправочникНоменклатура.Артикул ИЗ Справочник.Номенклатура КАК СправочникНоменклатура";
МультиязычностьСервер.ИзменитьПолеЗапросаПодТекущийЯзык(ТекстЗапроса, "СправочникНоменклатура.Наименование");
```

После выполнения `Наименование` превратится в `НаименованиеЯзык1` (если текущий язык — первый дополнительный) или останется `Наименование` (если основной).

### 4. Начальное заполнение предопределённых значений

```bsl
Процедура ПриНачальномЗаполненииЭлементов(Элементы) Экспорт
    КодыЯзыков = Новый Массив;
    КодыЯзыков.Добавить("en");
    КодыЯзыков.Добавить("de");
    
    Для Каждого Элемент Из Элементы Цикл
        МультиязычностьСервер.ЗаполнитьМультиязычныйРеквизит(
            Элемент,
            "Наименование",
            "ru = 'Демо: пример'; en = 'Demo: example'; de = 'Demo: Beispiel'",
            КодыЯзыков);
    КонецЦикла;
КонецПроцедуры
```

Метод сам обеспечит fallback на основной язык для языков, отсутствующих в `ИсходнаяСтрока`.

## Anti-patterns

### ❌ Хранить переводы в одном строковом реквизите с разделителем

```bsl
// ❌ Не используется в БСП, ломает ввод по строке, индексирование, поиск
Реквизит "Наименование" = "Пример||Example||Beispiel";
```

✅ Добавить реквизиты `НаименованиеЯзык1`, `НаименованиеЯзык2` (при хранении в шапке) или табличную часть `Представления` с реквизитом `КодЯзыка`.

### ❌ Хардкодить суффикс `Язык1` в прикладном коде

```bsl
// ❌ Сломается, если порядок языков изменится (пользователь включил «en» первым, а в конфигурации первым идёт «de»)
ИмяРеквизита = "НаименованиеЯзык1";
```

✅ Получать суффикс программно:

```bsl
ИмяРеквизита = "Наименование" + МультиязычностьСервер.СуффиксТекущегоЯзыка();
// или по конкретному коду
ИмяРеквизита = "Наименование" + МультиязычностьСервер.СуффиксЯзыка("en");
```

### ❌ Реализовывать fallback на основной язык вручную

```bsl
// ❌ Дублирует логику БСП, рассинхронизируется при обновлениях
Если ПустаяСтрока(Объект.НаименованиеЯзык1) Тогда
    ЗначениеДляПользователя = Объект.Наименование;
Иначе
    ЗначениеДляПользователя = Объект.НаименованиеЯзык1;
КонецЕсли;
```

✅ Использовать `ОбработкаПолученияПредставления` — fallback уже встроен, и `ПриЧтенииНаСервере` сам подставит основной язык при первичном открытии формы.

### ❌ Вызывать `НСтр` для заполнения реквизитов объекта

```bsl
// ❌ НСтр зависит от текущего языка СЕАНСА, а не от языка, на котором редактируется объект
Объект.НаименованиеЯзык1 = НСтр("en = 'Example'");
```

✅ Использовать `ЗаполнитьМультиязычныйРеквизит` с явным списком кодов языков — он не зависит от текущего сеанса и заполняет все колонки сразу.

### ❌ Подключать `ПриСозданииНаСервере` формы списка без вызова БСП

```bsl
// ❌ Список будет показывать значения на основном языке для всех пользователей
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // ...свой код без МультиязычностьСервер.ПриСозданииНаСервере(ЭтотОбъект)...
КонецПроцедуры
```

✅ Всегда вызывать `МультиязычностьСервер.ПриСозданииНаСервере(ЭтотОбъект)` **первым** в обработчике формы списка — метод модифицирует `ТекстЗапроса` динамического списка, и при собственном коде после него текст может быть перезаписан.

### ❌ Вызывать `МультиязычностьПовтИсп` напрямую из прикладного кода

```bsl
// ❌ ПовтИсп предназначен для внутреннего использования БСП
Количество = МультиязычностьПовтИсп.КоличествоДополнительныхЯзыков();
```

✅ Использовать обёртку `МультиязычностьСервер.КоличествоДополнительныхЯзыков()` — она сама читает из `ПовтИсп` и не сломается при изменении внутреннего контракта.

## How to explore deeper

### Где искать модули в выгрузке конфигурации

- `CommonModules/МультиязычностьСервер/Ext/Module.bsl` — серверные обработчики форм, запросы, информация о языках.
- `CommonModules/МультиязычностьКлиентСервер/Ext/Module.bsl` — суффиксы и представление объекта (работает и на клиенте, и на сервере).
- `CommonModules/МультиязычностьКлиент/Ext/Module.bsl` — открытие формы ввода на разных языках по кнопке.
- `CommonModules/МультиязычностьПовтИсп/Ext/Module.bsl` — кешируемые справочники (не вызывается напрямую из прикладного кода).
- `CommonModules/МультиязычностьПереопределяемый/Ext/Module.bsl` — точка расширения `ПриОпределенииНастроек(Настройки)`.

### Grep-шаблоны

```text
# Найти все обработчики мультиязычных форм в модуле (полный список)
^(Функция|Процедура) (ПриСозданииНаСервере|ПриЧтенииНаСервере|ПередЗаписьюНаСервере|ПриЧтенииПредставленийНаСервере|ОбработкаПолученияДанныхВыбора|ИзменитьПолеЗапросаПодТекущийЯзык)

# Служебный API — обратная совместимость НЕ гарантируется
^#Область СлужебныйПрограммныйИнтерфейс

# Список реквизитов с языковым суффиксом в любом модуле (Язык1, Язык2, ...)
Язык[12]\b
```

### Смежные модули, которые могут потребоваться

- `СтроковыеФункцииКлиентСерверЛокализация` — локализация строковых утилит (символы национального алфавита). Не покрывается этим скилом.
- `УправлениеПечатьюМультиязычность*` — печатные формы на разных языках. Делегировать в `bsp-print-reports`.
- `ПереводТекстаНаДругиеЯзыки*` — сервисы машинного перевода. Делегировать в `bsp-comms`.
- `СтандартныеПодсистемыКлиент.ПараметрКлиента("КоличествоДополнительныхЯзыков")` — клиентский кеш количества языков (используется внутри `МультиязычностьКлиент`; в прикладном коде не нужен).

### На что обратить внимание в дереве метаданных

- У корневого `Справочник`/`Документ`: реквизиты шапки с суффиксом `Язык1`, `Язык2` (тип `Строка`) — основной способ хранения переводов.
- Табличная часть `Представления` с реквизитами `КодЯзыка` (тип `Строка`) + локализуемые реквизиты — альтернативный способ (для больших наборов локализаций).
- Константы `Язык1`, `Язык2` (тип `Строка`) — текущий выбранный код дополнительного языка.
- Функциональные опции `ИспользоватьЯзык1`, `ИспользоватьЯзык2` (тип `Булево`) — включён ли язык в приложении.
- Регистр сведений, хранящий основной язык: `Константа.ОсновнойЯзык` (тип `Строка`).
- В формах: наличие `ПараметрыМультиязычныхРеквизитов` (служебный реквизит формы, создаётся автоматически `ПриСозданииНаСервере`).
