GeWuYou 51de7f1102 feat(config): 添加配置验证和YAML解析功能
- 实现了配置模式解析器,支持递归对象/数组/标量树结构
- 添加了可编辑字段收集功能,支持批量编辑标量和数组类型
- 实现了YAML解析器,支持嵌套对象、标量数组和对象数组
- 添加了YAML注释提取功能,将注释映射到逻辑字段路径
- 实现了基于模式的示例YAML配置生成功能
- 添加了扩展端验证诊断功能,支持中英文错误消息
- 实现了表单更新应用功能,支持标量、数组和对象数组更新
- 添加了批处理数组值解析和模式枚举值标准化功能
- 实现了YAML标量格式化和引号移除功能
- 添加了完整的模式节点验证,支持数值约束、长度限制和模式匹配
- 实现了多语言验证消息本地化功能
- 添加了YAML标记化和块解析功能
- 实现了唯一性检查和比较键构建功能
2026-04-09 17:06:43 +08:00

259 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const {ValidationMessageKeys} = require("./localizationKeys");
/**
* Create a tiny in-process localizer for the extension runtime and webview.
* VS Code contribution points use package.nls files, while runtime strings are
* resolved here so the preview panel and prompts stay readable for both
* Simplified Chinese and English users.
*
* @param {string | undefined} language VS Code UI language.
* @returns {{languageTag: string, isChinese: boolean, t: (key: string, params?: Record<string, string | number>) => string}} Localizer.
*/
function createLocalizer(language) {
const normalizedLanguage = String(language || "en").toLowerCase();
const isChinese = normalizedLanguage.startsWith("zh");
const isTraditionalChinese =
normalizedLanguage === "zh-tw" ||
normalizedLanguage === "zh-hk" ||
normalizedLanguage === "zh-mo" ||
normalizedLanguage.startsWith("zh-hant");
const isSimplifiedChinese = isChinese && !isTraditionalChinese;
const languageTag = isTraditionalChinese
? normalizedLanguage
: isSimplifiedChinese
? "zh-CN"
: "en";
const dictionary = isTraditionalChinese
? enMessages
: isSimplifiedChinese
? zhCnMessages
: enMessages;
return {
languageTag,
isChinese: isSimplifiedChinese,
t(key, params) {
const template = dictionary[key] || enMessages[key] || key;
return template.replace(/\{([A-Za-z0-9_]+)\}/gu, (match, token) => {
if (!params || !Object.prototype.hasOwnProperty.call(params, token)) {
return match;
}
return String(params[token]);
});
}
};
}
const enMessages = {
"tree.noConfigDirectory.label": "No config directory",
"tree.noConfigDirectory.description": "Set gframeworkConfig.configPath or create the directory.",
"tree.fileDescription.schema": "schema",
"tree.fileDescription.schemaMissing": "schema missing",
"command.openRaw.title": "Open Raw",
"message.schemaNotFound": "Matching schema file was not found.",
"message.formSaved": "Config file saved from form preview.",
"message.formInitialized": "Example config initialized from the schema.",
"message.initializeFromSchemaConfirm": "Initializing from the schema will replace the current configuration and may discard unsaved form changes. Do you want to continue?",
"message.noYamlFilesInDomain": "No YAML config files were found in the selected domain.",
"message.batchEditNeedsSchema": "Batch edit requires a matching schema file for the selected domain.",
"message.batchEditNoEditableFields": "No top-level scalar or scalar-array fields were found in the matching schema.",
"message.batchEditNoChanges": "Batch edit did not change any selected config files.",
"message.batchEditUpdated": "Batch updated {count} config file(s) in '{domain}'.",
"message.referenceSchemaMissing": "The referenced schema '{refTable}.schema.json' was not found.",
"message.referenceDomainMissing": "The referenced config domain '{refTable}' was not found.",
"message.referenceValueMissing": "The referenced config '{refValue}' was not found in '{refTable}'.",
"diagnostic.schemaMissing": "Matching schema file not found: {schemaPath}",
"quickPick.batchEdit.title": "Batch Edit: {domain}",
"quickPick.batchEdit.placeholder": "Select the config files to update.",
"quickPick.batchEditFields.title": "Batch Edit Fields: {domain}",
"quickPick.batchEditFields.placeholder": "Select the fields to apply across the chosen files.",
"detail.required": "required",
"detail.refTable": "ref: {refTable}",
"detail.arrayType": "array<{itemType}>",
"detail.default": "default",
"button.cancel": "Cancel",
"button.initializeFromSchemaConfirm": "Initialize from schema",
"input.batchArray.title": "Batch Edit Array: {field}",
"input.batchArray.prompt": "Enter comma-separated items for '{fieldKey}' (expected array<{itemType}>). Leave empty to clear the array.",
"input.batchArray.placeholder.allowedItems": "Allowed items: {values}",
"input.batchArray.placeholder.default": "Default: {value}",
"quickPick.batchField.title": "Batch Edit Field: {field}",
"quickPick.batchField.placeholder": "Select a value for '{fieldKey}'.",
"input.batchField.title": "Batch Edit Field: {field}",
"input.batchField.prompt": "Enter the new value for '{fieldKey}' (expected {type}).",
"input.batchField.placeholder.refTable": "Ref table: {refTable}",
"webview.panelTitle": "Config Form: {fileName}",
"webview.meta.file": "File: {fileName}",
"webview.meta.schema": "Schema: {schemaPath}",
"webview.meta.schemaMissing": "Schema missing: {schemaPath}",
"webview.emptyState": "No editable schema-bound fields were detected. Use raw YAML for unsupported shapes.",
"webview.button.save": "Save Form",
"webview.button.openRaw": "Open Raw YAML",
"webview.button.initialize": "Initialize Example",
"webview.badge.required": "required",
"webview.help.summary": "Edit values, comments, and references here. Use raw YAML when you need unsupported structures or exact formatting control.",
"webview.comment.label": "YAML comment",
"webview.ref.openSchema": "Open Ref Schema",
"webview.ref.openDomain": "Open Ref Domain",
"webview.ref.openValue": "Open Ref File",
"webview.objectArray.item": "Item",
"webview.objectArray.itemNumber": "Item {index}",
"webview.objectArray.hint": "Each item uses the object schema below.",
"webview.objectArray.add": "Add Item",
"webview.objectArray.remove": "Remove",
"webview.array.hint": "One item per line. Expected type: {itemType}",
"webview.hint.default": "Default: {value}",
"webview.hint.allowed": "Allowed: {values}",
"webview.hint.minimum": "Minimum: {value}",
"webview.hint.exclusiveMinimum": "Exclusive minimum: {value}",
"webview.hint.maximum": "Maximum: {value}",
"webview.hint.exclusiveMaximum": "Exclusive maximum: {value}",
"webview.hint.multipleOf": "Multiple of: {value}",
"webview.hint.minLength": "Min length: {value}",
"webview.hint.maxLength": "Max length: {value}",
"webview.hint.pattern": "Pattern: {value}",
"webview.hint.minItems": "Min items: {value}",
"webview.hint.maxItems": "Max items: {value}",
"webview.hint.uniqueItems": "Items must be unique",
"webview.hint.itemMinimum": "Item minimum: {value}",
"webview.hint.itemExclusiveMinimum": "Item exclusive minimum: {value}",
"webview.hint.itemMaximum": "Item maximum: {value}",
"webview.hint.itemExclusiveMaximum": "Item exclusive maximum: {value}",
"webview.hint.itemMultipleOf": "Item multiple of: {value}",
"webview.hint.itemMinLength": "Item min length: {value}",
"webview.hint.itemMaxLength": "Item max length: {value}",
"webview.hint.itemPattern": "Item pattern: {value}",
"webview.hint.refTable": "Ref table: {refTable}",
"webview.unsupported.array": "Unsupported array shapes are currently raw-YAML-only in the form preview.",
"webview.unsupported.type": "{type} fields are currently raw-YAML-only.",
"webview.unsupported.objectArrayMixed": "Object-array items must be mappings. Use raw YAML if the current file mixes scalar and object items.",
"webview.unsupported.nestedObjectArray": "Nested object-array fields are currently raw-YAML-only inside the object-array editor.",
[ValidationMessageKeys.exclusiveMaximumViolation]: "Property '{displayPath}' must be less than {value}.",
[ValidationMessageKeys.exclusiveMinimumViolation]: "Property '{displayPath}' must be greater than {value}.",
[ValidationMessageKeys.maximumViolation]: "Property '{displayPath}' must be less than or equal to {value}.",
[ValidationMessageKeys.maxItemsViolation]: "Property '{displayPath}' must contain at most {value} items.",
[ValidationMessageKeys.maxLengthViolation]: "Property '{displayPath}' must be at most {value} characters long.",
[ValidationMessageKeys.minimumViolation]: "Property '{displayPath}' must be greater than or equal to {value}.",
[ValidationMessageKeys.multipleOfViolation]: "Property '{displayPath}' must be a multiple of {value}.",
[ValidationMessageKeys.minItemsViolation]: "Property '{displayPath}' must contain at least {value} items.",
[ValidationMessageKeys.minLengthViolation]: "Property '{displayPath}' must be at least {value} characters long.",
[ValidationMessageKeys.patternViolation]: "Property '{displayPath}' must match pattern '{value}'.",
[ValidationMessageKeys.uniqueItemsViolation]: "Property '{displayPath}' duplicates earlier array item '{duplicatePath}', but uniqueItems is required.",
[ValidationMessageKeys.enumMismatch]: "Property '{displayPath}' must be one of: {values}.",
[ValidationMessageKeys.expectedArray]: "Property '{displayPath}' is expected to be an array.",
[ValidationMessageKeys.expectedObject]: "{subject} is expected to be an object.",
[ValidationMessageKeys.expectedScalarShape]: "Property '{displayPath}' is expected to be '{schemaType}', but the current YAML shape is '{yamlKind}'.",
[ValidationMessageKeys.expectedScalarValue]: "Property '{displayPath}' is expected to be '{schemaType}', but the current scalar value is incompatible.",
[ValidationMessageKeys.missingRequired]: "Required property '{displayPath}' is missing.",
[ValidationMessageKeys.unknownProperty]: "Property '{displayPath}' is not declared in the matching schema."
};
const zhCnMessages = {
"tree.noConfigDirectory.label": "未找到配置目录",
"tree.noConfigDirectory.description": "请设置 gframeworkConfig.configPath或先创建该目录。",
"tree.fileDescription.schema": "已匹配 schema",
"tree.fileDescription.schemaMissing": "缺少 schema",
"command.openRaw.title": "打开原始文件",
"message.schemaNotFound": "未找到匹配的 schema 文件。",
"message.formSaved": "已从表单预览保存配置文件。",
"message.formInitialized": "已根据 schema 初始化示例配置。",
"message.initializeFromSchemaConfirm": "从 schema 初始化会替换当前配置,并且可能丢失尚未保存的表单修改。是否继续?",
"message.noYamlFilesInDomain": "所选配置域中没有找到 YAML 配置文件。",
"message.batchEditNeedsSchema": "批量编辑要求该配置域存在匹配的 schema 文件。",
"message.batchEditNoEditableFields": "匹配的 schema 中没有可批量编辑的顶层标量字段或标量数组字段。",
"message.batchEditNoChanges": "批量编辑未修改任何已选配置文件。",
"message.batchEditUpdated": "已在“{domain}”中批量更新 {count} 个配置文件。",
"message.referenceSchemaMissing": "未找到引用的 schema 文件“{refTable}.schema.json”。",
"message.referenceDomainMissing": "未找到引用的配置域“{refTable}”。",
"message.referenceValueMissing": "在“{refTable}”中未找到引用配置“{refValue}”。",
"diagnostic.schemaMissing": "未找到匹配的 schema 文件:{schemaPath}",
"quickPick.batchEdit.title": "批量编辑:{domain}",
"quickPick.batchEdit.placeholder": "选择要更新的配置文件。",
"quickPick.batchEditFields.title": "批量编辑字段:{domain}",
"quickPick.batchEditFields.placeholder": "选择要应用到已选文件的字段。",
"detail.required": "必填",
"detail.refTable": "引用表:{refTable}",
"detail.arrayType": "数组<{itemType}>",
"detail.default": "默认值",
"button.cancel": "取消",
"button.initializeFromSchemaConfirm": "从 schema 初始化",
"input.batchArray.title": "批量编辑数组:{field}",
"input.batchArray.prompt": "请输入“{fieldKey}”的逗号分隔项(期望类型:数组<{itemType}>)。留空表示清空数组。",
"input.batchArray.placeholder.allowedItems": "允许项:{values}",
"input.batchArray.placeholder.default": "默认值:{value}",
"quickPick.batchField.title": "批量编辑字段:{field}",
"quickPick.batchField.placeholder": "为“{fieldKey}”选择一个值。",
"input.batchField.title": "批量编辑字段:{field}",
"input.batchField.prompt": "请输入“{fieldKey}”的新值(期望类型:{type})。",
"input.batchField.placeholder.refTable": "引用表:{refTable}",
"webview.panelTitle": "配置表单:{fileName}",
"webview.meta.file": "文件:{fileName}",
"webview.meta.schema": "Schema{schemaPath}",
"webview.meta.schemaMissing": "缺少 Schema{schemaPath}",
"webview.emptyState": "当前没有可编辑的 schema 绑定字段。对于暂不支持的结构,请回退到原始 YAML 编辑。",
"webview.button.save": "保存表单",
"webview.button.openRaw": "打开原始 YAML",
"webview.button.initialize": "初始化示例",
"webview.badge.required": "必填",
"webview.help.summary": "你可以在这里直接编辑字段值、YAML 注释和关联跳转。遇到暂不支持的复杂结构或需要精确保留排版时,请回退到原始 YAML。",
"webview.comment.label": "YAML 注释",
"webview.ref.openSchema": "打开引用 Schema",
"webview.ref.openDomain": "打开引用配置域",
"webview.ref.openValue": "打开引用文件",
"webview.objectArray.item": "对象项",
"webview.objectArray.itemNumber": "对象项 {index}",
"webview.objectArray.hint": "每一项都按下面的对象 schema 编辑。",
"webview.objectArray.add": "新增对象项",
"webview.objectArray.remove": "删除",
"webview.array.hint": "每行一个元素。期望类型:{itemType}",
"webview.hint.default": "默认值:{value}",
"webview.hint.allowed": "允许值:{values}",
"webview.hint.minimum": "最小值:{value}",
"webview.hint.exclusiveMinimum": "开区间最小值:{value}",
"webview.hint.maximum": "最大值:{value}",
"webview.hint.exclusiveMaximum": "开区间最大值:{value}",
"webview.hint.multipleOf": "倍数约束:{value}",
"webview.hint.minLength": "最小长度:{value}",
"webview.hint.maxLength": "最大长度:{value}",
"webview.hint.pattern": "正则模式:{value}",
"webview.hint.minItems": "最少元素数:{value}",
"webview.hint.maxItems": "最多元素数:{value}",
"webview.hint.uniqueItems": "元素必须唯一",
"webview.hint.itemMinimum": "元素最小值:{value}",
"webview.hint.itemExclusiveMinimum": "元素开区间最小值:{value}",
"webview.hint.itemMaximum": "元素最大值:{value}",
"webview.hint.itemExclusiveMaximum": "元素开区间最大值:{value}",
"webview.hint.itemMultipleOf": "元素倍数约束:{value}",
"webview.hint.itemMinLength": "元素最小长度:{value}",
"webview.hint.itemMaxLength": "元素最大长度:{value}",
"webview.hint.itemPattern": "元素正则模式:{value}",
"webview.hint.refTable": "引用表:{refTable}",
"webview.unsupported.array": "当前表单预览暂不支持这种数组结构,请改用原始 YAML。",
"webview.unsupported.type": "当前表单预览暂不支持 {type} 字段,请改用原始 YAML。",
"webview.unsupported.objectArrayMixed": "对象数组中的每一项都必须是映射对象。如果当前文件混用了标量项和对象项,请改用原始 YAML。",
"webview.unsupported.nestedObjectArray": "对象数组编辑器内暂不支持更深层的对象数组字段,请改用原始 YAML。",
[ValidationMessageKeys.exclusiveMaximumViolation]: "属性“{displayPath}”必须小于 {value}。",
[ValidationMessageKeys.exclusiveMinimumViolation]: "属性“{displayPath}”必须大于 {value}。",
[ValidationMessageKeys.maximumViolation]: "属性“{displayPath}”必须小于或等于 {value}。",
[ValidationMessageKeys.maxItemsViolation]: "属性“{displayPath}”最多只能包含 {value} 个元素。",
[ValidationMessageKeys.maxLengthViolation]: "属性“{displayPath}”长度必须不超过 {value} 个字符。",
[ValidationMessageKeys.minimumViolation]: "属性“{displayPath}”必须大于或等于 {value}。",
[ValidationMessageKeys.multipleOfViolation]: "属性“{displayPath}”必须是 {value} 的整数倍。",
[ValidationMessageKeys.minItemsViolation]: "属性“{displayPath}”至少需要包含 {value} 个元素。",
[ValidationMessageKeys.minLengthViolation]: "属性“{displayPath}”长度必须至少为 {value} 个字符。",
[ValidationMessageKeys.patternViolation]: "属性“{displayPath}”必须匹配正则模式“{value}”。",
[ValidationMessageKeys.uniqueItemsViolation]: "属性“{displayPath}”与更早的数组元素“{duplicatePath}”重复;该数组要求元素唯一。",
[ValidationMessageKeys.enumMismatch]: "属性“{displayPath}”必须是以下值之一:{values}。",
[ValidationMessageKeys.expectedArray]: "属性“{displayPath}”应为数组。",
[ValidationMessageKeys.expectedObject]: "{subject}",
[ValidationMessageKeys.expectedScalarShape]: "属性“{displayPath}”应为“{schemaType}”,但当前 YAML 结构是“{yamlKind}”。",
[ValidationMessageKeys.expectedScalarValue]: "属性“{displayPath}”应为“{schemaType}”,但当前标量值不兼容。",
[ValidationMessageKeys.missingRequired]: "缺少必填属性“{displayPath}”。",
[ValidationMessageKeys.unknownProperty]: "属性“{displayPath}”未在匹配的 schema 中声明。"
};
module.exports = {
createLocalizer
};