transduck 0.6.6 → 0.6.7
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/cli.js +1 -1
- package/dist/scanner.js +8 -8
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/scanner.ts +8 -8
- package/tests/scanner.test.ts +59 -0
package/dist/cli.js
CHANGED
|
@@ -544,7 +544,7 @@ export async function runStats(opts) {
|
|
|
544
544
|
}
|
|
545
545
|
// CLI entry point
|
|
546
546
|
const program = new Command();
|
|
547
|
-
program.name('transduck').description('AI-native translation tool').version('0.6.
|
|
547
|
+
program.name('transduck').description('AI-native translation tool').version('0.6.7');
|
|
548
548
|
program.command('init')
|
|
549
549
|
.description('Initialize a new transduck project')
|
|
550
550
|
.action(async () => {
|
package/dist/scanner.js
CHANGED
|
@@ -10,10 +10,10 @@ const AIT_KEYWORD_CTX = /ait\s*\(\s*(['"])(.*?)\1(?:\s*,\s*context\s*=\s*(['"])(
|
|
|
10
10
|
const AIT_POSITIONAL_CTX = /ait\s*\(\s*(['"])(.*?)\1(?:\s*,\s*(['"])(.*?)\3)?/g;
|
|
11
11
|
// ait_plural("one", "other") or aitPlural("one", "other")
|
|
12
12
|
const AIT_PLURAL = /ait(?:_p|P)lural\s*\(\s*(['"])(.*?)\1\s*,\s*(['"])(.*?)\3/g;
|
|
13
|
-
// {% ait "text" %} or {% ait "text" context="ctx" %}
|
|
14
|
-
const DJANGO_TAG = /\{%\s*ait\s+(['"])(.*?)\1(?:\s+context=(['"])(.*?)\3)?\s*%\}/g;
|
|
15
|
-
// {% ait_plural "one" "other" %} or {% ait_plural "one" "other" context="ctx" %}
|
|
16
|
-
const DJANGO_PLURAL_TAG = /\{%\s*ait_plural\s+(['"])(.*?)\1\s+(['"])(.*?)\3(?:\s+context=(['"])(.*?)\5)?\s*%\}/g;
|
|
13
|
+
// {% ait "text" %} or {% ait "text" context="ctx" %} or {% ait "text" as var %}
|
|
14
|
+
const DJANGO_TAG = /\{%\s*ait\s+(['"])(.*?)\1(?:\s+context=(['"])(.*?)\3)?(?:\s+as\s+\w+)?\s*%\}/g;
|
|
15
|
+
// {% ait_plural "one" "other" %} or {% ait_plural "one" "other" context="ctx" %} or with as var
|
|
16
|
+
const DJANGO_PLURAL_TAG = /\{%\s*ait_plural\s+(['"])(.*?)\1\s+(['"])(.*?)\3(?:\s+context=(['"])(.*?)\5)?(?:\s+as\s+\w+)?\s*%\}/g;
|
|
17
17
|
// t("text") or t("text", "ctx") — only matched in files with transduck/react import
|
|
18
18
|
const T_POSITIONAL = /(?<![a-zA-Z_.$])t\s*\(\s*(['"])(.*?)\1(?:\s*,\s*(['"])(.*?)\3)?/g;
|
|
19
19
|
// tPlural("one", "other") — only matched in files with transduck/react import
|
|
@@ -21,11 +21,11 @@ const T_PLURAL = /(?<![a-zA-Z_.$])tPlural\s*\(\s*(['"])(.*?)\1\s*,\s*(['"])(.*?)
|
|
|
21
21
|
const HAS_TRANSDUCK_REACT = /from\s+['"]transduck\/react['"]/;
|
|
22
22
|
const HAS_AIT_IDENTIFIER = /\bait\b/;
|
|
23
23
|
// File extensions that use JS-style positional context
|
|
24
|
-
const JS_EXTENSIONS = new Set(['.js', '.ts', '.tsx', '.jsx']);
|
|
25
|
-
// File extensions that may contain Django template tags
|
|
26
|
-
const TEMPLATE_EXTENSIONS = new Set(['.html', '.jinja', '.jinja2']);
|
|
24
|
+
const JS_EXTENSIONS = new Set(['.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.vue', '.svelte', '.mdx']);
|
|
25
|
+
// File extensions that may contain Django/Nunjucks template tags
|
|
26
|
+
const TEMPLATE_EXTENSIONS = new Set(['.html', '.jinja', '.jinja2', '.njk', '.vue', '.svelte']);
|
|
27
27
|
// Supported file extensions for scanning
|
|
28
|
-
const SCAN_EXTENSIONS = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.html', '.jinja', '.jinja2']);
|
|
28
|
+
const SCAN_EXTENSIONS = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.html', '.jinja', '.jinja2', '.njk', '.vue', '.svelte', '.mdx']);
|
|
29
29
|
// Directories to skip
|
|
30
30
|
const SKIP_DIRS = new Set(['node_modules', '.venv', 'venv', '__pycache__', '.git', 'dist', 'build', '.next', 'site-packages']);
|
|
31
31
|
function shouldSkipDir(dirname) {
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -661,7 +661,7 @@ export async function runStats(opts: StatsOptions): Promise<string> {
|
|
|
661
661
|
// CLI entry point
|
|
662
662
|
const program = new Command();
|
|
663
663
|
|
|
664
|
-
program.name('transduck').description('AI-native translation tool').version('0.6.
|
|
664
|
+
program.name('transduck').description('AI-native translation tool').version('0.6.7');
|
|
665
665
|
|
|
666
666
|
program.command('init')
|
|
667
667
|
.description('Initialize a new transduck project')
|
package/src/scanner.ts
CHANGED
|
@@ -28,11 +28,11 @@ const AIT_POSITIONAL_CTX = /ait\s*\(\s*(['"])(.*?)\1(?:\s*,\s*(['"])(.*?)\3)?/g;
|
|
|
28
28
|
// ait_plural("one", "other") or aitPlural("one", "other")
|
|
29
29
|
const AIT_PLURAL = /ait(?:_p|P)lural\s*\(\s*(['"])(.*?)\1\s*,\s*(['"])(.*?)\3/g;
|
|
30
30
|
|
|
31
|
-
// {% ait "text" %} or {% ait "text" context="ctx" %}
|
|
32
|
-
const DJANGO_TAG = /\{%\s*ait\s+(['"])(.*?)\1(?:\s+context=(['"])(.*?)\3)?\s*%\}/g;
|
|
31
|
+
// {% ait "text" %} or {% ait "text" context="ctx" %} or {% ait "text" as var %}
|
|
32
|
+
const DJANGO_TAG = /\{%\s*ait\s+(['"])(.*?)\1(?:\s+context=(['"])(.*?)\3)?(?:\s+as\s+\w+)?\s*%\}/g;
|
|
33
33
|
|
|
34
|
-
// {% ait_plural "one" "other" %} or {% ait_plural "one" "other" context="ctx" %}
|
|
35
|
-
const DJANGO_PLURAL_TAG = /\{%\s*ait_plural\s+(['"])(.*?)\1\s+(['"])(.*?)\3(?:\s+context=(['"])(.*?)\5)?\s*%\}/g;
|
|
34
|
+
// {% ait_plural "one" "other" %} or {% ait_plural "one" "other" context="ctx" %} or with as var
|
|
35
|
+
const DJANGO_PLURAL_TAG = /\{%\s*ait_plural\s+(['"])(.*?)\1\s+(['"])(.*?)\3(?:\s+context=(['"])(.*?)\5)?(?:\s+as\s+\w+)?\s*%\}/g;
|
|
36
36
|
|
|
37
37
|
// t("text") or t("text", "ctx") — only matched in files with transduck/react import
|
|
38
38
|
const T_POSITIONAL = /(?<![a-zA-Z_.$])t\s*\(\s*(['"])(.*?)\1(?:\s*,\s*(['"])(.*?)\3)?/g;
|
|
@@ -44,13 +44,13 @@ const HAS_TRANSDUCK_REACT = /from\s+['"]transduck\/react['"]/;
|
|
|
44
44
|
const HAS_AIT_IDENTIFIER = /\bait\b/;
|
|
45
45
|
|
|
46
46
|
// File extensions that use JS-style positional context
|
|
47
|
-
const JS_EXTENSIONS = new Set(['.js', '.ts', '.tsx', '.jsx']);
|
|
47
|
+
const JS_EXTENSIONS = new Set(['.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.vue', '.svelte', '.mdx']);
|
|
48
48
|
|
|
49
|
-
// File extensions that may contain Django template tags
|
|
50
|
-
const TEMPLATE_EXTENSIONS = new Set(['.html', '.jinja', '.jinja2']);
|
|
49
|
+
// File extensions that may contain Django/Nunjucks template tags
|
|
50
|
+
const TEMPLATE_EXTENSIONS = new Set(['.html', '.jinja', '.jinja2', '.njk', '.vue', '.svelte']);
|
|
51
51
|
|
|
52
52
|
// Supported file extensions for scanning
|
|
53
|
-
const SCAN_EXTENSIONS = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.html', '.jinja', '.jinja2']);
|
|
53
|
+
const SCAN_EXTENSIONS = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.html', '.jinja', '.jinja2', '.njk', '.vue', '.svelte', '.mdx']);
|
|
54
54
|
|
|
55
55
|
// Directories to skip
|
|
56
56
|
const SKIP_DIRS = new Set(['node_modules', '.venv', 'venv', '__pycache__', '.git', 'dist', 'build', '.next', 'site-packages']);
|
package/tests/scanner.test.ts
CHANGED
|
@@ -178,6 +178,26 @@ const headline = await t("Your property visits, organized.", "Landing page headl
|
|
|
178
178
|
expect(result[0].other).toBe('{count} long items description here');
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
+
it('extracts Django tag with as var', () => {
|
|
182
|
+
const result = extractStrings('{% ait "Welcome" as headline %}', 'test.html');
|
|
183
|
+
expect(result).toHaveLength(1);
|
|
184
|
+
expect(result[0].text).toBe('Welcome');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('extracts Django tag with context and as var', () => {
|
|
188
|
+
const result = extractStrings('{% ait "Book" context="Hotel booking" as btn %}', 'test.html');
|
|
189
|
+
expect(result).toHaveLength(1);
|
|
190
|
+
expect(result[0].text).toBe('Book');
|
|
191
|
+
expect(result[0].context).toBe('Hotel booking');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('extracts Django plural tag with as var', () => {
|
|
195
|
+
const result = extractStrings('{% ait_plural "{count} night" "{count} nights" as stay %}', 'test.html');
|
|
196
|
+
expect(result).toHaveLength(1);
|
|
197
|
+
expect(result[0].plural).toBe(true);
|
|
198
|
+
expect(result[0].one).toBe('{count} night');
|
|
199
|
+
});
|
|
200
|
+
|
|
181
201
|
it('extracts Django plural template tag', () => {
|
|
182
202
|
const result = extractStrings('{% ait_plural "{count} night" "{count} nights" %}', 'test.html');
|
|
183
203
|
expect(result).toHaveLength(1);
|
|
@@ -209,6 +229,45 @@ const headline = await t("Your property visits, organized.", "Landing page headl
|
|
|
209
229
|
expect(result).toHaveLength(0);
|
|
210
230
|
});
|
|
211
231
|
|
|
232
|
+
it('extracts from Vue SFC', () => {
|
|
233
|
+
const code = `<template>\n <h1>{% ait "Welcome" %}</h1>\n</template>\n<script setup>\nimport { ait } from 'transduck';\nconst msg = ait("Hello", "greeting");\n</script>`;
|
|
234
|
+
const result = extractStrings(code, 'test.vue');
|
|
235
|
+
const texts = result.filter(e => !e.plural).map(e => e.text);
|
|
236
|
+
expect(texts).toContain('Welcome');
|
|
237
|
+
expect(texts).toContain('Hello');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('extracts from Svelte', () => {
|
|
241
|
+
const code = `<script>\nimport { ait } from 'transduck';\n</script>\n<h1>{ait("Welcome")}</h1>`;
|
|
242
|
+
const result = extractStrings(code, 'test.svelte');
|
|
243
|
+
expect(result.some(e => e.text === 'Welcome')).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('extracts from .mjs', () => {
|
|
247
|
+
const result = extractStrings('import { ait } from "transduck";\nait("Hello");', 'test.mjs');
|
|
248
|
+
expect(result).toHaveLength(1);
|
|
249
|
+
expect(result[0].text).toBe('Hello');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('extracts from .cjs', () => {
|
|
253
|
+
const result = extractStrings('const { ait } = require("transduck");\nait("Hello");', 'test.cjs');
|
|
254
|
+
expect(result).toHaveLength(1);
|
|
255
|
+
expect(result[0].text).toBe('Hello');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('extracts from .mdx', () => {
|
|
259
|
+
const result = extractStrings('import { ait } from "transduck";\n\n# Title\n\n{ait("Welcome")}', 'test.mdx');
|
|
260
|
+
expect(result.some(e => e.text === 'Welcome')).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('extracts from .njk (Nunjucks)', () => {
|
|
264
|
+
const result = extractStrings('{% ait "Welcome" %}\n{% ait "Book" context="hotel" %}', 'test.njk');
|
|
265
|
+
expect(result).toHaveLength(2);
|
|
266
|
+
expect(result[0].text).toBe('Welcome');
|
|
267
|
+
expect(result[1].text).toBe('Book');
|
|
268
|
+
expect(result[1].context).toBe('hotel');
|
|
269
|
+
});
|
|
270
|
+
|
|
212
271
|
it('extracts multi-line single-quoted strings', () => {
|
|
213
272
|
const code = `ait(\n 'This is a long '\n 'paragraph'\n)`;
|
|
214
273
|
const result = extractStrings(code, 'test.py');
|