diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1
index 2104925a..2f8947b1 100644
--- a/.claude/skills/skd-compile/scripts/skd-compile.ps1
+++ b/.claude/skills/skd-compile/scripts/skd-compile.ps1
@@ -1,4 +1,4 @@
-# skd-compile v1.33 — Compile 1C DCS from JSON
+# skd-compile v1.34 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -827,10 +827,10 @@ function Emit-Field {
if ($fieldDef.restrict) {
$f.restrict = @($fieldDef.restrict)
}
- # Parse appearance
+ # Parse appearance (сохраняем значение как есть — может быть string или multilang dict)
if ($fieldDef.appearance) {
foreach ($prop in $fieldDef.appearance.PSObject.Properties) {
- $f.appearance[$prop.Name] = "$($prop.Value)"
+ $f.appearance[$prop.Name] = $prop.Value
}
}
if ($fieldDef.presentationExpression) {
@@ -935,14 +935,15 @@ function Emit-Field {
X "$indent`t"
foreach ($key in $f.appearance.Keys) {
$val = $f.appearance[$key]
- X "$indent`t`t"
- X "$indent`t`t`t$(Esc-Xml $key)"
- if ($key -eq "ГоризонтальноеПоложение") {
- X "$indent`t`t`t$(Esc-Xml $val)"
+ # ГоризонтальноеПоложение требует специального xsi:type (v8ui:HorizontalAlign), не строка
+ if ($key -eq "ГоризонтальноеПоложение" -and -not ($val -is [hashtable] -or $val -is [System.Collections.IDictionary] -or $val -is [PSCustomObject])) {
+ X "$indent`t`t"
+ X "$indent`t`t`t$(Esc-Xml $key)"
+ X "$indent`t`t`t$(Esc-Xml "$val")"
+ X "$indent`t`t"
} else {
- X "$indent`t`t`t$(Esc-Xml $val)"
+ Emit-AppearanceValue -key $key -val $val -indent "$indent`t`t"
}
- X "$indent`t`t"
}
X "$indent`t"
}
@@ -2025,24 +2026,34 @@ function Emit-AppearanceValue {
param([string]$key, $val, [string]$indent)
X "$indent"
- if ($val -is [PSCustomObject] -and $val.use -ne $null -and $val.use -eq $false) {
- X "$indent`tfalse"
- X "$indent`t$(Esc-Xml $key)"
- $actualVal = "$($val.value)"
- } else {
- X "$indent`t$(Esc-Xml $key)"
- $actualVal = "$val"
+
+ # Распознаём wrapper {use: false, value: ...} (необходимо отличать от multilang dict).
+ $useWrapper = $false
+ $innerVal = $val
+ if ($val -is [PSCustomObject] -and $val.PSObject.Properties['use'] -and $val.use -eq $false -and $val.PSObject.Properties['value']) {
+ $useWrapper = $true
+ $innerVal = $val.value
}
- # Auto-detect value type
- if ($actualVal -match '^(style|web|win):') {
- X "$indent`t$(Esc-Xml $actualVal)"
- } elseif ($actualVal -eq "true" -or $actualVal -eq "false") {
- X "$indent`t$actualVal"
- } elseif ($key -eq "Текст" -or $key -eq "Заголовок" -or $key -eq "Формат") {
- Emit-MLText -tag "dcscor:value" -text $actualVal -indent "$indent`t"
+ if ($useWrapper) { X "$indent`tfalse" }
+ X "$indent`t$(Esc-Xml $key)"
+
+ # Multilang dict ({"ru": "...", "en": "..."}) → LocalStringType независимо от ключа.
+ $isMultilang = ($innerVal -is [hashtable]) -or ($innerVal -is [System.Collections.IDictionary]) -or ($innerVal -is [PSCustomObject])
+ if ($isMultilang) {
+ Emit-MLText -tag "dcscor:value" -text $innerVal -indent "$indent`t"
} else {
- X "$indent`t$(Esc-Xml $actualVal)"
+ $actualVal = "$innerVal"
+ if ($actualVal -match '^(style|web|win):') {
+ X "$indent`t$(Esc-Xml $actualVal)"
+ } elseif ($actualVal -eq "true" -or $actualVal -eq "false") {
+ X "$indent`t$actualVal"
+ } elseif ($key -eq "Текст" -or $key -eq "Заголовок" -or $key -eq "Формат") {
+ # Строковые ключи, традиционно эмитятся как LocalStringType (даже если только ru).
+ Emit-MLText -tag "dcscor:value" -text $actualVal -indent "$indent`t"
+ } else {
+ X "$indent`t$(Esc-Xml $actualVal)"
+ }
}
X "$indent"
}
diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py
index 3df68cd6..524fc040 100644
--- a/.claude/skills/skd-compile/scripts/skd-compile.py
+++ b/.claude/skills/skd-compile/scripts/skd-compile.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# skd-compile v1.33 — Compile 1C DCS from JSON
+# skd-compile v1.34 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -627,10 +627,10 @@ def emit_field(lines, field_def, indent):
# Parse restrictions
if field_def.get('restrict'):
f['restrict'] = list(field_def['restrict'])
- # Parse appearance
+ # Parse appearance (сохраняем значение как есть — может быть string или multilang dict)
if field_def.get('appearance'):
for k, v in field_def['appearance'].items():
- f['appearance'][k] = str(v)
+ f['appearance'][k] = v
if field_def.get('presentationExpression'):
f['presentationExpression'] = str(field_def['presentationExpression'])
# attrRestrict
@@ -714,13 +714,14 @@ def emit_field(lines, field_def, indent):
if f.get('appearance') and len(f['appearance']) > 0:
lines.append(f'{indent}\t')
for key, val in f['appearance'].items():
- lines.append(f'{indent}\t\t')
- lines.append(f'{indent}\t\t\t{esc_xml(key)}')
- if key == '\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435':
- lines.append(f'{indent}\t\t\t{esc_xml(val)}')
+ # \u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e xsi:type, \u043d\u0435 \u0441\u0442\u0440\u043e\u043a\u0430
+ if key == '\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435' and not isinstance(val, dict):
+ lines.append(f'{indent}\t\t')
+ lines.append(f'{indent}\t\t\t{esc_xml(key)}')
+ lines.append(f'{indent}\t\t\t{esc_xml(str(val))}')
+ lines.append(f'{indent}\t\t')
else:
- lines.append(f'{indent}\t\t\t{esc_xml(val)}')
- lines.append(f'{indent}\t\t')
+ emit_appearance_value(lines, key, val, f'{indent}\t\t')
lines.append(f'{indent}\t')
# PresentationExpression
@@ -1686,23 +1687,31 @@ def emit_order(lines, items, indent, skip_auto=False):
def emit_appearance_value(lines, key, val, indent):
lines.append(f'{indent}')
- if isinstance(val, dict) and val.get('use') is False:
- lines.append(f'{indent}\tfalse')
- lines.append(f'{indent}\t{esc_xml(key)}')
- actual_val = str(val.get('value', ''))
- else:
- lines.append(f'{indent}\t{esc_xml(key)}')
- actual_val = str(val)
+ # \u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0451\u043c wrapper {use: false, value: ...} \u2014 \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043e\u0431\u0430 \u043a\u043b\u044e\u0447\u0430.
+ use_wrapper = False
+ inner_val = val
+ if isinstance(val, dict) and 'use' in val and val['use'] is False and 'value' in val:
+ use_wrapper = True
+ inner_val = val['value']
- # Auto-detect value type
- if re.match(r'^(style|web|win):', actual_val):
- lines.append(f'{indent}\t{esc_xml(actual_val)}')
- elif actual_val == 'true' or actual_val == 'false':
- lines.append(f'{indent}\t{actual_val}')
- elif key in ('\u0422\u0435\u043a\u0441\u0442', '\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a', '\u0424\u043e\u0440\u043c\u0430\u0442'):
- emit_mltext(lines, f'{indent}\t', 'dcscor:value', actual_val)
+ if use_wrapper:
+ lines.append(f'{indent}\tfalse')
+ lines.append(f'{indent}\t{esc_xml(key)}')
+
+ # Multilang dict ({"ru": "...", "en": "..."}) \u2192 LocalStringType \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u043a\u043b\u044e\u0447\u0430.
+ if isinstance(inner_val, dict):
+ emit_mltext(lines, f'{indent}\t', 'dcscor:value', inner_val)
else:
- lines.append(f'{indent}\t{esc_xml(actual_val)}')
+ actual_val = str(inner_val) if inner_val is not None else ''
+ if re.match(r'^(style|web|win):', actual_val):
+ lines.append(f'{indent}\t{esc_xml(actual_val)}')
+ elif actual_val == 'true' or actual_val == 'false':
+ lines.append(f'{indent}\t{actual_val}')
+ elif key in ('\u0422\u0435\u043a\u0441\u0442', '\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a', '\u0424\u043e\u0440\u043c\u0430\u0442'):
+ # \u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e \u044d\u043c\u0438\u0442\u044f\u0442\u0441\u044f \u043a\u0430\u043a LocalStringType (\u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e ru).
+ emit_mltext(lines, f'{indent}\t', 'dcscor:value', actual_val)
+ else:
+ lines.append(f'{indent}\t{esc_xml(actual_val)}')
lines.append(f'{indent}')
diff --git a/tests/skills/cases/skd-compile/snapshots/field-appearance-and-presentation/Template.xml b/tests/skills/cases/skd-compile/snapshots/field-appearance-and-presentation/Template.xml
index 935cc8ca..4593b927 100644
--- a/tests/skills/cases/skd-compile/snapshots/field-appearance-and-presentation/Template.xml
+++ b/tests/skills/cases/skd-compile/snapshots/field-appearance-and-presentation/Template.xml
@@ -36,7 +36,7 @@
РастягиватьПоГоризонтали
- true
+ true
diff --git a/tests/skills/cases/skd-decompile/appearance-multilang-value.json b/tests/skills/cases/skd-decompile/appearance-multilang-value.json
new file mode 100644
index 00000000..4cbaa1bf
--- /dev/null
+++ b/tests/skills/cases/skd-decompile/appearance-multilang-value.json
@@ -0,0 +1,42 @@
+{
+ "name": "Appearance с multilang значением (Формат={ru,en}) — round-trip",
+ "preRun": [
+ {
+ "script": "skd-compile/scripts/skd-compile",
+ "input": {
+ "dataSets": [{
+ "name": "Тест",
+ "query": "ВЫБРАТЬ * ИЗ Справочник.Сотрудники",
+ "fields": [
+ {
+ "field": "ДатаДокумента",
+ "type": "date",
+ "appearance": {
+ "Формат": { "ru": "ДЛФ=D", "en": "DLF=D" }
+ }
+ }
+ ]
+ }],
+ "settingsVariants": [
+ {
+ "name": "Основной",
+ "settings": {
+ "conditionalAppearance": [
+ {
+ "selection": ["ДатаДокумента"],
+ "appearance": {
+ "Формат": { "ru": "ДЛФ=DT", "en": "DLF=DT" }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "Template.xml" },
+ "cwd": "{workDir}"
+ }
+ ],
+ "params": { "templatePath": "Template.xml" },
+ "outputPath": "decompiled.json"
+}
diff --git a/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/Template.xml b/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/Template.xml
new file mode 100644
index 00000000..3ecb7672
--- /dev/null
+++ b/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/Template.xml
@@ -0,0 +1,79 @@
+
+
+
+ ИсточникДанных1
+ Local
+
+
+ Тест
+
+ ДатаДокумента
+ ДатаДокумента
+
+ xs:dateTime
+
+ Date
+
+
+
+
+ Формат
+
+
+ ru
+ ДЛФ=D
+
+
+ en
+ DLF=D
+
+
+
+
+
+ ИсточникДанных1
+ ВЫБРАТЬ * ИЗ Справочник.Сотрудники
+
+
+ Основной
+
+
+ ru
+ Основной
+
+
+
+
+
+
+
+ ДатаДокумента
+
+
+
+
+ Формат
+
+
+ ru
+ ДЛФ=DT
+
+
+ en
+ DLF=DT
+
+
+
+
+
+
+
+
+
diff --git a/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/decompiled.json b/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/decompiled.json
new file mode 100644
index 00000000..47864f78
--- /dev/null
+++ b/tests/skills/cases/skd-decompile/snapshots/appearance-multilang-value/decompiled.json
@@ -0,0 +1,40 @@
+{
+ "dataSets": [
+ {
+ "name": "Тест",
+ "query": "ВЫБРАТЬ * ИЗ Справочник.Сотрудники",
+ "fields": [
+ {
+ "field": "ДатаДокумента",
+ "type": "date",
+ "appearance": {
+ "Формат": {
+ "ru": "ДЛФ=D",
+ "en": "DLF=D"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "settingsVariants": [
+ {
+ "name": "Основной",
+ "settings": {
+ "conditionalAppearance": [
+ {
+ "selection": [
+ "ДатаДокумента"
+ ],
+ "appearance": {
+ "Формат": {
+ "ru": "ДЛФ=DT",
+ "en": "DLF=DT"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file