optiland-gui-zh 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- optiland_gui_zh-0.1.0/CONTRIBUTING.md +129 -0
- optiland_gui_zh-0.1.0/LICENSE +21 -0
- optiland_gui_zh-0.1.0/MANIFEST.in +15 -0
- optiland_gui_zh-0.1.0/NOTICE.md +42 -0
- optiland_gui_zh-0.1.0/PKG-INFO +329 -0
- optiland_gui_zh-0.1.0/README.en.md +308 -0
- optiland_gui_zh-0.1.0/README.md +295 -0
- optiland_gui_zh-0.1.0/docs/intro.md +317 -0
- optiland_gui_zh-0.1.0/pyproject.toml +86 -0
- optiland_gui_zh-0.1.0/setup.cfg +4 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/PKG-INFO +329 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/SOURCES.txt +25 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/dependency_links.txt +1 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/entry_points.txt +2 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/requires.txt +8 -0
- optiland_gui_zh-0.1.0/src/optiland_gui_zh.egg-info/top_level.txt +1 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/__init__.py +39 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/audit.py +234 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/catalog.py +246 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/catalogs/zh_CN.json +781 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/engine.py +647 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/extract.py +565 -0
- optiland_gui_zh-0.1.0/src/optiland_zh/launcher.py +288 -0
- optiland_gui_zh-0.1.0/tests/test_catalog.py +172 -0
- optiland_gui_zh-0.1.0/tests/test_engine.py +169 -0
- optiland_gui_zh-0.1.0/tests/test_launcher.py +102 -0
- optiland_gui_zh-0.1.0/tests/test_packaging.py +140 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# 贡献指南
|
|
2
|
+
|
|
3
|
+
三条路,按所需技能从低到高。
|
|
4
|
+
|
|
5
|
+
## 一、补词条(不需要懂 Python)
|
|
6
|
+
|
|
7
|
+
1. 跑一遍提取器看还缺什么:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
python -m optiland_zh.extract --diff src/optiland_zh/catalogs/zh_CN.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
它按源文件分组列出没翻译的字符串。`strings.json` 里每个词条还带**出现位置**
|
|
14
|
+
(文件 + 行号),想确认上下文就去翻那一行。
|
|
15
|
+
|
|
16
|
+
2. 在 `src/optiland_zh/catalogs/zh_CN.json` 的 `entries` 里加一条:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
"Analysis Settings": "分析设置"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
3. 验证覆盖率涨了:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
optiland-zh --coverage
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**术语请对齐 Zemax 中文版**,见 README 底部的对照表。新术语先查一下
|
|
29
|
+
《光学名词》或 Zemax 中文界面,不要自己造词。
|
|
30
|
+
|
|
31
|
+
### 什么不该翻
|
|
32
|
+
|
|
33
|
+
| 类型 | 例子 | 原因 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| 字体名 | `Cascadia Code` | 翻了会换成别的字体 |
|
|
36
|
+
| 第三方算法名 | `BFGS`、`SLSQP`、`trust-constr` | 是 scipy 的标识符,中文界也照写 |
|
|
37
|
+
| Qt 内部标识符 | `QuickActionsToolbar` | objectName,翻了会破坏控件查找和样式表 |
|
|
38
|
+
| 品牌名 | `Optiland` | 产品名 |
|
|
39
|
+
|
|
40
|
+
### 翻译时要注意的地方
|
|
41
|
+
|
|
42
|
+
- **`&` 是快捷键标记**,中文惯例写成 `文件(&F)`,不要丢掉它。
|
|
43
|
+
- **`%s` / `%d` 是 printf 占位符**,必须原样保留、数量一致。
|
|
44
|
+
- **文件对话框过滤器**形如 `JSON Files (*.json);;All Files (*)`,
|
|
45
|
+
只翻描述部分,`*.json` 和 `;;` 分隔符不能动。
|
|
46
|
+
- **HTML 标签**(`<b>` `<h3>`)要保留。
|
|
47
|
+
|
|
48
|
+
## 二、加一种语言(也不需要懂 Python)
|
|
49
|
+
|
|
50
|
+
1. 复制 `src/optiland_zh/catalogs/zh_CN.json`
|
|
51
|
+
2. 改名成目标语言代码(如 `zh_TW.json`、`ja_JP.json`)
|
|
52
|
+
3. 改 `language`(必须和文件名一致)和 `display_name`
|
|
53
|
+
4. 翻译 `entries` 的值,以及 `patterns` 里每条规则的 `replace`
|
|
54
|
+
—— **`match` 字段绝对不能改**,它是匹配用的模板
|
|
55
|
+
5. 验证:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
optiland-zh --list-languages
|
|
59
|
+
optiland-zh -l ja_JP --coverage
|
|
60
|
+
optiland-zh -l ja_JP --self-test
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`patterns` 用 `{0} {1}` 位置占位符,写法和 `str.format` 一样。
|
|
64
|
+
占位符的**数量和顺序**可以和 `match` 不同(比如中文语序不同),
|
|
65
|
+
但不能引用不存在的编号。
|
|
66
|
+
|
|
67
|
+
## 三、改引擎(需要懂 Python + Qt)
|
|
68
|
+
|
|
69
|
+
### 先读这两件事
|
|
70
|
+
|
|
71
|
+
1. **回译机制不能删。** `currentText()` / `QLineEdit.text()` 必须把原文还给
|
|
72
|
+
程序内部,否则分析、面型编辑、优化变量会静默失效。详见 README 的
|
|
73
|
+
「最难的部分」。
|
|
74
|
+
2. **`itemText()` 故意不打补丁。** Qt 用它渲染下拉列表。要动它之前先想清楚
|
|
75
|
+
列表显示怎么办。
|
|
76
|
+
|
|
77
|
+
### 加一个拦截点
|
|
78
|
+
|
|
79
|
+
界面上还有英文,先判断它是怎么产生的:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
python -m optiland_zh.audit # 走一遍真实控件树,列出没翻的文字
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
如果 `audit` 报了「命中英文键,补丁没生效」,说明词库里有译文但补丁没拦到。
|
|
86
|
+
这时去 `engine.py` 看:
|
|
87
|
+
|
|
88
|
+
- 控件**构造时**传的文字 → 加进 `_CONSTRUCTORS`
|
|
89
|
+
- 控件**建立后**设的文字 → 加进 `_TEXT_METHODS`
|
|
90
|
+
- 参数是字符串列表 → 加进 `_LIST_METHODS`
|
|
91
|
+
- 静态对话框(`QMessageBox.information` 等)→ 加进 `_DIALOG_STATICS`
|
|
92
|
+
- 下拉框、输入框这种需要回译的 → 单独写包装器
|
|
93
|
+
|
|
94
|
+
**判断某个类该不该进 `_CONSTRUCTORS`**:看它的 `__init__` 有没有"一上来就是
|
|
95
|
+
标题"的位置参数。只收 `parent` 的(`QWidget` / `QMainWindow` / `QDialog` /
|
|
96
|
+
`QTabWidget`)不要加。
|
|
97
|
+
|
|
98
|
+
加完必须:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
optiland-zh --self-test
|
|
102
|
+
python -m optiland_zh.audit
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 别忘了改提取器
|
|
106
|
+
|
|
107
|
+
新增了一个界面文字接口,`extract.py` 里的 `TEXT_APIS` 通常也要跟着加,
|
|
108
|
+
否则提取器捞不到通过它传的字符串,下次升级算差集就会漏。
|
|
109
|
+
|
|
110
|
+
反过来说:**往 `TEXT_APIS` 里加之前先确认那个接口收的全是给人看的文字。**
|
|
111
|
+
`_create_dock` 就是个反例——它第二个参数是 objectName,加了会把内部标识符
|
|
112
|
+
当成待翻译项收进来。
|
|
113
|
+
|
|
114
|
+
## 提交
|
|
115
|
+
|
|
116
|
+
- 一个小 PR 只做一件事:要么补词条,要么加语言,要么改引擎。
|
|
117
|
+
- 改引擎的 PR 请在描述里贴 `--self-test` 和 `--audit` 的输出。
|
|
118
|
+
- 加语言的 PR 请贴 `--coverage` 的数字,别低得离谱(目前 zh_CN 是 99.5%)。
|
|
119
|
+
|
|
120
|
+
## 报告问题
|
|
121
|
+
|
|
122
|
+
带上这些信息,排查会快很多:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
optiland-zh --list-languages
|
|
126
|
+
python -c "import PySide6, optiland; print(PySide6.__version__, optiland.__version__)"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
以及出问题的那块界面的截图。
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 optiland-zh contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# sdist 的默认内容只有 LICENSE / README.md / pyproject.toml / 包本体。
|
|
2
|
+
# 下面这些是"应该跟源码一起分发"的东西,默认不会被带上。
|
|
3
|
+
#
|
|
4
|
+
# 尤其是 NOTICE.md:LICENSE 讲了本项目的授权,NOTICE 讲了它和
|
|
5
|
+
# Optiland、PySide6/Qt 的关系。只发前者会让下游看不懂依赖边界。
|
|
6
|
+
|
|
7
|
+
include NOTICE.md
|
|
8
|
+
include CONTRIBUTING.md
|
|
9
|
+
include README.en.md
|
|
10
|
+
|
|
11
|
+
# 项目介绍(叙述式技术文档,对下游读源码有帮助)
|
|
12
|
+
recursive-include docs *.md
|
|
13
|
+
|
|
14
|
+
# 不用带 docs/*.png:README 和 intro.md 里的截图走的是
|
|
15
|
+
# raw.githubusercontent 绝对地址,sdist 里再塞一份是白增 230KB。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 第三方声明
|
|
2
|
+
|
|
3
|
+
本文件是补充说明,**不是许可证**。本项目的许可证是 [MIT](LICENSE)。
|
|
4
|
+
|
|
5
|
+
## Optiland
|
|
6
|
+
|
|
7
|
+
本项目汉化的对象是 [Optiland](https://github.com/optiland/optiland) 的图形界面。
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MIT License
|
|
11
|
+
Copyright (c) 2024 Kramer Harrison
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
项目主页:<https://github.com/optiland/optiland>
|
|
15
|
+
|
|
16
|
+
### 本项目与 Optiland 的关系
|
|
17
|
+
|
|
18
|
+
`optiland-zh` **不分发、不修改、也不链接** Optiland 的源码。它的工作方式是
|
|
19
|
+
在用户自己的进程里,于运行期给 PySide6 的绑定方法重新赋值,从而在文字进入
|
|
20
|
+
Qt 之前把它替换掉。
|
|
21
|
+
|
|
22
|
+
本仓库只包含自己的源代码和翻译数据。Optiland 是运行期依赖,需要单独安装:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pip install "optiland[gui]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## PySide6 / Qt
|
|
29
|
+
|
|
30
|
+
本项目的汉化层依赖 PySide6 提供的 Qt 绑定,并在运行期加载 PySide6 自带的
|
|
31
|
+
Qt 翻译文件(`qtbase_zh_CN.qm`)来覆盖 Qt 内置的文字(对话框按钮、
|
|
32
|
+
文件选择器的标签等)。
|
|
33
|
+
|
|
34
|
+
PySide6 由 Qt 公司及其贡献者提供,采用 LGPL v3 / 商业双许可。
|
|
35
|
+
本项目不打包、不修改这些文件,只是在运行期引用它们。
|
|
36
|
+
|
|
37
|
+
## 术语参考
|
|
38
|
+
|
|
39
|
+
词库里的中文术语对齐 Zemax OpticStudio 中文版的习惯用法(Lens Data Editor →
|
|
40
|
+
镜头数据编辑器、Aperture → 孔径、Spot Diagram → 点列图 等)。
|
|
41
|
+
|
|
42
|
+
这里只是术语惯例的参考,不涉及任何 Zemax 的代码、数据或文件格式实现。
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: optiland-gui-zh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Optiland GUI 简体中文汉化包 —— 运行时翻译层,不改上游源码
|
|
5
|
+
Author: optiland-zh contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Kino2315/optiland-zh
|
|
8
|
+
Project-URL: Repository, https://github.com/Kino2315/optiland-zh
|
|
9
|
+
Project-URL: Issues, https://github.com/Kino2315/optiland-zh/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Kino2315/optiland-zh/releases
|
|
11
|
+
Keywords: optiland,optical-design,optics,ray-tracing,gui,i18n,localization,chinese,zh-cn,pyside6
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
24
|
+
Classifier: Topic :: Software Development :: Localization
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# optiland-zh
|
|
36
|
+
vibe coding
|
|
37
|
+
|
|
38
|
+
**Optiland GUI 简体中文汉化包** —— 运行期翻译层,不改上游源码一行。
|
|
39
|
+
|
|
40
|
+
[English](https://github.com/Kino2315/optiland-zh/blob/main/README.en.md) | 中文
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
> 这份 README 是**查阅手册**:怎么装、怎么用、怎么改。
|
|
45
|
+
> 想看这个项目的**来龙去脉和技术看点**(文字即逻辑键的陷阱、六个实测踩出来的坑、
|
|
46
|
+
> 覆盖率数字该怎么审查),读 [项目介绍](https://github.com/Kino2315/optiland-zh/blob/main/docs/intro.md)。
|
|
47
|
+
|
|
48
|
+
## 这是什么
|
|
49
|
+
|
|
50
|
+
[Optiland](https://github.com/optiland/optiland) 是一个功能完整的开源光学设计软件(序列/非序列光线追迹、优化、公差、MTF、Zemax 文件导入……),自带一个 Qt 图形界面。但它**只有英文,也没有任何多语言开关**——源码里 `self.tr()` 出现 0 次,没有 `.qm/.ts` 翻译文件,而且它还主动把语言钉死:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
QLocale.setDefault(QLocale(QLocale.Language.English, QLocale.Country.UnitedStates))
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
这个包把界面变成中文。**不修改 `site-packages`**,所以 `pip install -U optiland` 之后汉化依然有效。
|
|
57
|
+
|
|
58
|
+
## 安装
|
|
59
|
+
|
|
60
|
+
分三种人,三条路。**绝大多数人只需要第一段。**
|
|
61
|
+
|
|
62
|
+
### 一、普通用户
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
pip install "optiland[gui]" # ① Optiland 本体(约 300 MB)
|
|
66
|
+
pip install optiland-gui-zh # ② 本汉化包(46 KB)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**两条都要写。** 本包刻意不声明 `optiland` 依赖——那样会强制拉一份完整的 Qt
|
|
70
|
+
和 VTK,和你已经装好的版本打架。所以 pip 不会替你带上它。
|
|
71
|
+
|
|
72
|
+
装完直接跑:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
optiland-zh
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
就这一条命令。它会装上汉化补丁,然后拉起 Optiland GUI。
|
|
79
|
+
|
|
80
|
+
> **这一路完全不碰 git。** 用户不需要 clone 仓库,也不需要下载 Optiland 的源码——
|
|
81
|
+
> 本包**不含**上游源码,只在运行期给自己进程里的 Qt 打补丁。原因见
|
|
82
|
+
> [为什么不直接改源码](#为什么不直接改源码)。
|
|
83
|
+
|
|
84
|
+
### 二、从源码安装
|
|
85
|
+
|
|
86
|
+
想读代码、想改,或者 PyPI 上还没发布时:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
pip install "optiland[gui]"
|
|
90
|
+
git clone https://github.com/Kino2315/optiland-zh
|
|
91
|
+
cd optiland-zh
|
|
92
|
+
pip install .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 三、开发
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
git clone https://github.com/Kino2315/optiland-zh
|
|
99
|
+
cd optiland-zh
|
|
100
|
+
pip install -e ".[dev]" # -e 是 editable:改完代码立刻生效,不用重装
|
|
101
|
+
pytest -q
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 只装了汉化包、没装上游?
|
|
105
|
+
|
|
106
|
+
你会看到这段提示,而不是一坨 traceback:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
[optiland-zh] 找不到:PySide6、optiland_gui
|
|
110
|
+
|
|
111
|
+
本包只是汉化层,**不含 Optiland 本体**。请先装上游:
|
|
112
|
+
|
|
113
|
+
pip install "optiland[gui]"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
照着做即可。`--list-languages` 和 `--help` 不需要 Qt,任何环境下都能用。
|
|
117
|
+
|
|
118
|
+
## 使用
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
optiland-zh
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
就这一条。它会装上汉化,然后拉起 Optiland GUI。
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
optiland-zh --list-languages # 有哪些语言
|
|
128
|
+
optiland-zh --coverage # 词库覆盖率报告
|
|
129
|
+
optiland-zh --self-test # 离屏自检,不开窗口
|
|
130
|
+
optiland-zh -c my.json # 用自定义词库调试
|
|
131
|
+
optiland-zh --no-locale # 只换文字,不动 locale
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
也可以在自己的脚本里用:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import optiland_zh
|
|
138
|
+
|
|
139
|
+
optiland_zh.install() # 必须早于 QApplication
|
|
140
|
+
from optiland_gui.run_gui import main
|
|
141
|
+
main()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 为什么不直接改源码
|
|
145
|
+
|
|
146
|
+
改 `site-packages` 里的文件最简单,但 `pip install -U optiland` 一升级就全没了,而且没法作为独立项目分发。
|
|
147
|
+
|
|
148
|
+
所以这里的做法是:**在文字进入 Qt 之前把它换掉**。补丁打在 PySide6 的绑定类型上(PySide6 允许给它们赋值,这点已经实测确认)。
|
|
149
|
+
|
|
150
|
+
## 最难的部分:回译
|
|
151
|
+
|
|
152
|
+
汉化 Qt 应用有个隐藏陷阱,不注意就会把程序改坏。
|
|
153
|
+
|
|
154
|
+
Optiland GUI 里有 **18 处拿控件文字当逻辑键**用的代码:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
self.on_analysis_type_changed(self.analysisTypeCombo.currentText())
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
这一句是拿下拉框里**显示的文字**去查"分析类注册表"。如果把 `"Spot Diagram"` 翻成 `"点列图"`,`currentText()` 就返回 `"点列图"`,注册表查不到——**整个分析功能直接失效**。
|
|
161
|
+
|
|
162
|
+
`QLineEdit` 更危险,面型编辑就是这么写的:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
new_type = self.type_edit.text() # lens_editor.py:205
|
|
166
|
+
if new_type.lower().strip() in get_available_surface_types():
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
后面比的是**英文**面型表。只做单向翻译,`"标准" in ["standard", ...]` 是 `False`,面型修改会静默失效。
|
|
170
|
+
|
|
171
|
+
所以本项目的核心不是"替换字符串",而是**双向**的:
|
|
172
|
+
|
|
173
|
+
| 方向 | 做法 |
|
|
174
|
+
|---|---|
|
|
175
|
+
| 文字**进入** Qt | 英译中(构造器 / `setText` / `addItem` / `addMenu` / …) |
|
|
176
|
+
| 原文**存起来** | 下拉框存进该项的 `userData`(自定义角色);输入框存进 Qt 动态属性 |
|
|
177
|
+
| 逻辑**读出来** | `currentText()` / `QLineEdit.text()` 还原成英文 |
|
|
178
|
+
| 按文字**查找** | `findText()` / `setCurrentText()` 的查询词先中译英 |
|
|
179
|
+
|
|
180
|
+
界面是中文,程序内部看到的仍是英文,两边都不坏。
|
|
181
|
+
|
|
182
|
+
> `itemText()` **故意不回译**:Qt 拿它渲染下拉列表,回译了列表就变英文。
|
|
183
|
+
> 全库只有一处调用它,是喂给 `setCurrentText`,那条路径本来就是通的。
|
|
184
|
+
|
|
185
|
+
## 还拦了哪些"看不见"的地方
|
|
186
|
+
|
|
187
|
+
光拦 `QLabel` 是不够的,实测踩到过这些:
|
|
188
|
+
|
|
189
|
+
| 陷阱 | 后果 | 处理 |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| `QFormLayout.addRow("标签", 控件)` | 那个 QLabel 由 Qt 在 **C++ 内部**创建,Python 层看不到 | 补丁打在 `addRow` 上(一开始漏了 19 个标签) |
|
|
192
|
+
| `QDockWidget("标题", parent)` | 构造器不在白名单里就不翻 | 加入构造器清单 |
|
|
193
|
+
| Qt 自带的 OK / Cancel / 打开 / 保存 | 那些字是 Qt 内部产生的,Optiland 源码里根本没有 | 载入 `qtbase_zh_CN.qm`(PySide6 自带) |
|
|
194
|
+
| 优化器下拉项带前导空格做对齐 | `" Least Squares"` 精确匹配不上 `"Least Squares"` | 词库匹配容忍首尾空白,并保留原缩进 |
|
|
195
|
+
| `Toggle {0}` 这类模板 | `{0}` 里的 `"Analysis"` 不会自动翻,结果是「显示/隐藏 Analysis」 | 动态规则捕获到的内容再过一遍静态词条 |
|
|
196
|
+
|
|
197
|
+
## 词库
|
|
198
|
+
|
|
199
|
+
词库是**纯数据**,一种语言一个 JSON,放在 `src/optiland_zh/catalogs/`。
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"language": "zh_CN",
|
|
204
|
+
"entries": {
|
|
205
|
+
"&File": "文件(&F)",
|
|
206
|
+
"Lens Data Editor": "镜头数据编辑器"
|
|
207
|
+
},
|
|
208
|
+
"patterns": [
|
|
209
|
+
{ "match": "Field {0}: ({1}, {2})", "replace": "视场 {0}:({1}, {2})" }
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
动态消息用 `{0} {1}` 占位,运行期编译成正则——比让译者写 `(?P<n>.+?)` 友好得多。
|
|
215
|
+
|
|
216
|
+
### 加词条 / 加语言
|
|
217
|
+
|
|
218
|
+
1. 复制 `src/optiland_zh/catalogs/zh_CN.json`
|
|
219
|
+
2. 改 `language` / `display_name`
|
|
220
|
+
3. 翻译 `entries` 和 `patterns` 的 `replace`(**不要动 `match`**)
|
|
221
|
+
4. 跑 `optiland-zh -l <你的语言> --coverage` 看覆盖率
|
|
222
|
+
|
|
223
|
+
## 升级 Optiland 之后
|
|
224
|
+
|
|
225
|
+
上游加了新界面文字,词库就落后了。用提取器算差集:
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
python -m optiland_zh.extract --diff src/optiland_zh/catalogs/zh_CN.json
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
它会扫源码里的 AST,告诉你哪些新字符串还没翻,并按文件分组。提取器是**上下文感知**的,会自动排除:
|
|
232
|
+
|
|
233
|
+
- `setObjectName("AnalysisPanel")` 里的内部标识符
|
|
234
|
+
- 文档字符串(`"""..."""`)
|
|
235
|
+
- `print()` / `logger.debug()` 的控制台输出
|
|
236
|
+
- 样式表(QSS)、快捷键(`Ctrl+Q`)、配置键(`Layouts/Config`)
|
|
237
|
+
- 示例代码片段
|
|
238
|
+
|
|
239
|
+
不排除这些的话,第一次跑会得到 857 条候选,其中一大半是噪音;正确的过滤能压到 312 条,且都是真要翻的。
|
|
240
|
+
|
|
241
|
+
## 验证
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
optiland-zh --self-test # 7 项断言:构造器/setText/菜单/下拉显示/回译/findText
|
|
245
|
+
optiland-zh --coverage # 对源码的静态覆盖率
|
|
246
|
+
python -m optiland_zh.audit # 离屏建真实主窗口,遍历控件树逐条核对
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`--audit` 是最硬的证据:它把所有面板都建出来(含未展开的菜单、未激活的标签页),
|
|
250
|
+
逐个文字节点和词库比对。当前结果:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
窗口树里的文字节点: 1082
|
|
254
|
+
已覆盖: 1047 (96.8%)
|
|
255
|
+
未覆盖: 35
|
|
256
|
+
|
|
257
|
+
判定分档(强度从高到低):
|
|
258
|
+
974 exact-value 文字正好等于词库里的某条译文
|
|
259
|
+
73 cjk-heuristic 含中文即算已汉化(宽松)
|
|
260
|
+
35 MISS:not-translatable 本就是数字/objectName/第三方标识符
|
|
261
|
+
|
|
262
|
+
严格口径(只认 exact-value): 974 / 1082 = 90.0%
|
|
263
|
+
真缺口(补丁没生效 + 仍是英文原文): 0
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**为什么要分档。** 单看"96.8% 已覆盖"没法判断里面有多少是靠宽松规则凑的。
|
|
267
|
+
`exact-value` 是硬证据;`cjk-heuristic`(含中文就算汉化)覆盖的是动态规则
|
|
268
|
+
拼出来的结果——它不会出现在词条值里,但也显然已经汉化。想逐条核查:
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
python -m optiland_zh.audit --rule cjk-heuristic
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
那 73 条实测全部属实:Qt 自动派生的 tooltip(`文件(&F)` → `文件(F)`)、
|
|
275
|
+
动态消息(`显示/隐藏 分析`、`视场 1:(0.000, 0.000)`)、带缩进的下拉项。
|
|
276
|
+
|
|
277
|
+
真正该盯的数字是 **`真缺口 = 0`**:没有任何一处"词库有译文却没生效",
|
|
278
|
+
也没有任何一处"仍是英文原文"。剩下 35 条**本来就该是英文**:
|
|
279
|
+
Qt 的 objectName(`QuickActionsToolbar`)、scipy 算法名(`BFGS` / `SLSQP` /
|
|
280
|
+
`trust-constr`)、序号、品牌名 `Optiland`、装饰符 `|||`。
|
|
281
|
+
|
|
282
|
+
> 两个百分比别混:`--coverage` 是 **99.5%**,量的是"源码里的字符串翻了多少";
|
|
283
|
+
> `--audit` 是 **96.8%**,量的是"界面上实际出现的文字有多少是中文"。
|
|
284
|
+
> 前者按源文件算,后者按运行时控件算——同一个字符串可能出现在几十个控件上,
|
|
285
|
+
> 反过来动态拼出来的字符串在源码里根本不存在,所以两个数不会相等。
|
|
286
|
+
|
|
287
|
+
> `--audit` 会临时关掉 VTK:离屏平台拿不到 OpenGL 像素格式,
|
|
288
|
+
> 3D 视图会让进程段错误(Windows 上 0xC0000005)。关掉后 GUI 走
|
|
289
|
+
> "VTK 不可用" 的降级分支,界面文字照样齐全。
|
|
290
|
+
|
|
291
|
+
## 已知限制
|
|
292
|
+
|
|
293
|
+
- **画在图里的文字够不到。** 绘图窗口标题(如 `System: Default System (2D)`)
|
|
294
|
+
是 matplotlib 画的,不是 Qt 控件,本方案的补丁层碰不到。
|
|
295
|
+
需要 matplotlib 自己的翻译机制,暂未处理。
|
|
296
|
+
- **只覆盖 Optiland 的界面。** 第三方控件(Jupyter 控制台的右键菜单等)
|
|
297
|
+
能翻的部分靠 Qt 通用补丁顺带覆盖,剩余依赖它们自己的 i18n。
|
|
298
|
+
- **PySide6 版本差异。** 个别类在不同版本里位置不同(`QStandardItem` 在
|
|
299
|
+
`QtGui` 而非 `QtWidgets`),引擎遇到找不到的类会跳过而不是崩,
|
|
300
|
+
对应控件则不汉化。
|
|
301
|
+
|
|
302
|
+
## 本项目使用的术语
|
|
303
|
+
|
|
304
|
+
对齐 Zemax 中文版习惯:
|
|
305
|
+
|
|
306
|
+
| 英文 | 中文 |
|
|
307
|
+
|---|---|
|
|
308
|
+
| Lens Data Editor | 镜头数据编辑器 |
|
|
309
|
+
| Aperture / Field / Wavelength | 孔径 / 视场 / 波长 |
|
|
310
|
+
| Radius / Thickness / Material / Conic | 半径 / 厚度 / 材料 / 圆锥系数 |
|
|
311
|
+
| Stop / Sag / Semi-Diameter | 光阑 / 矢高 / 半口径 |
|
|
312
|
+
| Spot Diagram / Ray Fan | 点列图 / 光线扇形图 |
|
|
313
|
+
| OPD / MTF / PSF | 光程差 / 调制传递函数 / 点扩散函数 |
|
|
314
|
+
|
|
315
|
+
**故意不翻译**:`Cascadia Code`(字体名,翻了会换字体)、scipy 算法名、
|
|
316
|
+
Qt 的 objectName。
|
|
317
|
+
|
|
318
|
+
## 许可
|
|
319
|
+
|
|
320
|
+
MIT,见 [LICENSE](https://github.com/Kino2315/optiland-zh/blob/main/LICENSE)。
|
|
321
|
+
|
|
322
|
+
上游 [Optiland](https://github.com/optiland/optiland) 同为 MIT
|
|
323
|
+
(Copyright © 2024 Kramer Harrison)。本项目通过运行期补丁工作,不分发也不修改
|
|
324
|
+
其源码。第三方组件与术语来源的说明见 [NOTICE.md](https://github.com/Kino2315/optiland-zh/blob/main/NOTICE.md)。
|
|
325
|
+
|
|
326
|
+
## 贡献
|
|
327
|
+
|
|
328
|
+
见 [CONTRIBUTING.md](https://github.com/Kino2315/optiland-zh/blob/main/CONTRIBUTING.md)。最需要的贡献是**新语言**和**补词条**——
|
|
329
|
+
那两件事不需要懂 Python。
|