@lokascript/i18n 2.3.0 → 2.4.0
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.
- package/dist/browser.cjs +626 -473
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +25 -3
- package/dist/browser.d.ts +25 -3
- package/dist/browser.js +623 -474
- package/dist/browser.js.map +1 -1
- package/dist/dictionaries/index.cjs +489 -472
- package/dist/dictionaries/index.cjs.map +1 -1
- package/dist/dictionaries/index.d.cts +5 -3
- package/dist/dictionaries/index.d.ts +5 -3
- package/dist/dictionaries/index.js +489 -473
- package/dist/dictionaries/index.js.map +1 -1
- package/dist/index.cjs +617 -473
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -37
- package/dist/index.d.ts +37 -37
- package/dist/index.js +617 -473
- package/dist/index.js.map +1 -1
- package/dist/lokascript-i18n.min.js +1 -1
- package/dist/lokascript-i18n.min.js.map +1 -1
- package/dist/lokascript-i18n.mjs +730 -478
- package/dist/lokascript-i18n.mjs.map +1 -1
- package/dist/plugins/vite.cjs +488 -472
- package/dist/plugins/vite.cjs.map +1 -1
- package/dist/plugins/vite.js +488 -472
- package/dist/plugins/vite.js.map +1 -1
- package/dist/plugins/webpack.cjs +488 -472
- package/dist/plugins/webpack.cjs.map +1 -1
- package/dist/plugins/webpack.js +488 -472
- package/dist/plugins/webpack.js.map +1 -1
- package/dist/{transformer-B0zy-kti.d.ts → transformer-BLv389qz.d.ts} +12 -47
- package/dist/{transformer-CzY_vHU0.d.cts → transformer-PFsYELrc.d.cts} +12 -47
- package/dist/{types-BYtpqGq3.d.cts → types-BcALAO6h.d.cts} +1 -1
- package/dist/{types-BYtpqGq3.d.ts → types-BcALAO6h.d.ts} +1 -1
- package/package.json +5 -5
- package/src/browser.ts +3 -0
- package/src/constants.ts +9 -0
- package/src/dictionaries/ar.ts +22 -43
- package/src/dictionaries/bn.ts +17 -0
- package/src/dictionaries/de.ts +19 -40
- package/src/dictionaries/en.ts +26 -0
- package/src/dictionaries/es.ts +19 -40
- package/src/dictionaries/fr.ts +19 -40
- package/src/dictionaries/he.ts +2 -0
- package/src/dictionaries/hi.ts +18 -48
- package/src/dictionaries/id.ts +22 -43
- package/src/dictionaries/index.ts +5 -1
- package/src/dictionaries/it.ts +18 -39
- package/src/dictionaries/ja.ts +18 -39
- package/src/dictionaries/ko.ts +22 -43
- package/src/dictionaries/ms.ts +16 -50
- package/src/dictionaries/pl.ts +18 -45
- package/src/dictionaries/pt.ts +23 -48
- package/src/dictionaries/qu.ts +22 -43
- package/src/dictionaries/ru.ts +18 -48
- package/src/dictionaries/sw.ts +22 -43
- package/src/dictionaries/th.ts +17 -0
- package/src/dictionaries/tl.ts +16 -50
- package/src/dictionaries/tr.ts +24 -45
- package/src/dictionaries/uk.ts +18 -48
- package/src/dictionaries/vi.ts +17 -0
- package/src/dictionaries/zh.ts +22 -43
- package/src/grammar/grammar.test.ts +143 -24
- package/src/grammar/index.ts +1 -0
- package/src/grammar/profiles/index.ts +31 -0
- package/src/grammar/transformer.ts +161 -0
- package/src/parser/he.ts +29 -0
- package/src/parser/index.ts +1 -0
- package/src/schema-alignment.test.ts +154 -0
- package/src/test-utils/complete-languages.ts +40 -0
- package/src/translation-validation.test.ts +1 -26
package/src/dictionaries/uk.ts
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Translations for Ukrainian language support.
|
|
5
|
-
* Uses infinitive verbs (common in software UIs).
|
|
6
|
-
*/
|
|
1
|
+
// Generated/merged from semantic profiles — hand-written entries are preserved
|
|
2
|
+
// To add derived entries, update the semantic profile and run: npm run generate:language-assets
|
|
7
3
|
|
|
8
4
|
import { Dictionary } from '../types';
|
|
9
5
|
|
|
10
6
|
export const ukrainianDictionary: Dictionary = {
|
|
11
7
|
commands: {
|
|
12
|
-
// Event handling
|
|
13
8
|
on: 'при',
|
|
14
9
|
tell: 'сказати',
|
|
15
10
|
trigger: 'викликати',
|
|
16
11
|
send: 'надіслати',
|
|
17
|
-
|
|
18
|
-
// DOM manipulation
|
|
19
12
|
take: 'взяти',
|
|
20
13
|
put: 'покласти',
|
|
21
14
|
set: 'встановити',
|
|
@@ -25,8 +18,6 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
25
18
|
toggle: 'перемкнути',
|
|
26
19
|
hide: 'сховати',
|
|
27
20
|
show: 'показати',
|
|
28
|
-
|
|
29
|
-
// Control flow
|
|
30
21
|
if: 'якщо',
|
|
31
22
|
unless: 'якщо_не',
|
|
32
23
|
repeat: 'повторити',
|
|
@@ -36,65 +27,46 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
36
27
|
continue: 'продовжити',
|
|
37
28
|
break: 'перервати',
|
|
38
29
|
halt: 'зупинити',
|
|
39
|
-
|
|
40
|
-
// Async
|
|
41
30
|
wait: 'чекати',
|
|
42
31
|
fetch: 'завантажити',
|
|
43
32
|
call: 'викликати',
|
|
44
33
|
return: 'повернути',
|
|
45
|
-
|
|
46
|
-
// Other commands
|
|
47
34
|
make: 'створити',
|
|
48
35
|
log: 'записати',
|
|
49
36
|
throw: 'кинути',
|
|
50
37
|
catch: 'зловити',
|
|
51
38
|
measure: 'виміряти',
|
|
52
39
|
transition: 'анімувати',
|
|
53
|
-
|
|
54
|
-
// Data Commands
|
|
55
40
|
increment: 'збільшити',
|
|
56
41
|
decrement: 'зменшити',
|
|
57
42
|
default: 'за_замовчуванням',
|
|
58
|
-
|
|
59
|
-
// Navigation Commands
|
|
60
43
|
go: 'перейти',
|
|
61
44
|
pushUrl: 'додати_url',
|
|
62
45
|
replaceUrl: 'замінити_url',
|
|
63
|
-
|
|
64
|
-
// Utility Commands
|
|
65
46
|
copy: 'копіювати',
|
|
66
47
|
pick: 'вибрати',
|
|
67
48
|
beep: 'звук',
|
|
68
|
-
|
|
69
|
-
// Advanced Commands
|
|
70
49
|
js: 'js',
|
|
71
50
|
async: 'асинхронно',
|
|
72
51
|
render: 'відобразити',
|
|
73
|
-
|
|
74
|
-
// Animation Commands
|
|
75
52
|
swap: 'поміняти',
|
|
76
53
|
morph: 'трансформувати',
|
|
77
54
|
settle: 'стабілізувати',
|
|
78
|
-
|
|
79
|
-
// Content Commands
|
|
80
55
|
append: 'додати_в_кінець',
|
|
81
|
-
|
|
82
|
-
// Control Flow
|
|
83
56
|
exit: 'вийти',
|
|
84
57
|
else: 'інакше',
|
|
85
|
-
|
|
86
|
-
// Behaviors
|
|
87
58
|
install: 'встановити',
|
|
88
59
|
init: 'ініціалізувати',
|
|
89
60
|
behavior: 'поведінка',
|
|
90
|
-
|
|
91
|
-
// Focus
|
|
92
61
|
focus: 'сфокусувати',
|
|
93
62
|
blur: 'розфокусувати',
|
|
94
|
-
|
|
95
|
-
// Clone
|
|
96
63
|
clone: 'клонувати',
|
|
97
64
|
prepend: 'додати_на_початок',
|
|
65
|
+
breakpoint: 'точка-зупинки',
|
|
66
|
+
clear: 'очистити',
|
|
67
|
+
close: 'закрити',
|
|
68
|
+
open: 'відкрити',
|
|
69
|
+
select: 'вибрати',
|
|
98
70
|
},
|
|
99
71
|
|
|
100
72
|
modifiers: {
|
|
@@ -127,23 +99,19 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
127
99
|
mouseover: 'наведення',
|
|
128
100
|
mouseout: 'відведення',
|
|
129
101
|
mousemove: 'рух_миші',
|
|
130
|
-
|
|
131
102
|
keydown: 'клавіша_вниз',
|
|
132
103
|
keyup: 'клавіша_вгору',
|
|
133
104
|
keypress: 'натискання_клавіші',
|
|
134
|
-
|
|
135
105
|
focus: 'фокус',
|
|
136
106
|
blur: 'розфокус',
|
|
137
107
|
change: 'зміна',
|
|
138
108
|
input: 'введення',
|
|
139
109
|
submit: 'надсилання',
|
|
140
110
|
reset: 'скидання',
|
|
141
|
-
|
|
142
111
|
load: 'завантаження',
|
|
143
112
|
unload: 'вивантаження',
|
|
144
113
|
resize: 'зміна_розміру',
|
|
145
114
|
scroll: 'прокрутка',
|
|
146
|
-
|
|
147
115
|
touchstart: 'дотик_початок',
|
|
148
116
|
touchend: 'дотик_кінець',
|
|
149
117
|
touchmove: 'дотик_рух',
|
|
@@ -166,8 +134,10 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
166
134
|
else: 'інакше',
|
|
167
135
|
otherwise: 'в_іншому_випадку',
|
|
168
136
|
end: 'кінець',
|
|
169
|
-
|
|
170
|
-
|
|
137
|
+
live: 'живий',
|
|
138
|
+
changes: 'змінюється',
|
|
139
|
+
has: 'має',
|
|
140
|
+
have: 'маю',
|
|
171
141
|
isNot: 'не_є',
|
|
172
142
|
empty: 'порожній',
|
|
173
143
|
},
|
|
@@ -241,7 +211,6 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
241
211
|
},
|
|
242
212
|
|
|
243
213
|
expressions: {
|
|
244
|
-
// Positional
|
|
245
214
|
first: 'перший',
|
|
246
215
|
last: 'останній',
|
|
247
216
|
next: 'наступний',
|
|
@@ -249,20 +218,21 @@ export const ukrainianDictionary: Dictionary = {
|
|
|
249
218
|
prev: 'попер',
|
|
250
219
|
at: 'в',
|
|
251
220
|
random: 'випадковий',
|
|
252
|
-
|
|
253
|
-
// DOM Traversal
|
|
254
221
|
closest: 'найближчий',
|
|
255
222
|
parent: 'батьківський',
|
|
256
223
|
children: 'діти',
|
|
257
224
|
within: 'всередині',
|
|
258
|
-
|
|
259
|
-
// Emptiness/Existence
|
|
260
225
|
no: 'ні',
|
|
261
226
|
empty: 'порожній',
|
|
262
227
|
some: 'деякі',
|
|
263
|
-
|
|
264
|
-
// String operations
|
|
265
228
|
'starts with': 'починається_з',
|
|
266
229
|
'ends with': 'закінчується_на',
|
|
230
|
+
'ignoring case': 'без_урахування_регістру',
|
|
231
|
+
'sorted by': 'відсортовано_за',
|
|
232
|
+
'mapped to': 'перетворено_на',
|
|
233
|
+
'split by': 'розділено_за',
|
|
234
|
+
'joined by': "об'єднано_через",
|
|
267
235
|
},
|
|
268
236
|
};
|
|
237
|
+
|
|
238
|
+
export const uk = ukrainianDictionary;
|
package/src/dictionaries/vi.ts
CHANGED
|
@@ -62,6 +62,11 @@ export const vi: Dictionary = {
|
|
|
62
62
|
clone: 'sao chép',
|
|
63
63
|
prepend: 'thêm đầu',
|
|
64
64
|
else: 'không thì',
|
|
65
|
+
breakpoint: 'điểm-dừng',
|
|
66
|
+
clear: 'xóa',
|
|
67
|
+
close: 'đóng',
|
|
68
|
+
open: 'mở',
|
|
69
|
+
select: 'chọn',
|
|
65
70
|
},
|
|
66
71
|
|
|
67
72
|
modifiers: {
|
|
@@ -79,6 +84,7 @@ export const vi: Dictionary = {
|
|
|
79
84
|
in: 'trong',
|
|
80
85
|
at: 'tại',
|
|
81
86
|
of: 'của',
|
|
87
|
+
between: 'giữa',
|
|
82
88
|
the: '',
|
|
83
89
|
times: 'lần',
|
|
84
90
|
},
|
|
@@ -98,6 +104,7 @@ export const vi: Dictionary = {
|
|
|
98
104
|
mouseleave: 'chuột rời',
|
|
99
105
|
focus: 'tập trung',
|
|
100
106
|
blur: 'mất tập trung',
|
|
107
|
+
reset: 'đặt-lại',
|
|
101
108
|
load: 'tải',
|
|
102
109
|
scroll: 'cuộn',
|
|
103
110
|
resize: 'đổi kích thước',
|
|
@@ -123,6 +130,8 @@ export const vi: Dictionary = {
|
|
|
123
130
|
else: 'không thì',
|
|
124
131
|
end: 'kết thúc',
|
|
125
132
|
then: 'rồi',
|
|
133
|
+
live: 'trực tiếp',
|
|
134
|
+
changes: 'thay đổi',
|
|
126
135
|
},
|
|
127
136
|
|
|
128
137
|
temporal: {
|
|
@@ -191,5 +200,13 @@ export const vi: Dictionary = {
|
|
|
191
200
|
parent: 'cha',
|
|
192
201
|
children: 'con',
|
|
193
202
|
closest: 'gần nhất',
|
|
203
|
+
empty: 'làm-rỗng',
|
|
204
|
+
'starts with': 'bắt đầu bằng',
|
|
205
|
+
'ends with': 'kết thúc bằng',
|
|
206
|
+
'ignoring case': 'không phân biệt hoa thường',
|
|
207
|
+
'sorted by': 'sắp xếp theo',
|
|
208
|
+
'mapped to': 'ánh xạ thành',
|
|
209
|
+
'split by': 'tách bởi',
|
|
210
|
+
'joined by': 'nối bởi',
|
|
194
211
|
},
|
|
195
212
|
};
|
package/src/dictionaries/zh.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Generated/merged from semantic profiles — hand-written entries are preserved
|
|
2
|
+
// To add derived entries, update the semantic profile and run: npm run generate:language-assets
|
|
2
3
|
|
|
3
4
|
import { Dictionary } from '../types';
|
|
4
5
|
|
|
5
6
|
export const zh: Dictionary = {
|
|
6
7
|
commands: {
|
|
7
|
-
// Event handling
|
|
8
8
|
on: '当',
|
|
9
9
|
tell: '告诉',
|
|
10
10
|
trigger: '触发',
|
|
11
11
|
send: '发送',
|
|
12
|
-
|
|
13
|
-
// DOM manipulation
|
|
14
12
|
take: '获取',
|
|
15
13
|
put: '放置',
|
|
16
14
|
set: '设置',
|
|
@@ -20,8 +18,6 @@ export const zh: Dictionary = {
|
|
|
20
18
|
toggle: '切换',
|
|
21
19
|
hide: '隐藏',
|
|
22
20
|
show: '显示',
|
|
23
|
-
|
|
24
|
-
// Control flow
|
|
25
21
|
if: '如果',
|
|
26
22
|
unless: '除非',
|
|
27
23
|
repeat: '重复',
|
|
@@ -31,54 +27,41 @@ export const zh: Dictionary = {
|
|
|
31
27
|
continue: '继续',
|
|
32
28
|
break: '中断',
|
|
33
29
|
halt: '停止',
|
|
34
|
-
|
|
35
|
-
// Async
|
|
36
30
|
wait: '等待',
|
|
37
31
|
fetch: '获取',
|
|
38
32
|
call: '调用',
|
|
39
33
|
return: '返回',
|
|
40
|
-
|
|
41
|
-
// Other commands
|
|
42
34
|
make: '制作',
|
|
43
35
|
log: '日志',
|
|
44
36
|
throw: '抛出',
|
|
45
37
|
catch: '捕获',
|
|
46
38
|
measure: '测量',
|
|
47
39
|
transition: '过渡',
|
|
48
|
-
|
|
49
|
-
// Data Commands
|
|
50
40
|
increment: '增加',
|
|
51
41
|
decrement: '减少',
|
|
52
42
|
default: '默认',
|
|
53
|
-
|
|
54
|
-
// Navigation Commands
|
|
55
43
|
go: '前往',
|
|
56
44
|
pushUrl: '推送网址',
|
|
57
45
|
replaceUrl: '替换网址',
|
|
58
|
-
|
|
59
|
-
// Utility Commands
|
|
60
46
|
copy: '复制',
|
|
61
47
|
pick: '选取',
|
|
62
48
|
beep: '蜂鸣',
|
|
63
|
-
|
|
64
|
-
// Advanced Commands
|
|
65
49
|
js: 'JS执行',
|
|
66
50
|
async: '异步',
|
|
67
51
|
render: '渲染',
|
|
68
|
-
|
|
69
|
-
// Animation Commands
|
|
70
52
|
swap: '交换',
|
|
71
53
|
morph: '变形',
|
|
72
54
|
settle: '稳定',
|
|
73
|
-
|
|
74
|
-
// Content Commands
|
|
75
55
|
append: '追加',
|
|
76
|
-
|
|
77
|
-
// Control Flow
|
|
78
56
|
exit: '退出',
|
|
79
|
-
|
|
80
|
-
// Behaviors
|
|
81
57
|
install: '安装',
|
|
58
|
+
breakpoint: '断点',
|
|
59
|
+
clear: '清除',
|
|
60
|
+
close: '关闭',
|
|
61
|
+
open: '打开',
|
|
62
|
+
select: '选择',
|
|
63
|
+
clone: '克隆',
|
|
64
|
+
prepend: '前置',
|
|
82
65
|
},
|
|
83
66
|
|
|
84
67
|
modifiers: {
|
|
@@ -110,23 +93,19 @@ export const zh: Dictionary = {
|
|
|
110
93
|
mouseover: '鼠标悬停',
|
|
111
94
|
mouseout: '鼠标移出',
|
|
112
95
|
mousemove: '鼠标移动',
|
|
113
|
-
|
|
114
96
|
keydown: '按键按下',
|
|
115
97
|
keyup: '按键抬起',
|
|
116
98
|
keypress: '按键',
|
|
117
|
-
|
|
118
99
|
focus: '聚焦',
|
|
119
100
|
blur: '失焦',
|
|
120
101
|
change: '改变',
|
|
121
102
|
input: '输入',
|
|
122
103
|
submit: '提交',
|
|
123
104
|
reset: '重置',
|
|
124
|
-
|
|
125
105
|
load: '加载',
|
|
126
106
|
unload: '卸载',
|
|
127
107
|
resize: '调整大小',
|
|
128
108
|
scroll: '滚动',
|
|
129
|
-
|
|
130
109
|
touchstart: '触摸开始',
|
|
131
110
|
touchend: '触摸结束',
|
|
132
111
|
touchmove: '触摸移动',
|
|
@@ -145,12 +124,14 @@ export const zh: Dictionary = {
|
|
|
145
124
|
contains: '包含',
|
|
146
125
|
includes: '包括',
|
|
147
126
|
equals: '等于',
|
|
148
|
-
has: '有',
|
|
149
|
-
have: '有',
|
|
127
|
+
has: '有',
|
|
128
|
+
have: '有',
|
|
150
129
|
then: '那么',
|
|
151
130
|
else: '否则',
|
|
152
131
|
otherwise: '要不然',
|
|
153
132
|
end: '结束',
|
|
133
|
+
live: '实时',
|
|
134
|
+
changes: '改变时',
|
|
154
135
|
},
|
|
155
136
|
|
|
156
137
|
temporal: {
|
|
@@ -174,13 +155,13 @@ export const zh: Dictionary = {
|
|
|
174
155
|
null: '空',
|
|
175
156
|
undefined: '未定义',
|
|
176
157
|
it: '它',
|
|
177
|
-
its: '它的',
|
|
158
|
+
its: '它的',
|
|
178
159
|
me: '我',
|
|
179
160
|
my: '我的',
|
|
180
161
|
myself: '我自己',
|
|
181
|
-
you: '你',
|
|
182
|
-
your: '你的',
|
|
183
|
-
yourself: '你自己',
|
|
162
|
+
you: '你',
|
|
163
|
+
your: '你的',
|
|
164
|
+
yourself: '你自己',
|
|
184
165
|
element: '元素',
|
|
185
166
|
target: '目标',
|
|
186
167
|
detail: '详情',
|
|
@@ -204,7 +185,6 @@ export const zh: Dictionary = {
|
|
|
204
185
|
},
|
|
205
186
|
|
|
206
187
|
expressions: {
|
|
207
|
-
// Positional
|
|
208
188
|
first: '第一个',
|
|
209
189
|
last: '最后一个',
|
|
210
190
|
next: '下一个',
|
|
@@ -212,20 +192,19 @@ export const zh: Dictionary = {
|
|
|
212
192
|
prev: '上个',
|
|
213
193
|
at: '在',
|
|
214
194
|
random: '随机',
|
|
215
|
-
|
|
216
|
-
// DOM Traversal
|
|
217
195
|
closest: '最近的',
|
|
218
196
|
parent: '父级',
|
|
219
197
|
children: '子级',
|
|
220
198
|
within: '之内',
|
|
221
|
-
|
|
222
|
-
// Emptiness/Existence
|
|
223
199
|
no: '没有',
|
|
224
200
|
empty: '空的',
|
|
225
201
|
some: '一些',
|
|
226
|
-
|
|
227
|
-
// String operations
|
|
228
202
|
'starts with': '以开头',
|
|
229
203
|
'ends with': '以结尾',
|
|
204
|
+
'ignoring case': '忽略大小写',
|
|
205
|
+
'sorted by': '按_排序',
|
|
206
|
+
'mapped to': '映射为',
|
|
207
|
+
'split by': '按_分割',
|
|
208
|
+
'joined by': '用_连接',
|
|
230
209
|
},
|
|
231
210
|
};
|
|
@@ -40,30 +40,37 @@ import type { ParsedElement, SemanticRole } from './types';
|
|
|
40
40
|
describe('Language Profiles', () => {
|
|
41
41
|
it('should have profiles for all supported locales', () => {
|
|
42
42
|
const locales = getSupportedLocales();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
43
|
+
// Explicit expected list — when adding a profile, append it here so the
|
|
44
|
+
// count assertion stays meaningful without becoming a magic number.
|
|
45
|
+
const expectedLocales = [
|
|
46
|
+
'en',
|
|
47
|
+
'ja',
|
|
48
|
+
'ko',
|
|
49
|
+
'zh',
|
|
50
|
+
'ar',
|
|
51
|
+
'tr',
|
|
52
|
+
'es',
|
|
53
|
+
'de',
|
|
54
|
+
'fr',
|
|
55
|
+
'pt',
|
|
56
|
+
'id',
|
|
57
|
+
'qu',
|
|
58
|
+
'sw',
|
|
59
|
+
'bn',
|
|
60
|
+
'it',
|
|
61
|
+
'ru',
|
|
62
|
+
'uk',
|
|
63
|
+
'vi',
|
|
64
|
+
'hi',
|
|
65
|
+
'tl',
|
|
66
|
+
'th',
|
|
67
|
+
'pl',
|
|
68
|
+
'he',
|
|
69
|
+
];
|
|
70
|
+
for (const code of expectedLocales) {
|
|
71
|
+
expect(locales, `missing profile: ${code}`).toContain(code);
|
|
72
|
+
}
|
|
73
|
+
expect(locales.length).toBe(expectedLocales.length);
|
|
67
74
|
});
|
|
68
75
|
|
|
69
76
|
it('should return undefined for unknown locales', () => {
|
|
@@ -1149,4 +1156,116 @@ describe('Possessive Dot Notation Translation', () => {
|
|
|
1149
1156
|
expect(result).not.toMatch(/\bmy\b/);
|
|
1150
1157
|
});
|
|
1151
1158
|
});
|
|
1159
|
+
|
|
1160
|
+
// ──── Reactive `live` block scope ────
|
|
1161
|
+
// Before this fix, the splitter cut between `live` and the body's
|
|
1162
|
+
// first command keyword, then the join logic re-inserted the target
|
|
1163
|
+
// language's `then` keyword between them. E.g.
|
|
1164
|
+
// `live put X into me end` (en→de) became
|
|
1165
|
+
// `live dann setzen X zu ich ende` (spurious `dann`).
|
|
1166
|
+
// The fix tracks block depth from `live` to its matching `end` and
|
|
1167
|
+
// suppresses splits inside that scope.
|
|
1168
|
+
describe('live block does not spuriously inject "then"', () => {
|
|
1169
|
+
const cases: Array<[string, string, RegExp]> = [
|
|
1170
|
+
// [target lang, input, banned pattern]
|
|
1171
|
+
['de', 'live put $count into me end', /\bdann\b/i],
|
|
1172
|
+
['es', 'live put $count into me end', /\bentonces\b/i],
|
|
1173
|
+
['ja', 'live put $count into me end', /それから/],
|
|
1174
|
+
['tr', 'live put $count into me end', /\bsonra\b/i],
|
|
1175
|
+
['it', 'live put $count into me end', /\ballora\b/i],
|
|
1176
|
+
['ru', 'live put $count into me end', /\bзатем\b/i],
|
|
1177
|
+
['vi', 'live put $count into me end', /\brồi\b/i],
|
|
1178
|
+
];
|
|
1179
|
+
|
|
1180
|
+
for (const [target, input, banned] of cases) {
|
|
1181
|
+
it(`(${target}) does not inject "then" inside the body`, () => {
|
|
1182
|
+
const transformer = new GrammarTransformer('en', target);
|
|
1183
|
+
const result = transformer.transform(input);
|
|
1184
|
+
expect(result, `unexpected ${banned} in: ${result}`).not.toMatch(banned);
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
it('keeps live body as one unit — no `live then` artifact', () => {
|
|
1189
|
+
const transformer = new GrammarTransformer('en', 'en');
|
|
1190
|
+
const result = transformer.transform('live put $count into me end');
|
|
1191
|
+
expect(result).not.toMatch(/live\s+then\b/i);
|
|
1192
|
+
expect(result).toMatch(/^live\b/);
|
|
1193
|
+
expect(result).toMatch(/\bend$/);
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
it('still splits on explicit "then" OUTSIDE the live block', () => {
|
|
1197
|
+
// Legitimate `then`-based chains MUST still split.
|
|
1198
|
+
const transformer = new GrammarTransformer('en', 'de');
|
|
1199
|
+
const result = transformer.transform('live put $x into me end then toggle .active');
|
|
1200
|
+
// German "then" = "dann" — appears exactly once (between block + toggle).
|
|
1201
|
+
const occurrences = (result.match(/\bdann\b/gi) || []).length;
|
|
1202
|
+
expect(occurrences).toBe(1);
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
// ──── Block extraction for `when`, `unless`, and SOV `live` ────
|
|
1207
|
+
// Block-syntactic tokens are pulled out before parseStatement so
|
|
1208
|
+
// they don't end up as command verbs (`live` → action role) or get
|
|
1209
|
+
// swept into role values (`end` → destination tail). The body
|
|
1210
|
+
// recurses through the regular pipeline, which keeps SOV reorder
|
|
1211
|
+
// working *inside* the body without disturbing the block frame.
|
|
1212
|
+
describe('reactive blocks — when/unless/live block extraction', () => {
|
|
1213
|
+
it('`when X changes Y` does not truncate — body is translated (en→de)', () => {
|
|
1214
|
+
const t = new GrammarTransformer('en', 'de');
|
|
1215
|
+
const result = t.transform('when $count changes log $count');
|
|
1216
|
+
// Pre-fix output was just `wenn` (parseConditional returned only
|
|
1217
|
+
// the action role). Now we should see head + body translated.
|
|
1218
|
+
expect(result.length).toBeGreaterThan('wenn'.length + 5);
|
|
1219
|
+
expect(result).toMatch(/wenn/i);
|
|
1220
|
+
expect(result).toMatch(/protokolliere|log/i);
|
|
1221
|
+
expect(result).toMatch(/ändert|changes/i);
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
it('`unless X Y` does not truncate — body is translated (en→de)', () => {
|
|
1225
|
+
const t = new GrammarTransformer('en', 'de');
|
|
1226
|
+
const result = t.transform('unless $disabled add .ready to me');
|
|
1227
|
+
expect(result.length).toBeGreaterThan('wennnicht'.length + 5);
|
|
1228
|
+
expect(result).toMatch(/wennnicht|unless/i);
|
|
1229
|
+
expect(result).toMatch(/hinzufüg|add/i);
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
it('`live X end` keeps live at start and end at end (SOV: ja)', () => {
|
|
1233
|
+
const t = new GrammarTransformer('en', 'ja');
|
|
1234
|
+
const result = t.transform('live put $count into me end');
|
|
1235
|
+
// Block frame: head first, tail last. Pre-fix the SOV reorder
|
|
1236
|
+
// dragged `live` to the end as if it were the action verb.
|
|
1237
|
+
expect(result).toMatch(/^(live|ライブ)/);
|
|
1238
|
+
expect(result).toMatch(/(end|終わり)$/);
|
|
1239
|
+
// SOV reorder applies inside the body — destination marker に
|
|
1240
|
+
// should appear, indicating `me` was reordered with its postposition.
|
|
1241
|
+
expect(result).toMatch(/に/);
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
it('`live X end` keeps live at start and end at end (SOV: tr)', () => {
|
|
1245
|
+
const t = new GrammarTransformer('en', 'tr');
|
|
1246
|
+
const result = t.transform('live put $count into me end');
|
|
1247
|
+
expect(result).toMatch(/^(live|canlı)/i);
|
|
1248
|
+
expect(result).toMatch(/(end|son)$/i);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
it('regression: `if X then Y end` still works (no block extraction)', () => {
|
|
1252
|
+
// `if` is intentionally not a BLOCK_HEAD_KEYWORD — splitOnThen +
|
|
1253
|
+
// parseConditional already handle it. Verify we didn't regress.
|
|
1254
|
+
const t = new GrammarTransformer('en', 'de');
|
|
1255
|
+
const result = t.transform('if $x then increment $count end');
|
|
1256
|
+
expect(result).toMatch(/wenn|if/i);
|
|
1257
|
+
expect(result).toMatch(/dann|then/i);
|
|
1258
|
+
expect(result).toMatch(/erhöh|increment/i);
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
it('regression: `live X end then toggle .y` — exactly one "then" connector', () => {
|
|
1262
|
+
// Splitter must still recognize the `then` *outside* the live
|
|
1263
|
+
// block as a statement boundary, even now that we route blocks
|
|
1264
|
+
// around parseStatement.
|
|
1265
|
+
const t = new GrammarTransformer('en', 'de');
|
|
1266
|
+
const result = t.transform('live put $x into me end then toggle .active');
|
|
1267
|
+
const danns = (result.match(/\bdann\b/gi) || []).length;
|
|
1268
|
+
expect(danns).toBe(1);
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1152
1271
|
});
|
package/src/grammar/index.ts
CHANGED
|
@@ -826,6 +826,36 @@ export const polishProfile: LanguageProfile = {
|
|
|
826
826
|
],
|
|
827
827
|
};
|
|
828
828
|
|
|
829
|
+
// =============================================================================
|
|
830
|
+
// Hebrew (SVO, RTL, Prepositions)
|
|
831
|
+
// =============================================================================
|
|
832
|
+
|
|
833
|
+
export const hebrewProfile: LanguageProfile = {
|
|
834
|
+
code: 'he',
|
|
835
|
+
name: 'עברית',
|
|
836
|
+
|
|
837
|
+
wordOrder: 'SVO', // Modern Hebrew is predominantly SVO (Biblical Hebrew was VSO)
|
|
838
|
+
adpositionType: 'preposition',
|
|
839
|
+
morphology: 'fusional', // Root-and-pattern (Semitic), like Arabic
|
|
840
|
+
direction: 'rtl',
|
|
841
|
+
|
|
842
|
+
canonicalOrder: ['event', 'action', 'patient', 'destination'],
|
|
843
|
+
|
|
844
|
+
// Hebrew uses single-letter prefix prepositions (ב, ל, מ, כ) that attach to
|
|
845
|
+
// the following word. For tokenization we treat them as spaced forms.
|
|
846
|
+
markers: [
|
|
847
|
+
{ form: 'ב', role: 'event', position: 'preposition', required: true },
|
|
848
|
+
{ form: 'על', role: 'destination', position: 'preposition', required: false },
|
|
849
|
+
{ form: 'אל', role: 'destination', position: 'preposition', required: false },
|
|
850
|
+
{ form: 'ל', role: 'destination', position: 'preposition', required: false },
|
|
851
|
+
{ form: 'את', role: 'patient', position: 'preposition', required: false }, // Direct object marker
|
|
852
|
+
{ form: 'מ', role: 'source', position: 'preposition', required: false },
|
|
853
|
+
{ form: 'מן', role: 'source', position: 'preposition', required: false },
|
|
854
|
+
{ form: 'עם', role: 'style', position: 'preposition', required: false },
|
|
855
|
+
{ form: 'כ', role: 'method', position: 'preposition', required: false },
|
|
856
|
+
],
|
|
857
|
+
};
|
|
858
|
+
|
|
829
859
|
// =============================================================================
|
|
830
860
|
// Profile Registry
|
|
831
861
|
// =============================================================================
|
|
@@ -854,6 +884,7 @@ export const profiles: Record<string, LanguageProfile> = {
|
|
|
854
884
|
tl: tagalogProfile,
|
|
855
885
|
th: thaiProfile,
|
|
856
886
|
pl: polishProfile,
|
|
887
|
+
he: hebrewProfile,
|
|
857
888
|
};
|
|
858
889
|
|
|
859
890
|
export function getProfile(locale: string): LanguageProfile | undefined {
|