diff --git a/docs/superpowers/specs/code-reader/spec.md b/docs/superpowers/specs/code-reader/spec.md index 14c51ef..fd3adfa 100644 --- a/docs/superpowers/specs/code-reader/spec.md +++ b/docs/superpowers/specs/code-reader/spec.md @@ -77,7 +77,7 @@ export interface CodeBlock { export type TokenRole = | 'keyword' // 流程关键字:import from export if else for return try catch await … | 'storage' // 声明关键字与字面量关键字:const function async class interface new true false null … - | 'operator' // 被翻成文字的运算符:全等 与 或 非 + | 'operator' // 被翻成文字的运算符:全等 并且 或者 非 | 'variable' | 'function' | 'type' | 'string' | 'number' | 'literal' | 'comment' | 'plain' export interface Segment { text: string, role: TokenRole } @@ -157,7 +157,7 @@ export interface CodeReaderApi { 1. 关键字:只认语法树里的关键字节点(匿名节点),所以属性名 `i.number`、对象键不会被误翻。 2. 内置类型:只在类型位置翻(TS 的 `predefined_type`、Python 注解里的内置类型名)。 -3. 运算符:`===` 全等、`!==` 不全等、`==` 等于、`!=` 不等于、`&&` 与、`||` 或、`!` 非(Python 的 `not` / `and` / `or` 本身是关键字)。 +3. 运算符:`===` 全等、`!==` 不全等、`==` 等于、`!=` 不等于、`&&` 并且、`||` 或者、`!` 非(Python 的 `not` / `and` / `or` 本身是关键字)。 其他规则:空数组字面量 → 空数组(Python 空列表 → 空列表、空字典 → 空字典);复合语句头部去掉结尾的 `{` 或 `:`;多行合成一行(连续空白压成一个空格,右括号前的尾逗号去掉);注释去掉注释符号,原文照搬。关键字与前后内容之间保留源码里的空格(`!` 翻成「非」后补一个空格)。 @@ -178,7 +178,7 @@ TypeScript 关键字表(流程 = keyword,声明 = storage): TypeScript 内置类型:string 字符串、number 数值、boolean 布尔、any 任意、unknown 未知、never 永不、void 无、object 对象、bigint 大整数、symbol 符号。`Promise` 等库类型不翻。 -Python 关键字:import 导入、from(导入)从 / (`raise … from`)源自 / (`yield from`)自、as(导入别名、`with … as`、`except … as`)作为、def 函数、async 异步、await 等待、class 类、return 返回、if 如果、elif 否则如果、else(`if` 后)否则 / (`for` / `while` 后,循环没被 break 才执行)未中断时 / (`try` 后,没出错才执行)未出错时、for 遍历、in(比较里)在 / (for 头部与推导式里)取自、while 当、try 尝试、except 捕获、finally 最终、raise 抛出、with 使用、yield 产出、lambda 匿名函数、pass 跳过、break 跳出、continue 继续、global 全局、nonlocal 非局部、del 删除、assert 断言、not 非、and 与、or 或、is 是、not in 不在、is not 不是、None 空、True 真、False 假、match 匹配、case 情况。 +Python 关键字:import 导入、from(导入)从 / (`raise … from`)源自 / (`yield from`)自、as(导入别名、`with … as`、`except … as`)作为、def 函数、async 异步、await 等待、class 类、return 返回、if 如果、elif 否则如果、else(`if` 后)否则 / (`for` / `while` 后,循环没被 break 才执行)未中断时 / (`try` 后,没出错才执行)未出错时、for 遍历、in(比较里)在 / (for 头部与推导式里)取自、while 当、try 尝试、except 捕获、finally 最终、raise 抛出、with 使用、yield 产出、lambda 匿名函数、pass 跳过、break 跳出、continue 继续、global 全局、nonlocal 非局部、del 删除、assert 断言、not 非、and 并且、or 或者、is 是、not in 不在、is not 不是、None 空、True 真、False 假、match 匹配、case 情况。 Python 内置类型(仅注解里):str 字符串、int 整数、float 浮点数、bool 布尔、list 列表、dict 字典、set 集合、tuple 元组、bytes 字节串。 diff --git a/vscode/src/codeReader/contract.ts b/vscode/src/codeReader/contract.ts index 5ef12bd..9ee97af 100644 --- a/vscode/src/codeReader/contract.ts +++ b/vscode/src/codeReader/contract.ts @@ -23,7 +23,7 @@ export interface CodeBlock { export type TokenRole = | 'keyword' // 流程关键字:import from export if else for return try catch await … | 'storage' // 声明关键字与字面量关键字:const function async class interface new true false null … - | 'operator' // 被翻成文字的运算符:全等 与 或 非 + | 'operator' // 被翻成文字的运算符:全等 并且 或者 非 | 'variable' | 'function' | 'type' | 'string' | 'number' | 'literal' | 'comment' | 'plain' export interface Segment { text: string, role: TokenRole } diff --git a/vscode/src/codeReader/translators/pythonZh.ts b/vscode/src/codeReader/translators/pythonZh.ts index 3d534af..b4b3ed9 100644 --- a/vscode/src/codeReader/translators/pythonZh.ts +++ b/vscode/src/codeReader/translators/pythonZh.ts @@ -56,8 +56,8 @@ export const pythonZhTranslator: Translator = createZhTemplateTranslator({ 'del': '删除', 'assert': '断言', 'not': '非', - 'and': '与', - 'or': '或', + 'and': '并且', + 'or': '或者', 'is': '是', 'not in': '不在', 'is not': '不是', diff --git a/vscode/src/codeReader/translators/typescriptZh.ts b/vscode/src/codeReader/translators/typescriptZh.ts index 779b2a2..9bc1948 100644 --- a/vscode/src/codeReader/translators/typescriptZh.ts +++ b/vscode/src/codeReader/translators/typescriptZh.ts @@ -83,7 +83,7 @@ export const typescriptZhTranslator: Translator = createZhTemplateTranslator({ symbol: '符号', }, isTypePosition: leaf => leaf.parent?.type === 'predefined_type', - operators: { '===': '全等', '!==': '不全等', '==': '等于', '!=': '不等于', '&&': '与', '||': '或', '!': '非' }, + operators: { '===': '全等', '!==': '不全等', '==': '等于', '!=': '不等于', '&&': '并且', '||': '或者', '!': '非' }, operatorParents: ['binary_expression', 'unary_expression'], emptyLiterals: { array: '空数组' }, stringNodes: ['string', 'template_string', 'regex'], diff --git a/vscode/src/codeReader/translators/zhTemplate.test.ts b/vscode/src/codeReader/translators/zhTemplate.test.ts index 7900fa4..dc7d5d3 100644 --- a/vscode/src/codeReader/translators/zhTemplate.test.ts +++ b/vscode/src/codeReader/translators/zhTemplate.test.ts @@ -85,7 +85,7 @@ describe('typeScript 中文直译', () => { expect(issue[1]).toBe('导入 类型 { Issue } 来自 \'../gitea/types\'') expect(issue[5]).toBe('prerequisiteNumber?: 数值') expect(issue[9]).toBe('纯判定:工单被未完成的前置挡住才算锁定。快照里找不到工单或前置一律放行, 与 webview 的 `isIssueLocked` 保持同一语义。') - expect(issue[15]).toBe('如果 (非 issue 或 issue.prerequisite 全等 未定义)') + expect(issue[15]).toBe('如果 (非 issue 或者 issue.prerequisite 全等 未定义)') }) it('每个被翻的词单独成段,着色取语法树词性', async () => { @@ -112,11 +112,15 @@ describe('typeScript 中文直译', () => { ].join('\n') const map = texts(await translateAll(ts, 'typescript', typescriptZhTranslator, source)) expect(map[1]).toBe('常量 o = { if: 1, return: i.number, string: x!.type, new: a.default }') - expect(map[2]).toBe('变量 n = a 不全等 b 与 非 c 或 d 等于 e 不等于 f') + expect(map[2]).toBe('变量 n = a 不全等 b 并且 非 c 或者 d 等于 e 不等于 f') // eslint-disable-next-line no-template-curly-in-string -- 模板字符串原样照抄进译文 expect(map[3]).toBe('常量 s: 字符串 | 无 = 如果 类型为 v 全等 \'string\' 则 `${v}` 否则 \'number\'') }) + it('逻辑与、或译成「并且」「或者」', async () => { + expect(texts(await translateAll(ts, 'typescript', typescriptZhTranslator, 'a && b || c'))).toEqual({ 1: 'a 并且 b 或者 c' }) + }) + it('多行合成一行、去掉右括号前的尾逗号、行中注释不进译文', async () => { const source = [ 'call(', @@ -172,7 +176,7 @@ describe('typeScript 中文直译', () => { 1: '如果 err 属于 Error 则 err.message 否则 String(err)', 2: 'z = 如果 x 则 (如果 y 则 1 否则 2) 否则 3', 3: '常量 w = 如果 a 则 b 否则 如果 c 则 d 否则 e', - 4: '常量 v = 如果 a 不全等 b 与 非 c 则 类型为 d 否则 等待 e', + 4: '常量 v = 如果 a 不全等 b 并且 非 c 则 类型为 d 否则 等待 e', }) expect(map.get(1)?.segments.filter(segment => segment.role === 'keyword')).toEqual([ { text: '如果', role: 'keyword' }, @@ -195,7 +199,7 @@ describe('python 中文直译', () => { 10: '函数 find_env_files(root: 字符串) -> 列表[字符串]', 11: 'Return every .env file under root.', 12: '跳过备份文件', - 13: 'found = [p 遍历 p 取自 os.listdir(root) 如果 p.startswith(\'.env\') 与 非 p.endswith(\'.bak\')]', + 13: 'found = [p 遍历 p 取自 os.listdir(root) 如果 p.startswith(\'.env\') 并且 非 p.endswith(\'.bak\')]', 14: '如果 非 found', 15: '返回 空列表', 16: '否则如果 len(found) > MAX_DEPTH', @@ -234,6 +238,10 @@ describe('python 中文直译', () => { expect(map[2]).toBe('返回 [str(k) 遍历 k 取自 items 如果 k 不在 list]') }) + it('逻辑与、或译成「并且」「或者」', async () => { + expect(texts(await translateAll(py, 'python', pythonZhTranslator, 'a and b or c'))).toEqual({ 1: 'a 并且 b 或者 c' }) + }) + it('from:导入里「从」,raise … from 里「源自」,yield from 里「自」', async () => { const source = [ 'from typing import Annotated', @@ -286,7 +294,7 @@ describe('python 中文直译', () => { 1: '_default_port = 如果 _role 等于 "beat" 则 5680 否则 5679', 2: 'y = 如果 x 则 a 否则 如果 y 则 b 否则 c', 3: 'z = 如果 y 则 (如果 x 则 a 否则 b) 否则 c', - 4: 'v = 如果 非 done 与 ok 则 [k 遍历 k 取自 items 如果 k 不在 seen] 否则 空', + 4: 'v = 如果 非 done 并且 ok 则 [k 遍历 k 取自 items 如果 k 不在 seen] 否则 空', }) expect(map.get(1)?.segments.filter(segment => segment.role === 'keyword' || segment.role === 'operator')).toEqual([ { text: '如果', role: 'keyword' },