✨ feat(vscode): 条件表达式按「如果…则…否则…」翻译
This commit is contained in:
1 parent
3a184eed9b
commit
6897954644
5 files changed
+212
-27
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import type { LanguageAdapter, Segment, Translator } from '../contract'
|
||||
import type { ZhTable } from './zhTemplate'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createPythonAdapter } from '../languages/python'
|
||||
import { createTypeScriptAdapter } from '../languages/typescript'
|
||||
@@ -79,7 +80,7 @@ describe('typeScript 中文直译', () => {
|
||||
expect(env[32]).toBe('公共 接口 EnvLockResult')
|
||||
expect(env[42]).toBe('尝试')
|
||||
expect(env[46]).toBe('捕获 (err)')
|
||||
expect(env[47]).toBe('result.failed.push({ path: f, reason: err 属于 Error ? err.message : String(err) })')
|
||||
expect(env[47]).toBe('result.failed.push({ path: f, reason: 如果 err 属于 Error 则 err.message 否则 String(err) })')
|
||||
expect(env[1]).toBe('.env file lock / unlock helpers. Recursively scans the main workspace folder for `.env*` files (skipping `node_modules`, `.git`, `.claude`) and chmods them to 0o444 (locked) or 0o644 (unlocked) in bulk. Backs the toolbar Lock toggle in `PanelHeader`. No persistent watcher — re-scan happens on every toggle, so newly created `.env` files are only picked up the next time the user clicks the button.')
|
||||
expect(issue[1]).toBe('导入 类型 { Issue } 来自 \'../gitea/types\'')
|
||||
expect(issue[5]).toBe('prerequisiteNumber?: 数值')
|
||||
@@ -113,7 +114,7 @@ describe('typeScript 中文直译', () => {
|
||||
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')
|
||||
// eslint-disable-next-line no-template-curly-in-string -- 模板字符串原样照抄进译文
|
||||
expect(map[3]).toBe('常量 s: 字符串 | 无 = 类型为 v 全等 \'string\' ? `${v}` : \'number\'')
|
||||
expect(map[3]).toBe('常量 s: 字符串 | 无 = 如果 类型为 v 全等 \'string\' 则 `${v}` 否则 \'number\'')
|
||||
})
|
||||
|
||||
it('多行合成一行、去掉右括号前的尾逗号、行中注释不进译文', async () => {
|
||||
@@ -158,6 +159,28 @@ describe('typeScript 中文直译', () => {
|
||||
expect(map[11]).toBe('公共 默认 函数 g()')
|
||||
expect(map[13]).toBe('默认情况')
|
||||
})
|
||||
|
||||
it('三元表达式按「如果…则…否则…」重排,嵌套的、槽位里的关键字和运算符照样翻', async () => {
|
||||
const source = [
|
||||
'err instanceof Error ? err.message : String(err)',
|
||||
'z = x ? (y ? 1 : 2) : 3',
|
||||
'const w = a ? b : c ? d : e',
|
||||
'const v = a !== b && !c ? typeof d : await e',
|
||||
].join('\n')
|
||||
const map = await translateAll(ts, 'typescript', typescriptZhTranslator, source)
|
||||
expect(texts(map)).toEqual({
|
||||
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',
|
||||
})
|
||||
expect(map.get(1)?.segments.filter(segment => segment.role === 'keyword')).toEqual([
|
||||
{ text: '如果', role: 'keyword' },
|
||||
{ text: '则', role: 'keyword' },
|
||||
{ text: '否则', role: 'keyword' },
|
||||
])
|
||||
expectWordsApart(map)
|
||||
})
|
||||
})
|
||||
|
||||
describe('python 中文直译', () => {
|
||||
@@ -196,7 +219,7 @@ describe('python 中文直译', () => {
|
||||
36: 'failed: 字典[字符串, 字符串] = 空字典',
|
||||
37: 'label = \'env\'',
|
||||
39: '函数 summary(self, verbose: 布尔 = 假) -> 字符串',
|
||||
40: '返回 f\'{self.total} files\' 如果 verbose 不是 空 否则 \'\'',
|
||||
40: '返回 如果 verbose 不是 空 则 f\'{self.total} files\' 否则 \'\'',
|
||||
})
|
||||
expectWordsApart(map)
|
||||
})
|
||||
@@ -250,19 +273,39 @@ describe('python 中文直译', () => {
|
||||
expect(map[10]).toBe('未中断时')
|
||||
expect(map[16]).toBe('未出错时')
|
||||
})
|
||||
|
||||
it('条件表达式按「如果…则…否则…」重排,嵌套的、槽位里的关键字和运算符照样翻', async () => {
|
||||
const source = [
|
||||
'_default_port = 5680 if _role == "beat" else 5679',
|
||||
'y = a if x else b if y else c',
|
||||
'z = (a if x else b) if y else c',
|
||||
'v = [k for k in items if k not in seen] if not done and ok else None',
|
||||
].join('\n')
|
||||
const map = await translateAll(py, 'python', pythonZhTranslator, source)
|
||||
expect(texts(map)).toEqual({
|
||||
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] 否则 空',
|
||||
})
|
||||
expect(map.get(1)?.segments.filter(segment => segment.role === 'keyword' || segment.role === 'operator')).toEqual([
|
||||
{ text: '如果', role: 'keyword' },
|
||||
{ text: '等于', role: 'operator' },
|
||||
{ text: '则', role: 'keyword' },
|
||||
{ text: '否则', role: 'keyword' },
|
||||
])
|
||||
expectWordsApart(map)
|
||||
})
|
||||
})
|
||||
|
||||
describe('中文模板引擎', () => {
|
||||
it('关键字按父节点类型选译法,更长的节点类型链优先,都不匹配用默认', async () => {
|
||||
const translator = createZhTemplateTranslator({
|
||||
function testTranslator(table: Partial<ZhTable>): Translator {
|
||||
return createZhTemplateTranslator({
|
||||
id: 'test.zh-template',
|
||||
label: '测试',
|
||||
languageIds: ['python'],
|
||||
tokenRole: () => 'keyword',
|
||||
keywords: {
|
||||
in: { zh: '甲', byContext: { for_statement: '乙' } },
|
||||
else: { zh: '丙', byContext: { 'else_clause': '丁', 'for_statement > else_clause': '戊' } },
|
||||
},
|
||||
keywords: {},
|
||||
literals: {},
|
||||
builtinTypes: {},
|
||||
isTypePosition: () => false,
|
||||
@@ -271,6 +314,17 @@ describe('中文模板引擎', () => {
|
||||
emptyLiterals: {},
|
||||
stringNodes: [],
|
||||
commentText: node => node.text,
|
||||
nodeTemplates: {},
|
||||
...table,
|
||||
})
|
||||
}
|
||||
|
||||
it('关键字按父节点类型选译法,更长的节点类型链优先,都不匹配用默认', async () => {
|
||||
const translator = testTranslator({
|
||||
keywords: {
|
||||
in: { zh: '甲', byContext: { for_statement: '乙' } },
|
||||
else: { zh: '丙', byContext: { 'else_clause': '丁', 'for_statement > else_clause': '戊' } },
|
||||
},
|
||||
})
|
||||
const source = [
|
||||
'for k in items:',
|
||||
@@ -288,4 +342,54 @@ describe('中文模板引擎', () => {
|
||||
expect(map[5]).toBe('x = a if c 丙 b')
|
||||
expect(map[6]).toBe('戊')
|
||||
})
|
||||
|
||||
it('节点模板按节点类型重排:槽位按字段名或具名子节点序号取子节点并递归翻译,译词按关键字着色', async () => {
|
||||
const translator = testTranslator({
|
||||
tokenRole: () => 'variable',
|
||||
keywords: { in: '在', else: { zh: '丙', byContext: { else_clause: '丁' } } },
|
||||
operators: { '==': '等于' },
|
||||
operatorParents: ['comparison_operator'],
|
||||
nodeTemplates: {
|
||||
conditional_expression: ['若', { namedChild: 1 }, '则', { namedChild: 0 }, '不然', { namedChild: 2 }],
|
||||
assignment: ['令', { field: 'left' }, '类型', { field: 'type' }, '为', { field: 'right' }],
|
||||
},
|
||||
})
|
||||
const source = [
|
||||
'n: int = a if k in seen else b',
|
||||
'print(a if c else b if d else e)',
|
||||
'w = (a # 注释不占序号',
|
||||
' if k == 1 else b)',
|
||||
].join('\n')
|
||||
const map = await translateAll(py, 'python', translator, source)
|
||||
expect(texts(map)).toEqual({
|
||||
1: '令 n 类型 int 为 若 k 在 seen 则 a 不然 b',
|
||||
2: 'print(若 c 则 a 不然 若 d 则 b 不然 e)',
|
||||
3: 'w = (若 k 等于 1 则 a 不然 b)',
|
||||
})
|
||||
expect(map.get(1)?.segments.filter(segment => segment.role === 'keyword').map(segment => segment.text))
|
||||
.toEqual(['令', '类型', '为', '若', '则', '不然'])
|
||||
})
|
||||
|
||||
it('没命中模板的节点照抄源码结构;模板引用的子节点不存在时也不重排', async () => {
|
||||
const translator = testTranslator({
|
||||
keywords: { in: '在', else: { zh: '丙', byContext: { else_clause: '丁' } } },
|
||||
nodeTemplates: {
|
||||
assignment: ['令', { field: 'left' }, '类型', { field: 'type' }, '为', { field: 'right' }],
|
||||
},
|
||||
})
|
||||
const source = [
|
||||
'x = a if k in seen else b',
|
||||
'if k in seen:',
|
||||
' pass',
|
||||
'else:',
|
||||
' pass',
|
||||
].join('\n')
|
||||
expect(texts(await translateAll(py, 'python', translator, source))).toEqual({
|
||||
1: 'x = a if k 在 seen 丙 b',
|
||||
2: 'if k 在 seen',
|
||||
3: 'pass',
|
||||
4: '丁',
|
||||
5: 'pass',
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user