From 77dff0c0af68966fe311ec71d492960a98938f1c Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Thu, 4 Jun 2026 00:39:45 +0800 Subject: [PATCH 1/6] docs: add Chinese translation of README --- docs/README.zh-CN.md | 280 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 docs/README.zh-CN.md diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md new file mode 100644 index 0000000..1a85f66 --- /dev/null +++ b/docs/README.zh-CN.md @@ -0,0 +1,280 @@ +# Cat5Dev + +Cat5Dev 是一款用于在 VSCode 和 CATIA V5 之间同步 VBA 模块的 VSCode 扩展。 + +--- + +## 功能特点 + +### 拉取 / 推送 (Pull / Push) VBA 模块 +在 CATIA V5 和本地工作区之间同步 VBA 模块。 + +- **拉取 (Pull)** — 将目标 CATIA 项目中的所有 VBA 模块导出到本地的 `modules/` 文件夹。 +- **推送 (Push)** — 将本地 VBA 模块导入回 CATIA(仅传输有改动的文件,未改动的模块将被跳过以提高速度)。 + +### 选择目标项目 +通过快速选择对话框,选择要同步的 CATIA VBA 项目。 +所选项目名称将保存在 `cat5dev.toml` 的 `[project]` 部分中,并在不同的会话间持久保留。 + +### 模块树形视图 (Module Tree View) +活动栏 (Activity Bar) 中的专用面板将显示工作区的当前状态。 + +- 显示目标项目名称 +- 模块按类型分组: + - **标准模块** (`.bas_utf`) + - **类模块** (`.cls_utf`) + - **用户窗体** (`.frm_utf`) +- 单击任何模块即可在编辑器中将其打开 +- 更改目标项目时,树形视图会自动更新 +- 右键单击任何模块以访问上下文菜单选项: + - **重命名 (Rename)** — 重命名模块文件 + - **删除 (Delete)** — 删除模块文件(有确认提示) + - **复制路径 (Copy Path)** — 将完整的文件路径复制到剪贴板 + +### 工具栏按钮 +面板标题栏中提供了快速访问按钮: + +| 图标 | 操作 | +|------|--------| +| ☁️↓ | 从 CATIA 拉取 (Pull) | +| ☁️↑ | 推送到 CATIA (Push) | +| ⚙️ | 选择目标项目 | +| 🔄 | 刷新模块列表 | +| 🌐 | 切换语言 | + +### 多语言支持 +本扩展支持 **日语 (日本語)**、**英语 (English)** 和 **中文 (简体中文)**。 +- 单击面板标题栏中的 🌐 按钮切换语言 +- 语言偏好设置保存在 `cat5dev.toml` 的 `[project]` 部分下 +- 所有 UI 消息、TreeView 标签和工具提示都会被翻译 +- 更改语言后需要重新加载 VSCode 才能生效 + +### 符号导航 (Symbol Navigation) +VBA 符号会被识别并公开给 VSCode 的导航功能: + +- **面包屑导航 (Breadcrumb)** — 随着光标移动显示当前的 `Sub` / `Function` / `Property` 名称 +- **大纲视图 (Outline view)** — 列出当前文件中的所有过程 (Procedure) 和属性 (Property) +- **跳转到符号 (Go to Symbol)** (`Ctrl+Shift+O`) — 直接跳转到任何过程 + +可识别的符号类型:`Sub`, `Function`, `Property Get/Let/Set`, `Type`, `Enum` + +### VBA 代码检查器 (Linter) + +在您编辑 `.bas_utf`、`.cls_utf` 和 `.frm_utf` 文件时,内置的 VBA 校验器 (Linter) 会实时运行。 +诊断结果会以内联波浪线形式显示,并在 **问题 (Problems)** 面板中列出。 + +**规则 (Rules):** + +| 诊断代码 | 规则 | 严重程度 | +|------|------|----------| +| VBA001 | 未声明 `Option Explicit` | 警告 | +| VBA002 | 使用 `On Error Resume Next` | 警告 | +| VBA003 | 使用 `GoTo`(不包括 `On Error GoTo`) | 警告 | +| VBA004 | 行长度超过最大限制(默认:100) | 警告 | +| VBA005 | 使用 `Dim` 声明了变量但从未被使用 | 警告 | +| VBA006 | 嵌套深度超过阈值(默认:5) | 警告 | +| VBA007 | Sub/Function 超过行数阈值(默认:300) | 警告 | +| VBA008 | 括号不匹配 | 错误 | +| VBA009 | 缺少 `End If` / `End Sub` / `End Function` 等 | 错误 | + +**配置 (`cat5dev.toml`):** + +代码检查 (Linting) 默认**被禁用**。要启用它,请在工作区根目录中创建一个 `cat5dev.toml` 文件: + +1. 从命令面板运行 **`cat5dev.init`** 生成配置模板,或者 +2. 按照以下结构手动创建 `cat5dev.toml`: + +```toml +[project] +target_project = "" +language = "zh" + +[lint] +enabled = true + +[lint.rules] +option_explicit = true +on_error_resume_next = false +goto = false +max_line_length = 100 # 0 = 禁用 +unused_variables = true +max_nesting_depth = 5 # 0 = 禁用 +max_function_lines = 300 # 0 = 禁用 +unmatched_parens = true +unmatched_blocks = true + +[formatter] +enabled = false +# ... (参见下面的格式化器部分) +``` + +对 `cat5dev.toml` 的更改在保存后立即生效——无需重新加载。 + +### VBA 格式化器 (Formatter) (`Shift+Alt+F` / 保存时) + +内置的 VBA 代码格式化器可按需使用 (`Shift+Alt+F`),也可在保存时自动运行。 + +格式化器默认**被禁用**。要启用它,请在 `cat5dev.toml` 中设置 `enabled = true`: + +```toml +[formatter] +enabled = true +``` + +**默认格式化处理(启用时):** +- 关键字大小写规范(如 `dim` → `Dim`,`end if` → `End If` 等) +- 缩进修正(支持的块包括:`Sub`、`If`、`For`、`With`、`Select Case` 等) +- 删除行尾空白字符 +- 换行连字符前添加空格(如 `Show(_` → `Show( _`) +- 换行缩进(以 `_` 结束的行下一行会多加一级缩进;仅包含 `)` 的闭合行除外) +- 空行规范化(连续空行最多保留 2 行;过程与过程之间保证有空行) + +**可选格式化选项(默认禁用):** + +| `cat5dev.toml` 键名 | 描述 | +|---------------------|-------------| +| `normalize_operator_spacing` | `x=1+2` → `x = 1 + 2` | +| `normalize_comma_spacing` | `foo(a,b)` → `foo(a, b)` | +| `normalize_comment_space` | `'comment` → `' comment` | +| `expand_type_suffixes` | `Dim x%` → `Dim x As Integer` | + +**完整的 `cat5dev.toml` 格式化器配置:** + +```toml +[formatter] +enabled = true +indent_size = 4 +capitalize_keywords = true +fix_indentation = true +trim_trailing_space = true +ensure_continuation_space = true +indent_continuation_lines = true +max_blank_lines = 2 +normalize_operator_spacing = false +normalize_comma_spacing = false +normalize_comment_space = false +expand_type_suffixes = false +format_on_save = false +``` + +--- + +## 环境要求 + +- 必须运行 CATIA V5 并打开一个 VBA 项目 +- Windows 系统(通过 `cscript.exe` 使用 COM 自动化操作) + +--- + +## 安装方法 + +Cat5Dev 以 `.vsix` 文件形式分发。安装步骤如下: + +1. **下载 `.vsix` 文件** + 从发布页面下载 `cat5dev-x.x.x.vsix`(或更晚版本)。 + +2. **在 VSCode 中安装** + 在 VSCode 中打开扩展视图(`Ctrl+Shift+X`),单击右上角的 `⋯` 菜单,然后选择 **从 VSIX 安装... (Install from VSIX...)**。 + + ![安装步骤](./resources/installation.png) + +3. **选择文件** + 选择已下载的 `.vsix` 文件并确认安装。 + +### ⚠️ 重要提示:首次使用前请备份 CATIA 设置 + +在首次使用 Cat5Dev 之前,**请务必备份您的 CATIA V5 设置文件夹**: + +- **文件夹路径**: `%APPDATA%\Dassault Systemes\CATSettings` +- **原因**: 在极少数情况下,CATIA 设置可能会损坏或配置错误。备份允许您在需要时恢复设置。 + +**备份步骤:** +``` +1. 按下 Win+R 并输入: %APPDATA% +2. 导航至 Dassault Systemes\CATSettings +3. 右键单击并选择“复制” +4. 将其粘贴到安全的位置(如桌面、OneDrive 等) +5. 重命名备份文件夹(例如 “CATSettings_backup_2026-04-18”) +``` + +--- + +## 文件结构 + +``` +/ +├── .gitignore # 由 cat5dev.init 生成 +├── cat5dev.toml # 配置文件(由 cat5dev.init 生成) +└── modules/ + ├── MyModule.bas_utf # 标准模块 (Standard Module) + ├── MyClass.cls_utf # 类模块 (Class Module) + └── MyForm.frm_utf # 用户窗体 (UserForm) +``` + +> 配置保存在 `cat5dev.toml` 的 `[project]` 部分下: +> - `target_project` — 目标 CATIA VBA 项目的名称 +> - `language` — `"ja"` 表示日语,`"en"` 表示英语,`"zh"` 表示中文(默认:`"ja"`) + +--- + +## 快速入门 + +1. 安装本扩展 +2. 在 VSCode 中打开一个工作区文件夹 +3. 单击活动栏中的 **Cat5Dev** 图标 +4. 单击 ⚙️ 选择要同步的目标 CATIA VBA 项目 +5. 单击 ☁️↓ **拉取 (Pull)**,将模块从 CATIA 导入到 `modules/` 文件夹中 +6. 在 VSCode 中编辑您的 VBA 代码 +7. 单击 ☁️↑ **推送 (Push)**,将更改同步回 CATIA + +--- + +## 重要提示:推送 (Push) 后必须在 CATIA 的 VBA 编辑器中进行保存 + +在从 VSCode 推送之后,**您必须在 CATIA 的 VBA 编辑器中保存项目**(在 VBA 编辑器内按 `Ctrl+S`,或点击 菜单 > 保存)。 + +推送操作是将模块代码写入 CATIA 内存中的 VBE。如果在未保存的情况下关闭 CATIA,所有推送的更改都将丢失。 + +``` +VSCode (编辑) → 推送 (Push) → 在 CATIA 的 VBA 编辑器中保存 ✅ + ↓ + 更改将持久保留到 CATIA 文档中 +``` + +--- + +## 注意事项 + +- VBA 文件以 UTF-8 编码存储,并带有 `_utf` 后缀,以便与 CATIA 的原生 Shift-JIS 导出进行区分。 +- 本扩展通过 COM (`MSAPC.Apc`) 来操作 CATIA V5 的 VBE (Visual Basic 编辑器)。 +- 目前不支持在 VSCode 内进行调试运行;请使用 CATIA 自身的 VBA IDE 进行调试。 + +--- + +## 为什么选择 VBA? + +我曾尝试使用多种语言进行 CATIA V5 宏开发,但出于以下几个原因,我最终还是回到了 VBA: + +- **原生支持** — VBA 直接内置于 CATIA V5 中。 +- **最佳的开发环境** — VBA 编辑器 (VBE) 依然是最完整和最稳定的选择。 +- **出乎意料的快速** — 在许多情况下,VBA 的性能表现优于其他替代方案。 +- **紧凑的发布** — 不需要外部运行时或额外的依赖关系。 + +我*喜欢* VBA 这种语言吗? +嗯……那就是完全另外一个问题了。 + +--- + +## 关于编码的注意事项(请提供帮助) + +我只使用过日语版的 CATIA V5,所以只熟悉从 VBA 编辑器导出的、以 Shift-JIS 保存的文件。由于这非常不方便,Cat5Dev 在拉取/推送过程中会自动将编码转换为 UTF-8。 + +如果您在您的环境中使用此扩展时遇到乱码,您可能需要调整编码设置。很遗憾,我没有办法在日语环境之外验证其行为。 + +如果您遇到任何问题,欢迎随时联系我。 + +--- + +## 开源协议 (License) + +MIT From c4c3985a69efe15ccfcbb104aa464f217818aac6 Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Thu, 4 Jun 2026 00:40:05 +0800 Subject: [PATCH 2/6] docs: add Chinese translation link to main README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1abf707..ea2fba4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Cat5Dev +[🇨🇳 中文](docs/README.zh-CN.md) + Cat5Dev is a VSCode extension that syncs VBA modules between VSCode and CATIA V5. --- From 64d5d425737aebd54612aaf4e0cb71d27f72c346 Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:01:37 +0800 Subject: [PATCH 3/6] feat: add Simplified Chinese UI translation and robust language fallback support --- package.json | 8 +-- src/extension.ts | 15 +++--- src/i18n.ts | 124 +++++++++++++++++++++++++++++++++++++++++++--- src/lintConfig.ts | 27 ++++++++++ 4 files changed, 157 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 490e2ed..23ac4c9 100644 --- a/package.json +++ b/package.json @@ -158,14 +158,16 @@ "type": "string", "enum": [ "ja", - "en" + "en", + "zh" ], "enumDescriptions": [ "日本語", - "English" + "English", + "简体中文" ], "default": "ja", - "description": "Language for Cat5Dev extension (日本語 or English)" + "description": "Language for Cat5Dev extension (日本語, English, or 简体中文)" }, "cat5dev.formatter.indentSize": { "type": "number", diff --git a/src/extension.ts b/src/extension.ts index 7736930..b8d83c7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,7 +8,7 @@ import { CatiaVbaTreeProvider } from './treeView'; import { VbaDocumentSymbolProvider } from './symbolProvider'; import { VbaDocumentFormatter, registerFormatOnSave, formatVbaDocument } from './formatter'; import { VbaServer } from './vbaServer'; -import { t, getLanguage, setLanguage } from './i18n'; +import { t, getLanguage, setLanguage, Language, messages } from './i18n'; import { registerLinter } from './linter'; import { tomlTemplate, gitignoreTemplate, readProjectSettings, writeTomlProjectKey } from './lintConfig'; import { startLspClient } from './lspClient'; @@ -54,14 +54,17 @@ export function activate(context: vscode.ExtensionContext) { const currentLang = getLanguage(); const selected = await vscode.window.showQuickPick( [ - { label: t('language.japanese'), description: t('language.description'), value: 'ja' }, - { label: t('language.english'), description: t('language.description'), value: 'en' } + { label: t('language.japanese'), description: currentLang === 'ja' ? t('language.description') : '', value: 'ja' }, + { label: t('language.english'), description: currentLang === 'en' ? t('language.description') : '', value: 'en' }, + { label: t('language.chinese'), description: currentLang === 'zh' ? t('language.description') : '', value: 'zh' } ], { placeHolder: t('language.title') } ); if (selected && selected.value !== currentLang) { - setLanguage(selected.value as 'ja' | 'en'); - vscode.window.showInformationMessage(t('language.reload')); + const nextLang = selected.value as Language; + await setLanguage(nextLang); + const msg = (messages[nextLang] as any)['language.reload'] || messages.ja['language.reload']; + vscode.window.showInformationMessage(msg); } }); @@ -1097,7 +1100,7 @@ WScript.Echo "VBS: End" if (hasErrors) { outputChannel.show(true); } - const deleteMsg = performDelete ? '(削除同期を含む)' : ''; + const deleteMsg = performDelete ? t('push.deleteSync') : ''; vscode.window.showInformationMessage(t('info.pushSuccess', String(count), '', deleteMsg)); vscode.commands.executeCommand('cat5dev.refreshTree'); resolve(); diff --git a/src/i18n.ts b/src/i18n.ts index 5483c5e..6c13dca 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,22 +1,43 @@ import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; import { readProjectSettings, writeTomlProjectKey } from './lintConfig'; -export type Language = 'ja' | 'en'; +export type Language = 'ja' | 'en' | 'zh'; export function getLanguage(): Language { const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders) { return 'ja'; } + if (workspaceFolders) { + const tomlPath = path.join(workspaceFolders[0].uri.fsPath, 'cat5dev.toml'); + if (fs.existsSync(tomlPath)) { + const { language } = readProjectSettings(workspaceFolders[0].uri.fsPath); + if (language === 'ja' || language === 'en' || language === 'zh') { return language; } + } + } + + // Fallback 1: VSCode config setting + const configLang = vscode.workspace.getConfiguration('cat5dev').get('language'); + if (configLang === 'ja' || configLang === 'en' || configLang === 'zh') { return configLang; } + + // Fallback 2: VSCode display language + const envLang = vscode.env.language.toLowerCase(); + if (envLang.startsWith('zh')) { return 'zh'; } + if (envLang.startsWith('ja')) { return 'ja'; } - const { language } = readProjectSettings(workspaceFolders[0].uri.fsPath); - if (language === 'ja' || language === 'en') { return language; } - return 'ja'; + return 'en'; // Default fallback } -export function setLanguage(lang: Language): void { +export async function setLanguage(lang: Language): Promise { const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) { return; } - writeTomlProjectKey(workspaceFolders[0].uri.fsPath, 'language', lang); + const tomlPath = path.join(workspaceFolders[0].uri.fsPath, 'cat5dev.toml'); + if (fs.existsSync(tomlPath)) { + writeTomlProjectKey(workspaceFolders[0].uri.fsPath, 'language', lang); + } else { + // Fallback to updating VSCode configuration if toml doesn't exist + await vscode.workspace.getConfiguration('cat5dev').update('language', lang, vscode.ConfigurationTarget.Workspace); + } } export const messages = { @@ -76,6 +97,7 @@ export const messages = { 'language.title': '言語を選択してください', 'language.japanese': '日本語', 'language.english': 'English', + 'language.chinese': '简体中文', // File operations 'file.rename': '名前変更', @@ -89,6 +111,9 @@ export const messages = { 'language.description': '(現在の言語)', 'language.reload': '言語変更を反映するにはVSCodeを再読み込みしてください。', + // Push + 'push.deleteSync': '(削除同期を含む)', + // Init command 'init.tomlExists': 'cat5dev.toml は既に存在します。上書きしますか?', 'init.overwrite': '上書き', @@ -151,6 +176,7 @@ export const messages = { 'language.title': 'Select a language', 'language.japanese': '日本語', 'language.english': 'English', + 'language.chinese': '简体中文', // File operations 'file.rename': 'Rename', @@ -164,17 +190,99 @@ export const messages = { 'language.description': '(Current language)', 'language.reload': 'Please reload VSCode to apply language changes.', + // Push + 'push.deleteSync': ' (includes deletion sync)', + // Init command 'init.tomlExists': 'cat5dev.toml already exists. Overwrite?', 'init.overwrite': 'Overwrite', 'init.gitignoreExists': '.gitignore already exists. Overwrite?', 'init.success': 'cat5dev.toml has been created.', + }, + zh: { + // Sidebar & TreeView + 'sidebar.title': 'CATIA V5 VBA', + 'sidebar.modules': '模块', + 'treeview.modules': '标准模块', + 'treeview.classModules': '类模块', + 'treeview.forms': '用户窗体', + 'treeview.targetProject': '目标 CATIA VBA 项目', + + // Commands + 'command.pull': 'CATIA: 拉取 VBA 模块', + 'command.push': 'CATIA: 推送 VBA 模块', + 'command.select': 'CATIA: 选择目标项目', + 'command.refresh': '刷新模块列表', + 'command.switchLanguage': 'CATIA: 切换语言', + + // Error messages + 'error.noWorkspace': '请打开一个工作区文件夹以配置 CATIA VBA 同步。', + 'error.pullFailed': '拉取失败。详情请检查输出面板。', + 'error.pushFailed': '推送失败。详情请检查输出面板。', + 'error.selectFailed': '无法从 CATIA 获取 VBA 项目。\n详情请检查输出面板。', + 'error.noModulesDir': '工作区中未找到 modules 目录。', + 'error.noModuleFiles': '工作区中未找到可推送的 VBA 文件 (.bas_utf, .cls_utf, .frm_utf)。', + 'error.checkComponentsFailed': '[检查组件错误]', + + // Information messages + 'info.projectNotFound': '在 CATIA 中未找到 VBA 项目。', + 'info.projectSelected': '已将目标 VBA 项目设置为 {0}。', + 'info.pullSuccess': '成功从 CATIA 拉取了 {0} 个模块。', + 'info.pushSuccess': '已推送 {0} 个模块。{1}{2}', + 'info.noChanges': '自上次推送以来,所有模块 ({0} 个) 均未更改。已跳过推送。', + 'info.pullCancelled': '拉取已取消。', + 'info.pushCancelled': '推送已取消。', + + // Warning messages + 'warning.deleteModules': '以下模块存在于 CATIA 中,但不存在于 VSCode 中:\n{0}\n\n是否从 CATIA 中删除它们以实现完全同步?', + 'warning.newUserForms': '以下用户窗体 (UserForm) 在 CATIA 中不存在,无法新建。请先在 CATIA 中创建同名的空用户窗体。这些文件将被跳过:\n{0}', + 'warning.noMoreFiles': '没有更多可推送的文件。处理结束。', + 'warning.longModuleNames': '以下模块名称超过了 31 个字符(VBA 编辑器的限制):\n{0}\n\n是否仍要继续推送?', + + // Dialog options + 'dialog.delete': '是 (删除)', + 'dialog.keep': '否 (保留)', + 'dialog.continue': '继续', + + // Progress titles + 'progress.pull': '正在从 CATIA 拉取 VBA ({0})...', + 'progress.push': '正在向 CATIA 推送 VBA ({0})...', + + // Select project + 'select.placeholder': '选择要同步的 CATIA VBA 项目', + + // Language switch + 'language.title': '请选择语言', + 'language.japanese': '日本語', + 'language.english': 'English', + 'language.chinese': '简体中文', + + // File operations + 'file.rename': '重命名', + 'file.delete': '删除', + 'file.copy': '复制路径', + 'file.deleteConfirm': '确定要删除 {0} 吗?', + 'file.deleteButton': '删除', + 'file.copySuccess': '已复制路径: {0}', + + // Language switch + 'language.description': '(当前语言)', + 'language.reload': '请重新加载 VSCode 以应用语言更改。', + + // Push + 'push.deleteSync': '(包含删除同步)', + + // Init command + 'init.tomlExists': 'cat5dev.toml 已存在。是否覆盖?', + 'init.overwrite': '覆盖', + 'init.gitignoreExists': '.gitignore 已存在。是否覆盖?', + 'init.success': '已创建 cat5dev.toml。', } }; export function t(key: keyof typeof messages.ja, ...args: string[]): string { const lang = getLanguage(); - let text = messages[lang][key as keyof typeof messages.ja] || messages.ja[key]; + let text = (messages[lang] as any)[key] || messages.ja[key]; // Simple string interpolation args.forEach((arg, index) => { diff --git a/src/lintConfig.ts b/src/lintConfig.ts index 6bcd9f9..2a64a42 100644 --- a/src/lintConfig.ts +++ b/src/lintConfig.ts @@ -339,6 +339,33 @@ const tomlComments = { expandTypeSuffixes: '# Expand type suffix shorthand (% → Integer, $ → String, etc.)', formatOnSave: '# Automatically format on save', }, + zh: { + header: '# Cat5Dev 配置文件', + targetProject: '# 目标 CATIA VBA 项目名称', + language: '# 语言设置 (ja / en / zh)', + optionExplicit: '# 当未声明 Option Explicit 时发出警告', + onErrorResumeNext: '# 使用 On Error Resume Next 时发出警告', + goto: '# 使用 GoTo 时发出警告 (不包括 On Error GoTo)', + maxLineLength: '# 单行字符数超过指定值时发出警告 (0 = 禁用)', + unusedVariables: '# 变量已声明但未使用时发出警告', + maxNestingDepth: '# 嵌套深度超过阈值时发出警告 (0 = 禁用)', + maxFunctionLines: '# Sub/Function 行数超过阈值时发出警告 (0 = 禁用)', + unmatchedParens: '# 将括号不匹配报告为错误', + unmatchedBlocks: '# 将缺少 End If / End Sub / End Function 等报告为错误', + formatterEnabled: '# 启用或禁用格式化程序', + indentSize: '# 每个缩进级别的空格数', + capitalizeKeywords: '# 自动大写 VBA 关键字 (If, Dim, Sub 等)', + fixIndentation: '# 自动修正缩进', + trimTrailingSpace: '# 删除行尾空白字符', + ensureContinuationSpace: '# 确保换行符 (_) 前有空格', + indentContinuationLines: '# 将换行行缩进一个级别', + maxBlankLines: '# 连续空行的最大数量 (0 = 禁用)', + normalizeOperatorSpacing: '# 规范运算符 (=, +, - 等) 两侧的空格', + normalizeCommaSpacing: '# 规范逗号后的空格', + normalizeCommentSpace: '# 确保注释符号 (\') 后有空格', + expandTypeSuffixes: '# 展开类型后缀简写 (% → Integer, $ → String 等)', + formatOnSave: '# 保存时自动格式化', + }, }; /** cat5dev.toml の雛形テキストを返す。値は DEFAULT_*_OPTIONS から生成する */ From 020960f67eceb0630d0a15ffd5689b64d903dd3d Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:20:28 +0800 Subject: [PATCH 4/6] feat: make transfer encoding configurable and support Chinese VBA code transfer using GBK default --- scripts/c5d_push.catvbs.bas_utf | 4 ++-- src/extension.ts | 34 +++++++++++++++++++--------- src/lintConfig.ts | 40 ++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/scripts/c5d_push.catvbs.bas_utf b/scripts/c5d_push.catvbs.bas_utf index 4c33f7c..b2f71af 100644 --- a/scripts/c5d_push.catvbs.bas_utf +++ b/scripts/c5d_push.catvbs.bas_utf @@ -69,7 +69,7 @@ Sub CATMain() If fso.FileExists("{{TEMP_DIR}}\delete_list.txt") Then Set inStr = CreateObject("ADODB.Stream") inStr.Type = 2 - inStr.Charset = "shift_jis" + inStr.Charset = "{{ENCODING}}" inStr.Open inStr.LoadFromFile "{{TEMP_DIR}}\delete_list.txt" @@ -159,7 +159,7 @@ Sub CATMain() Else Set inStr = CreateObject("ADODB.Stream") inStr.Type = 2 - inStr.Charset = "shift_jis" + inStr.Charset = "{{ENCODING}}" inStr.Open inStr.LoadFromFile fp newContent = inStr.ReadText diff --git a/src/extension.ts b/src/extension.ts index b8d83c7..bb7bc30 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -226,6 +226,10 @@ async function executeSelectProject(_context: vscode.ExtensionContext, rootPath? rootPath = workspaceFolders[0].uri.fsPath; } + const activeLang = getLanguage(); + const { encoding: configEncoding } = readProjectSettings(rootPath); + const encoding = configEncoding || (activeLang === 'zh' ? 'gbk' : (activeLang === 'en' ? 'utf-8' : 'shift_jis')); + const tempDir = path.join(os.tmpdir(), 'cat5dev'); if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true, force: true }); @@ -317,7 +321,7 @@ ag_sys.ExecuteScript "${tempDir}", 1, "c5d_list.catvbs", "CATMain", ag_args } const buffer = fs.readFileSync(outTxtPath); - const text = iconv.decode(buffer, 'shift_jis'); + const text = iconv.decode(buffer, encoding); const projects = text.split('\n').map(p => p.replace(/\r/g, '').trim()).filter(p => p.length > 0); fs.unlinkSync(outTxtPath); @@ -347,6 +351,10 @@ async function executeCatiaPull(context: vscode.ExtensionContext, vbaServer: Vba return; } const rootPath = workspaceFolders[0].uri.fsPath; + const activeLang = getLanguage(); + const { encoding: configEncoding } = readProjectSettings(rootPath); + const encoding = configEncoding || (activeLang === 'zh' ? 'gbk' : (activeLang === 'en' ? 'utf-8' : 'shift_jis')); + outputChannel.appendLine(`[Pull] Start: language=${activeLang}, encoding=${encoding}`); const modulesDir = path.join(rootPath, 'modules'); if (!fs.existsSync(modulesDir)) { @@ -395,7 +403,7 @@ Sub CATMain() ' Write error log Set ag_outStr = CreateObject("ADODB.Stream") ag_outStr.Type = 2 - ag_outStr.Charset = "shift_jis" + ag_outStr.Charset = "${encoding}" ag_outStr.Open ag_outStr.WriteText "ERROR: VBE access failed" ag_outStr.SaveToFile "${tempDir}\\_error.log", 2 @@ -423,7 +431,7 @@ Sub CATMain() ag_outPath = "${tempDir}\\" & ag_comp.Name & "_TYPE_" & ag_comp.Type & ".txt" Set ag_outStr = CreateObject("ADODB.Stream") ag_outStr.Type = 2 - ag_outStr.Charset = "shift_jis" + ag_outStr.Charset = "${encoding}" ag_outStr.Open ag_outStr.WriteText ag_codeMod.Lines(1, ag_lineCount) ag_outStr.SaveToFile ag_outPath, 2 @@ -501,7 +509,7 @@ sys.ExecuteScript "${tempDir}", 1, "c5d_pull.catvbs", "CATMain", args else if (compType === '3') ext = '.frm_utf'; // Userform const shiftJisBuffer = fs.readFileSync(path.join(tempDir, file)); - const utf8String = iconv.decode(shiftJisBuffer, 'shift_jis'); + const utf8String = iconv.decode(shiftJisBuffer, encoding); // Normalize newlines: Remove all trailing newlines/spaces and ensure exactly one LF const normalized = utf8String.replace(/\r/g, '').trimEnd() + '\n'; @@ -540,6 +548,10 @@ async function executeCatiaPush(context: vscode.ExtensionContext) { return; } const rootPath = workspaceFolders[0].uri.fsPath; + const activeLang = getLanguage(); + const { encoding: configEncoding } = readProjectSettings(rootPath); + const encoding = configEncoding || (activeLang === 'zh' ? 'gbk' : (activeLang === 'en' ? 'utf-8' : 'shift_jis')); + outputChannel.appendLine(`[Push] Start: language=${activeLang}, encoding=${encoding}`); const modulesDir = path.join(rootPath, 'modules'); if (!fs.existsSync(modulesDir)) { @@ -588,7 +600,7 @@ async function executeCatiaPush(context: vscode.ExtensionContext) { localContents[compName] = trimmed; localCompTypes[compName] = compType; - const shiftJisBuffer = iconv.encode(trimmed, 'shift_jis'); + const shiftJisBuffer = iconv.encode(trimmed, encoding); const tempFilePath = path.join(tempDir, `${compName}_TYPE_${compType}.txt`); fs.writeFileSync(tempFilePath, shiftJisBuffer); @@ -661,7 +673,7 @@ Sub CATMain() If ag_lineCount > 0 Then Set ag_codeStr = CreateObject("ADODB.Stream") ag_codeStr.Type = 2 - ag_codeStr.Charset = "shift_jis" + ag_codeStr.Charset = "${encoding}" ag_codeStr.Open ag_codeStr.WriteText ag_comp.CodeModule.Lines(1, ag_lineCount) ag_codeStr.SaveToFile "${tempDir}\\" & ag_comp.Name & "_REMOTE.txt", 2 @@ -729,7 +741,7 @@ ag_sys.ExecuteScript "${tempDir}", 1, "c5d_check.catvbs", "CATMain", ag_args let remoteCompNames: string[] = []; if (fs.existsSync(remoteCompsFile)) { const buffer = fs.readFileSync(remoteCompsFile); - const text = iconv.decode(buffer, 'shift_jis'); + const text = iconv.decode(buffer, encoding); remoteCompNames = text.split('\n').map(p => p.replace(/\r/g, '').trim()).filter(p => p.length > 0); fs.unlinkSync(remoteCompsFile); } @@ -739,7 +751,7 @@ ag_sys.ExecuteScript "${tempDir}", 1, "c5d_check.catvbs", "CATMain", ag_args const remoteFilePath = path.join(tempDir, `${compName}_REMOTE.txt`); if (fs.existsSync(remoteFilePath)) { const remoteBuf = fs.readFileSync(remoteFilePath); - const remoteText = iconv.decode(remoteBuf, 'shift_jis').trimEnd(); + const remoteText = iconv.decode(remoteBuf, encoding).trimEnd(); fs.unlinkSync(remoteFilePath); if (localContents[compName] === remoteText) { @@ -779,7 +791,7 @@ ag_sys.ExecuteScript "${tempDir}", 1, "c5d_check.catvbs", "CATMain", ag_args } if (resp === t('dialog.delete')) { performDelete = true; - const delListShiftJis = iconv.encode(toDelete.join('\r\n'), 'shift_jis'); + const delListShiftJis = iconv.encode(toDelete.join('\r\n'), encoding); fs.writeFileSync(path.join(tempDir, 'delete_list.txt'), delListShiftJis); } } @@ -886,7 +898,7 @@ Sub CATMain() If fso.FileExists("${tempDir}\\delete_list.txt") Then Set inStr = CreateObject("ADODB.Stream") inStr.Type = 2 - inStr.Charset = "shift_jis" + inStr.Charset = "${encoding}" inStr.Open inStr.LoadFromFile "${tempDir}\\delete_list.txt" @@ -976,7 +988,7 @@ Sub CATMain() Else Set inStr = CreateObject("ADODB.Stream") inStr.Type = 2 - inStr.Charset = "shift_jis" + inStr.Charset = "${encoding}" inStr.Open inStr.LoadFromFile fp newContent = inStr.ReadText diff --git a/src/lintConfig.ts b/src/lintConfig.ts index 2a64a42..ba97fce 100644 --- a/src/lintConfig.ts +++ b/src/lintConfig.ts @@ -248,17 +248,18 @@ temp/ } /** cat5dev.toml の [project] セクションから設定を読み込む */ -export function readProjectSettings(workspaceRoot: string): { targetProject: string; language: string } { +export function readProjectSettings(workspaceRoot: string): { targetProject: string; language: string; encoding: string } { const tomlPath = path.join(workspaceRoot, 'cat5dev.toml'); - if (!fs.existsSync(tomlPath)) { return { targetProject: '', language: 'ja' }; } + if (!fs.existsSync(tomlPath)) { return { targetProject: '', language: '', encoding: '' }; } let content: string; - try { content = fs.readFileSync(tomlPath, 'utf-8'); } catch { return { targetProject: '', language: 'ja' }; } + try { content = fs.readFileSync(tomlPath, 'utf-8'); } catch { return { targetProject: '', language: '', encoding: '' }; } const proj = parseToml(content)['project'] ?? {}; const targetProject = (proj['target_project'] ?? '').replace(/^"|"$/g, ''); - const language = (proj['language'] ?? 'ja').replace(/^"|"$/g, ''); - return { targetProject, language }; + const language = (proj['language'] ?? '').replace(/^"|"$/g, ''); + const encoding = (proj['encoding'] ?? '').replace(/^"|"$/g, ''); + return { targetProject, language, encoding }; } /** cat5dev.toml の [project] セクションの指定キーの値を書き換える(コメント・他行を保持) */ @@ -269,7 +270,7 @@ export function writeTomlProjectKey(workspaceRoot: string, key: string, value: s const lines = fs.readFileSync(tomlPath, 'utf-8').split(/\r?\n/); let inProject = false; let written = false; - const result = lines.map(line => { + let result = lines.map(line => { const secMatch = line.trim().match(/^\[([^\]]+)\]$/); if (secMatch) { inProject = secMatch[1].trim() === 'project'; } if (inProject && !written) { @@ -281,6 +282,26 @@ export function writeTomlProjectKey(workspaceRoot: string, key: string, value: s } return line; }); + + if (!written) { + const newResult: string[] = []; + let projectFound = false; + for (const line of result) { + newResult.push(line); + const secMatch = line.trim().match(/^\[([^\]]+)\]$/); + if (secMatch && secMatch[1].trim() === 'project') { + newResult.push(`${key} = "${value}"`); + projectFound = true; + } + } + if (projectFound) { + result = newResult; + } else { + result.push('[project]'); + result.push(`${key} = "${value}"`); + } + } + fs.writeFileSync(tomlPath, result.join('\n'), 'utf-8'); } @@ -289,6 +310,7 @@ const tomlComments = { header: '# Cat5Dev 設定ファイル', targetProject: '# 対象の CATIA VBA プロジェクト名', language: '# 言語設定 (ja / en)', + encoding: '# エンコーディング設定 (utf-8 / shift_jis / gbk 等)', optionExplicit: '# Option Explicit が宣言されていない場合に警告', onErrorResumeNext: '# On Error Resume Next の使用時に警告', goto: '# GoTo の使用時に警告 (On Error GoTo は除外)', @@ -316,6 +338,7 @@ const tomlComments = { header: '# Cat5Dev configuration file', targetProject: '# Target CATIA VBA project name', language: '# Language (ja / en)', + encoding: '# Encoding for VBA code transfer (utf-8 / shift_jis / gbk / etc.)', optionExplicit: '# Warn when Option Explicit is not declared', onErrorResumeNext: '# Warn on usage of On Error Resume Next', goto: '# Warn on GoTo usage (On Error GoTo is excluded)', @@ -343,6 +366,7 @@ const tomlComments = { header: '# Cat5Dev 配置文件', targetProject: '# 目标 CATIA VBA 项目名称', language: '# 语言设置 (ja / en / zh)', + encoding: '# VBA 代码传输编码 (utf-8 / shift_jis / gbk 等)', optionExplicit: '# 当未声明 Option Explicit 时发出警告', onErrorResumeNext: '# 使用 On Error Resume Next 时发出警告', goto: '# 使用 GoTo 时发出警告 (不包括 On Error GoTo)', @@ -373,6 +397,7 @@ export function tomlTemplate(lang: Language = 'ja'): string { const l = DEFAULT_LINT_OPTIONS; const f = DEFAULT_FORMATTER_OPTIONS; const c = tomlComments[lang]; + const defaultEncoding = lang === 'zh' ? 'gbk' : (lang === 'en' ? 'utf-8' : 'shift_jis'); return `${c.header} [project] @@ -382,6 +407,9 @@ target_project = "" ${c.language} language = "${lang}" +${c.encoding} +encoding = "${defaultEncoding}" + [lint] enabled = false From 7a6ba4afba734eb997452fc08ab50e046561adf3 Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Fri, 5 Jun 2026 00:47:57 +0800 Subject: [PATCH 5/6] feat: localize output logs and align Chinese translations with core VBA terms --- src/extension.ts | 20 ++++---- src/formatter.ts | 7 +-- src/i18n.ts | 125 +++++++++++++++++++++++++++++++++++++---------- src/linter.ts | 5 +- src/vbaServer.ts | 17 ++++--- 5 files changed, 126 insertions(+), 48 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index bb7bc30..2659377 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,7 +22,7 @@ function flushCatScriptErrors(tempDir: string): boolean { let hasErrors = false; try { const content = fs.readFileSync(errLogPath, 'utf-8').trim(); - outputChannel.appendLine(`[CATScript Log]\n${content || '(空)'}`); + outputChannel.appendLine(`[CATScript Log]\n${content || t('log.catscript.empty')}`); outputChannel.show(true); hasErrors = /\[Push\.(Fatal|Add|DeleteLines|AddFromString)\]/.test(content); } catch { @@ -354,7 +354,7 @@ async function executeCatiaPull(context: vscode.ExtensionContext, vbaServer: Vba const activeLang = getLanguage(); const { encoding: configEncoding } = readProjectSettings(rootPath); const encoding = configEncoding || (activeLang === 'zh' ? 'gbk' : (activeLang === 'en' ? 'utf-8' : 'shift_jis')); - outputChannel.appendLine(`[Pull] Start: language=${activeLang}, encoding=${encoding}`); + outputChannel.appendLine(t('log.pull.start', activeLang, encoding)); const modulesDir = path.join(rootPath, 'modules'); if (!fs.existsSync(modulesDir)) { @@ -551,7 +551,7 @@ async function executeCatiaPush(context: vscode.ExtensionContext) { const activeLang = getLanguage(); const { encoding: configEncoding } = readProjectSettings(rootPath); const encoding = configEncoding || (activeLang === 'zh' ? 'gbk' : (activeLang === 'en' ? 'utf-8' : 'shift_jis')); - outputChannel.appendLine(`[Push] Start: language=${activeLang}, encoding=${encoding}`); + outputChannel.appendLine(t('log.push.start', activeLang, encoding)); const modulesDir = path.join(rootPath, 'modules'); if (!fs.existsSync(modulesDir)) { @@ -1040,10 +1040,10 @@ End Sub `; fs.writeFileSync(catScriptPath, catScriptContent, 'utf-8'); - outputChannel.appendLine(`[Push] tempDir: ${tempDir}`); - outputChannel.appendLine(`[Push] 対象プロジェクト: ${targetProject}`); - outputChannel.appendLine(`[Push] 転送ファイル数: ${count}`); - outputChannel.appendLine(`[Push] CATScript生成: ${catScriptPath}`); + outputChannel.appendLine(t('log.push.tempDir', tempDir)); + outputChannel.appendLine(t('log.push.targetProject', targetProject)); + outputChannel.appendLine(t('log.push.fileCount', String(count))); + outputChannel.appendLine(t('log.push.catscriptGenerated', catScriptPath)); outputChannel.show(true); const doneFlagPath = path.join(tempDir, 'c5d_push_done.txt'); @@ -1082,8 +1082,8 @@ If fso2.FileExists(doneFile) Then fso2.DeleteFile doneFile WScript.Echo "VBS: End" `; fs.writeFileSync(pushVbsPath, pushVbsScript, 'utf-8'); - outputChannel.appendLine(`[Push] VBS生成: ${pushVbsPath}`); - outputChannel.appendLine(`[Push] cscript実行開始...`); + outputChannel.appendLine(t('log.push.vbsGenerated', pushVbsPath)); + outputChannel.appendLine(t('log.push.cscriptStart')); outputChannel.show(true); vscode.window.withProgress({ @@ -1093,7 +1093,7 @@ WScript.Echo "VBS: End" }, async (progress) => { return new Promise((resolve, reject) => { exec(`%SystemRoot%\\SysWOW64\\cscript.exe //nologo "${pushVbsPath}"`, { maxBuffer: 1024 * 1024 * 10 }, (error, stdout, stderr) => { - outputChannel.appendLine(`[Push] cscript終了 error=${error?.code ?? 'null'} stdout="${stdout.trim()}" stderr="${stderr.trim()}"`); + outputChannel.appendLine(t('log.push.cscriptEnd', String(error?.code ?? 'null'), stdout.trim(), stderr.trim())); outputChannel.show(true); if (fs.existsSync(pushVbsPath)) fs.unlinkSync(pushVbsPath); if (fs.existsSync(catScriptPath)) fs.unlinkSync(catScriptPath); diff --git a/src/formatter.ts b/src/formatter.ts index 3980acd..2809093 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { VbaServer, httpPost } from './vbaServer'; import { readFormatterOptions } from './lintConfig'; +import { t } from './i18n'; const FORMATTER_TIMEOUT_MS = 10000; @@ -13,7 +14,7 @@ export async function formatVbaDocument( ): Promise { const baseUrl = server.getBaseUrl(); if (baseUrl === null) { - outputChannel.appendLine('[vbafmt] サーバーが起動していません'); + outputChannel.appendLine(t('log.vbafmt.serverNotRunning')); return null; } @@ -47,12 +48,12 @@ export async function formatVbaDocument( const responseText = await httpPost(`${baseUrl}/format`, requestBody, FORMATTER_TIMEOUT_MS); const response = JSON.parse(responseText) as { result: string; error: string }; if (response.error) { - outputChannel.appendLine(`[vbafmt] エラー: ${response.error}`); + outputChannel.appendLine(t('log.vbafmt.error', response.error)); return null; } return response.result; } catch (err) { - outputChannel.appendLine(`[vbafmt] リクエストエラー: ${err}`); + outputChannel.appendLine(t('log.vbafmt.requestError', String(err))); return null; } } diff --git a/src/i18n.ts b/src/i18n.ts index 6c13dca..84c39b2 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -62,7 +62,7 @@ export const messages = { 'error.pullFailed': 'プルに失敗しました。詳細 Output を確認してください。', 'error.pushFailed': 'プッシュに失敗しました。詳細 Output を確認してください。', 'error.selectFailed': 'CATIAからVBAプロジェクトを取得できませんでした。\n詳細 Output を確認してください。', - 'error.noModulesDir': 'プッシュ対象の modules ディレクトリがワークスペース内に見つかりません。', + 'error.noModulesDir': 'プッシュ対象 of modules ディレクトリがワークスペース内に見つかりません。', 'error.noModuleFiles': 'ワークスペース内にプッシュ対象のVBAファイル (.bas_utf, .cls_utf, .frm_utf) が見つかりませんでした。', 'error.checkComponentsFailed': '[Check Components Error]', @@ -119,6 +119,31 @@ export const messages = { 'init.overwrite': '上書き', 'init.gitignoreExists': '.gitignore は既に存在します。上書きしますか?', 'init.success': 'cat5dev.toml を作成しました。', + + // Logs + 'log.vbafmt.binaryNotFound': '[vbafmt] バイナリが見つかりません: {0}', + 'log.vbafmt.buildBinaryPrompt': '[vbafmt] npm run compile:go を実行してバイナリをビルドしてください', + 'log.vbafmt.serverStarted': '[vbafmt] サーバー起動完了 (port: {0})', + 'log.vbafmt.serverStartFailed': '[vbafmt] サーバー起動失敗: {0}', + 'log.vbafmt.serverTimeout': 'サーバー起動タイムアウト', + 'log.vbafmt.processExited': 'プロセスが終了しました (exit {0})', + 'log.vbafmt.maxRestartReached': '[vbafmt] サーバー再起動の上限に達しました。手動で再起動してください。', + 'log.vbafmt.restarting': '[vbafmt] {0}秒後に再起動します... ({1}/{2})', + 'log.vbalint.error': '[vba-lint] エラー: {0}', + 'log.vbalint.requestError': '[vba-lint] リクエストエラー: {0}', + 'log.vbafmt.serverNotRunning': '[vbafmt] サーバーが起動していません', + 'log.vbafmt.error': '[vbafmt] エラー: {0}', + 'log.vbafmt.requestError': '[vbafmt] リクエストエラー: {0}', + 'log.catscript.empty': '(空)', + 'log.push.start': '[Push] Start: language={0}, encoding={1}', + 'log.pull.start': '[Pull] Start: language={0}, encoding={1}', + 'log.push.tempDir': '[Push] tempDir: {0}', + 'log.push.targetProject': '[Push] 対象プロジェクト: {0}', + 'log.push.fileCount': '[Push] 転送ファイル数: {0}', + 'log.push.catscriptGenerated': '[Push] CATScript生成: {0}', + 'log.push.vbsGenerated': '[Push] VBS生成: {0}', + 'log.push.cscriptStart': '[Push] cscript実行開始...', + 'log.push.cscriptEnd': '[Push] cscript終了 error={0} stdout="{1}" stderr="{2}"', }, en: { // Sidebar & TreeView @@ -198,27 +223,52 @@ export const messages = { 'init.overwrite': 'Overwrite', 'init.gitignoreExists': '.gitignore already exists. Overwrite?', 'init.success': 'cat5dev.toml has been created.', + + // Logs + 'log.vbafmt.binaryNotFound': '[vbafmt] Binary not found: {0}', + 'log.vbafmt.buildBinaryPrompt': '[vbafmt] Please run "npm run compile:go" to build the binary', + 'log.vbafmt.serverStarted': '[vbafmt] Server started (port: {0})', + 'log.vbafmt.serverStartFailed': '[vbafmt] Failed to start server: {0}', + 'log.vbafmt.serverTimeout': 'Server start timeout', + 'log.vbafmt.processExited': 'Process exited (exit {0})', + 'log.vbafmt.maxRestartReached': '[vbafmt] Reached maximum server restart count. Please restart manually.', + 'log.vbafmt.restarting': '[vbafmt] Restarting in {0} seconds... ({1}/{2})', + 'log.vbalint.error': '[vba-lint] Error: {0}', + 'log.vbalint.requestError': '[vba-lint] Request error: {0}', + 'log.vbafmt.serverNotRunning': '[vbafmt] Server is not running', + 'log.vbafmt.error': '[vbafmt] Error: {0}', + 'log.vbafmt.requestError': '[vbafmt] Request error: {0}', + 'log.catscript.empty': '(Empty)', + 'log.push.start': '[Push] Start: language={0}, encoding={1}', + 'log.pull.start': '[Pull] Start: language={0}, encoding={1}', + 'log.push.tempDir': '[Push] tempDir: {0}', + 'log.push.targetProject': '[Push] Target Project: {0}', + 'log.push.fileCount': '[Push] Transfer files count: {0}', + 'log.push.catscriptGenerated': '[Push] CATScript generated: {0}', + 'log.push.vbsGenerated': '[Push] VBS generated: {0}', + 'log.push.cscriptStart': '[Push] Starting cscript execution...', + 'log.push.cscriptEnd': '[Push] cscript finished error={0} stdout="{1}" stderr="{2}"', }, zh: { // Sidebar & TreeView 'sidebar.title': 'CATIA V5 VBA', - 'sidebar.modules': '模块', - 'treeview.modules': '标准模块', - 'treeview.classModules': '类模块', - 'treeview.forms': '用户窗体', - 'treeview.targetProject': '目标 CATIA VBA 项目', + 'sidebar.modules': 'Modules', + 'treeview.modules': 'Modules', + 'treeview.classModules': 'Class Modules', + 'treeview.forms': 'Forms', + 'treeview.targetProject': 'Target CATIA VBA Project', // Commands - 'command.pull': 'CATIA: 拉取 VBA 模块', - 'command.push': 'CATIA: 推送 VBA 模块', - 'command.select': 'CATIA: 选择目标项目', - 'command.refresh': '刷新模块列表', - 'command.switchLanguage': 'CATIA: 切换语言', + 'command.pull': 'CATIA: Pull VBA Modules', + 'command.push': 'CATIA: Push VBA Modules', + 'command.select': 'CATIA: Select Target Project', + 'command.refresh': 'Refresh Modules', + 'command.switchLanguage': 'CATIA: Switch Language', // Error messages 'error.noWorkspace': '请打开一个工作区文件夹以配置 CATIA VBA 同步。', - 'error.pullFailed': '拉取失败。详情请检查输出面板。', - 'error.pushFailed': '推送失败。详情请检查输出面板。', + 'error.pullFailed': 'Pull 失败。详情请检查输出面板。', + 'error.pushFailed': 'Push 失败。详情请检查输出面板。', 'error.selectFailed': '无法从 CATIA 获取 VBA 项目。\n详情请检查输出面板。', 'error.noModulesDir': '工作区中未找到 modules 目录。', 'error.noModuleFiles': '工作区中未找到可推送的 VBA 文件 (.bas_utf, .cls_utf, .frm_utf)。', @@ -227,17 +277,17 @@ export const messages = { // Information messages 'info.projectNotFound': '在 CATIA 中未找到 VBA 项目。', 'info.projectSelected': '已将目标 VBA 项目设置为 {0}。', - 'info.pullSuccess': '成功从 CATIA 拉取了 {0} 个模块。', - 'info.pushSuccess': '已推送 {0} 个模块。{1}{2}', - 'info.noChanges': '自上次推送以来,所有模块 ({0} 个) 均未更改。已跳过推送。', - 'info.pullCancelled': '拉取已取消。', - 'info.pushCancelled': '推送已取消。', + 'info.pullSuccess': '成功从 CATIA Pull 了 {0} 个 Modules。', + 'info.pushSuccess': '已 Push {0} 个 Modules。{1}{2}', + 'info.noChanges': '自上次 Push 以来,所有 Modules ({0} 个) 均未更改。已跳过 Push。', + 'info.pullCancelled': 'Pull 已取消。', + 'info.pushCancelled': 'Push 已取消。', // Warning messages - 'warning.deleteModules': '以下模块存在于 CATIA 中,但不存在于 VSCode 中:\n{0}\n\n是否从 CATIA 中删除它们以实现完全同步?', - 'warning.newUserForms': '以下用户窗体 (UserForm) 在 CATIA 中不存在,无法新建。请先在 CATIA 中创建同名的空用户窗体。这些文件将被跳过:\n{0}', - 'warning.noMoreFiles': '没有更多可推送的文件。处理结束。', - 'warning.longModuleNames': '以下模块名称超过了 31 个字符(VBA 编辑器的限制):\n{0}\n\n是否仍要继续推送?', + 'warning.deleteModules': '以下 Modules 存在于 CATIA 中,但不存在于 VSCode 中:\n{0}\n\n是否从 CATIA 中删除它们以实现完全同步?', + 'warning.newUserForms': '以下 Forms 在 CATIA 中不存在,无法新建。请先在 CATIA 中创建同名的空 Forms。这些文件将被跳过:\n{0}', + 'warning.noMoreFiles': '没有更多可 Push 的文件。处理结束。', + 'warning.longModuleNames': '以下 Module 名称超过了 31 个字符(VBA 编辑器的限制):\n{0}\n\n是否仍要继续 Push?', // Dialog options 'dialog.delete': '是 (删除)', @@ -245,11 +295,11 @@ export const messages = { 'dialog.continue': '继续', // Progress titles - 'progress.pull': '正在从 CATIA 拉取 VBA ({0})...', - 'progress.push': '正在向 CATIA 推送 VBA ({0})...', + 'progress.pull': '正在从 CATIA Pull VBA ({0})...', + 'progress.push': '正在向 CATIA Push VBA ({0})...', // Select project - 'select.placeholder': '选择要同步的 CATIA VBA 项目', + 'select.placeholder': '选择要同步的 CATIA VBA Target Project', // Language switch 'language.title': '请选择语言', @@ -277,6 +327,31 @@ export const messages = { 'init.overwrite': '覆盖', 'init.gitignoreExists': '.gitignore 已存在。是否覆盖?', 'init.success': '已创建 cat5dev.toml。', + + // Logs + 'log.vbafmt.binaryNotFound': '[vbafmt] 未找到二进制文件: {0}', + 'log.vbafmt.buildBinaryPrompt': '[vbafmt] 请运行 "npm run compile:go" 以构建二进制文件', + 'log.vbafmt.serverStarted': '[vbafmt] 服务启动完成 (port: {0})', + 'log.vbafmt.serverStartFailed': '[vbafmt] 服务启动失败: {0}', + 'log.vbafmt.serverTimeout': '服务启动超时', + 'log.vbafmt.processExited': '进程已退出 (exit {0})', + 'log.vbafmt.maxRestartReached': '[vbafmt] 已达到服务重启上限。请手动重启。', + 'log.vbafmt.restarting': '[vbafmt] 将在 {0} 秒后重启... ({1}/{2})', + 'log.vbalint.error': '[vba-lint] 错误: {0}', + 'log.vbalint.requestError': '[vba-lint] 请求错误: {0}', + 'log.vbafmt.serverNotRunning': '[vbafmt] 服务未运行', + 'log.vbafmt.error': '[vbafmt] 错误: {0}', + 'log.vbafmt.requestError': '[vbafmt] 请求错误: {0}', + 'log.catscript.empty': '(空)', + 'log.push.start': '[Push] Start: language={0}, encoding={1}', + 'log.pull.start': '[Pull] Start: language={0}, encoding={1}', + 'log.push.tempDir': '[Push] tempDir: {0}', + 'log.push.targetProject': '[Push] Target Project: {0}', + 'log.push.fileCount': '[Push] 传输文件数: {0}', + 'log.push.catscriptGenerated': '[Push] 生成 CATScript: {0}', + 'log.push.vbsGenerated': '[Push] 生成 VBS: {0}', + 'log.push.cscriptStart': '[Push] cscript 开始运行...', + 'log.push.cscriptEnd': '[Push] cscript 结束 error={0} stdout="{1}" stderr="{2}"', } }; diff --git a/src/linter.ts b/src/linter.ts index 0232aad..2f3c95f 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { VbaServer, httpPost } from './vbaServer'; import { readLintOptions, LintOptions } from './lintConfig'; +import { t } from './i18n'; const LINT_TIMEOUT_MS = 10000; const DEBOUNCE_MS = 500; @@ -37,12 +38,12 @@ async function callLint( const raw = await httpPost(`${baseUrl}/lint`, body, LINT_TIMEOUT_MS); const resp = JSON.parse(raw) as LintResponse; if (resp.error) { - outputChannel.appendLine(`[vba-lint] エラー: ${resp.error}`); + outputChannel.appendLine(t('log.vbalint.error', resp.error)); return null; } return resp.diagnostics ?? []; } catch (err) { - outputChannel.appendLine(`[vba-lint] リクエストエラー: ${err}`); + outputChannel.appendLine(t('log.vbalint.requestError', String(err))); return null; } } diff --git a/src/vbaServer.ts b/src/vbaServer.ts index f97e8be..e521085 100644 --- a/src/vbaServer.ts +++ b/src/vbaServer.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import * as fs from 'fs'; import * as http from 'http'; import { spawn, ChildProcess } from 'child_process'; +import { t } from './i18n'; const SERVER_START_TIMEOUT_MS = 5000; const MAX_RESTART_COUNT = 3; @@ -25,16 +26,16 @@ export class VbaServer implements vscode.Disposable { async start(): Promise { if (!fs.existsSync(this.binaryPath)) { - this.outputChannel.appendLine(`[vbafmt] バイナリが見つかりません: ${this.binaryPath}`); - this.outputChannel.appendLine('[vbafmt] npm run compile:go を実行してバイナリをビルドしてください'); + this.outputChannel.appendLine(t('log.vbafmt.binaryNotFound', this.binaryPath)); + this.outputChannel.appendLine(t('log.vbafmt.buildBinaryPrompt')); return; } try { this.port = await this.spawnServer(); - this.outputChannel.appendLine(`[vbafmt] サーバー起動完了 (port: ${this.port})`); + this.outputChannel.appendLine(t('log.vbafmt.serverStarted', String(this.port))); } catch (err) { - this.outputChannel.appendLine(`[vbafmt] サーバー起動失敗: ${err}`); + this.outputChannel.appendLine(t('log.vbafmt.serverStartFailed', String(err))); } } @@ -47,7 +48,7 @@ export class VbaServer implements vscode.Disposable { const timer = setTimeout(() => { proc.kill(); - reject(new Error('サーバー起動タイムアウト')); + reject(new Error(t('log.vbafmt.serverTimeout'))); }, SERVER_START_TIMEOUT_MS); let portResolved = false; @@ -77,7 +78,7 @@ export class VbaServer implements vscode.Disposable { proc.on('exit', (code) => { clearTimeout(timer); if (!portResolved) { - reject(new Error(`プロセスが終了しました (exit ${code})`)); + reject(new Error(t('log.vbafmt.processExited', String(code)))); return; } this.port = null; @@ -91,12 +92,12 @@ export class VbaServer implements vscode.Disposable { private scheduleRestart(): void { if (this.restartCount >= MAX_RESTART_COUNT) { - this.outputChannel.appendLine('[vbafmt] サーバー再起動の上限に達しました。手動で再起動してください。'); + this.outputChannel.appendLine(t('log.vbafmt.maxRestartReached')); return; } const delay = Math.pow(2, this.restartCount) * 1000; this.restartCount++; - this.outputChannel.appendLine(`[vbafmt] ${delay / 1000}秒後に再起動します... (${this.restartCount}/${MAX_RESTART_COUNT})`); + this.outputChannel.appendLine(t('log.vbafmt.restarting', String(delay / 1000), String(this.restartCount), String(MAX_RESTART_COUNT))); setTimeout(() => { if (!this.disposed) { this.start(); From 3bcc5c56ad57e47e44a70b335c00c8be6277f6cc Mon Sep 17 00:00:00 2001 From: zedeeee <61963989+zedeeee@users.noreply.github.com> Date: Fri, 5 Jun 2026 00:53:29 +0800 Subject: [PATCH 6/6] docs: update English README and Chinese README for encoding and zh language configurations --- README.md | 18 ++++++++++-------- docs/README.zh-CN.md | 14 ++++++++------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ea2fba4..dec697d 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Quick-access buttons are available in the panel title bar: | 🌐 | Switch language | ### Multi-Language Support -The extension supports both **Japanese (日本語)** and **English**. +The extension supports **Japanese (日本語)**, **English**, and **Chinese (简体中文)**. - Click the 🌐 button in the panel title bar to switch languages - The language preference is saved in `cat5dev.toml` under the `[project]` section - All UI messages, TreeView labels, and tooltips will be translated @@ -215,7 +215,8 @@ Before using Cat5Dev for the first time, **backup your CATIA V5 settings folder* > Settings are stored in `cat5dev.toml` under the `[project]` section: > - `target_project` — The name of the target CATIA VBA project -> - `language` — `"ja"` for Japanese, `"en"` for English (default: `"ja"`) +> - `language` — `"ja"` for Japanese, `"en"` for English, `"zh"` for Chinese (default: `"ja"`) +> - `encoding` — The character encoding of VBA code transfer (e.g., `"gbk"`, `"shift_jis"`, `"utf-8"`). Defaults to `"gbk"` for Chinese (`"zh"`), `"shift_jis"` for Japanese (`"ja"`), and `"utf-8"` for English (`"en"`). --- @@ -267,13 +268,14 @@ Well… that’s a different question entirely. --- -## Notes on Encoding (Please Help) +## Notes on Encoding +VBA files are synchronized between VSCode (UTF-8) and CATIA V5 (native ANSI, e.g., Shift-JIS or GBK). +By default, Cat5Dev automatically detects and sets the transfer encoding based on the configured language: +- `"gbk"` for Chinese (`"zh"`) +- `"shift_jis"` for Japanese (`"ja"`) +- `"utf-8"` for English (`"en"`) -I have only ever used the Japanese version of CATIA V5, so I am familiar only with files exported from the VBA Editor being saved in Shift‑JIS. Because this was quite inconvenient, Cat5Dev automatically converts the encoding to UTF‑8 during the Pull/Push process. - -If you encounter garbled characters when using this extension in your environment, you may need to adjust the encoding settings. Unfortunately, I have no way to verify its behavior outside of a Japanese environment. - -If you run into any issues, please feel free to let me know. +You can customize this by setting the `encoding` property under the `[project]` section in `cat5dev.toml` (e.g., `encoding = "gb2312"` or `encoding = "shift_jis"`). If you run into any issues, please feel free to let me know. --- diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index 1a85f66..3ce1a16 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -214,6 +214,7 @@ Cat5Dev 以 `.vsix` 文件形式分发。安装步骤如下: > 配置保存在 `cat5dev.toml` 的 `[project]` 部分下: > - `target_project` — 目标 CATIA VBA 项目的名称 > - `language` — `"ja"` 表示日语,`"en"` 表示英语,`"zh"` 表示中文(默认:`"ja"`) +> - `encoding` — VBA 代码传输的字符编码(如 `"gbk"`、`"shift_jis"`、`"utf-8"` 等)。中文 (`"zh"`) 默认值为 `"gbk"`,日语 (`"ja"`) 默认值为 `"shift_jis"`,英语 (`"en"`) 默认值为 `"utf-8"`。 --- @@ -265,13 +266,14 @@ VSCode (编辑) → 推送 (Push) → 在 CATIA 的 VBA 编辑器中保存 ✅ --- -## 关于编码的注意事项(请提供帮助) +## 关于编码的注意事项 +VBA 文件是在 VSCode (UTF-8) 和 CATIA V5 (原生 ANSI 编码,如 GBK 或 Shift-JIS) 之间进行同步的。 +默认情况下,Cat5Dev 会根据您配置的 `language` 自动推导和设置传输编码: +- 中文 (`"zh"`) 系统默认为 `"gbk"` +- 日语 (`"ja"`) 系统默认为 `"shift_jis"` +- 英语 (`"en"`) 系统默认为 `"utf-8"` -我只使用过日语版的 CATIA V5,所以只熟悉从 VBA 编辑器导出的、以 Shift-JIS 保存的文件。由于这非常不方便,Cat5Dev 在拉取/推送过程中会自动将编码转换为 UTF-8。 - -如果您在您的环境中使用此扩展时遇到乱码,您可能需要调整编码设置。很遗憾,我没有办法在日语环境之外验证其行为。 - -如果您遇到任何问题,欢迎随时联系我。 +如果需要,您可以在 `cat5dev.toml` 文件的 `[project]` 部分下,通过设置 `encoding` 属性来自定义您所需的任何编码(例如 `encoding = "gb2312"`)。如果遇到任何问题,欢迎随时联系我。 ---