translomatic-cli 1.0.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.
Files changed (46) hide show
  1. package/README.md +216 -0
  2. package/dist/commands/analyze.d.ts +11 -0
  3. package/dist/commands/analyze.d.ts.map +1 -0
  4. package/dist/commands/analyze.js +108 -0
  5. package/dist/commands/analyze.js.map +1 -0
  6. package/dist/commands/init.d.ts +12 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +100 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/status.d.ts +10 -0
  11. package/dist/commands/status.d.ts.map +1 -0
  12. package/dist/commands/status.js +116 -0
  13. package/dist/commands/status.js.map +1 -0
  14. package/dist/commands/translate.d.ts +15 -0
  15. package/dist/commands/translate.d.ts.map +1 -0
  16. package/dist/commands/translate.js +58 -0
  17. package/dist/commands/translate.js.map +1 -0
  18. package/dist/config.d.ts +19 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +59 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +50 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/logger.d.ts +25 -0
  27. package/dist/logger.d.ts.map +1 -0
  28. package/dist/logger.js +122 -0
  29. package/dist/logger.js.map +1 -0
  30. package/dist/scanner/detector.d.ts +6 -0
  31. package/dist/scanner/detector.d.ts.map +1 -0
  32. package/dist/scanner/detector.js +236 -0
  33. package/dist/scanner/detector.js.map +1 -0
  34. package/dist/scanner/extractor.d.ts +43 -0
  35. package/dist/scanner/extractor.d.ts.map +1 -0
  36. package/dist/scanner/extractor.js +107 -0
  37. package/dist/scanner/extractor.js.map +1 -0
  38. package/dist/translator.d.ts +34 -0
  39. package/dist/translator.d.ts.map +1 -0
  40. package/dist/translator.js +138 -0
  41. package/dist/translator.js.map +1 -0
  42. package/dist/types.d.ts +92 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +46 -0
@@ -0,0 +1,43 @@
1
+ import type { FlatMap } from "../types.js";
2
+ /**
3
+ * Flatten a nested JSON object into dotted-key format.
4
+ *
5
+ * Example:
6
+ * { common: { save: "Save", buttons: { ok: "OK" } } }
7
+ * → { "common.save": "Save", "common.buttons.ok": "OK" }
8
+ */
9
+ export declare function flattenJson(obj: Record<string, unknown>, prefix?: string): FlatMap;
10
+ /**
11
+ * Unflatten a dotted-key map back into nested JSON.
12
+ *
13
+ * Example:
14
+ * { "common.save": "Save", "common.buttons.ok": "OK" }
15
+ * → { common: { save: "Save", buttons: { ok: "OK" } } }
16
+ */
17
+ export declare function unflattenJson(flat: FlatMap): Record<string, unknown>;
18
+ /**
19
+ * Read a JSON locale file and return flattened key-value pairs.
20
+ */
21
+ export declare function readLocaleFile(filePath: string): Promise<FlatMap>;
22
+ /**
23
+ * Write a flattened key-value map back to a JSON file.
24
+ * Preserves nested structure.
25
+ */
26
+ export declare function writeLocaleFile(filePath: string, flatData: FlatMap, nested?: boolean): Promise<void>;
27
+ /**
28
+ * Resolve locale file path from pattern.
29
+ *
30
+ * Patterns:
31
+ * "{lang}.json" → "locales/vi.json"
32
+ * "{lang}/{namespace}.json" → "locales/vi/common.json"
33
+ */
34
+ export declare function resolveLocalePath(localesDir: string, pattern: string, lang: string, namespace?: string): string;
35
+ /**
36
+ * Find missing keys: keys in source that are not in target.
37
+ */
38
+ export declare function findMissingKeys(source: FlatMap, target: FlatMap): FlatMap;
39
+ /**
40
+ * Merge translations into existing target, preserving existing entries.
41
+ */
42
+ export declare function mergeTranslations(existing: FlatMap, newTranslations: FlatMap): FlatMap;
43
+ //# sourceMappingURL=extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/scanner/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,SAAK,GACV,OAAO,CAmBT;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBpE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAavE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,OAAc,GACrB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAQzE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,EACjB,eAAe,EAAE,OAAO,GACvB,OAAO,CAET"}
@@ -0,0 +1,107 @@
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ /**
4
+ * Flatten a nested JSON object into dotted-key format.
5
+ *
6
+ * Example:
7
+ * { common: { save: "Save", buttons: { ok: "OK" } } }
8
+ * → { "common.save": "Save", "common.buttons.ok": "OK" }
9
+ */
10
+ export function flattenJson(obj, prefix = "") {
11
+ const result = {};
12
+ for (const [key, value] of Object.entries(obj)) {
13
+ const fullKey = prefix ? `${prefix}.${key}` : key;
14
+ if (typeof value === "string") {
15
+ result[fullKey] = value;
16
+ }
17
+ else if (typeof value === "object" &&
18
+ value !== null &&
19
+ !Array.isArray(value)) {
20
+ Object.assign(result, flattenJson(value, fullKey));
21
+ }
22
+ // Skip arrays, numbers, booleans, null
23
+ }
24
+ return result;
25
+ }
26
+ /**
27
+ * Unflatten a dotted-key map back into nested JSON.
28
+ *
29
+ * Example:
30
+ * { "common.save": "Save", "common.buttons.ok": "OK" }
31
+ * → { common: { save: "Save", buttons: { ok: "OK" } } }
32
+ */
33
+ export function unflattenJson(flat) {
34
+ const result = {};
35
+ for (const [dottedKey, value] of Object.entries(flat)) {
36
+ const parts = dottedKey.split(".");
37
+ let current = result;
38
+ for (let i = 0; i < parts.length - 1; i++) {
39
+ const part = parts[i];
40
+ if (!(part in current) || typeof current[part] !== "object") {
41
+ current[part] = {};
42
+ }
43
+ current = current[part];
44
+ }
45
+ current[parts[parts.length - 1]] = value;
46
+ }
47
+ return result;
48
+ }
49
+ /**
50
+ * Read a JSON locale file and return flattened key-value pairs.
51
+ */
52
+ export async function readLocaleFile(filePath) {
53
+ try {
54
+ const raw = await readFile(filePath, "utf-8");
55
+ const parsed = JSON.parse(raw);
56
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
57
+ return {};
58
+ }
59
+ return flattenJson(parsed);
60
+ }
61
+ catch {
62
+ return {};
63
+ }
64
+ }
65
+ /**
66
+ * Write a flattened key-value map back to a JSON file.
67
+ * Preserves nested structure.
68
+ */
69
+ export async function writeLocaleFile(filePath, flatData, nested = true) {
70
+ // Ensure directory exists
71
+ await mkdir(dirname(filePath), { recursive: true });
72
+ const data = nested ? unflattenJson(flatData) : flatData;
73
+ const content = JSON.stringify(data, null, 2) + "\n";
74
+ await writeFile(filePath, content, "utf-8");
75
+ }
76
+ /**
77
+ * Resolve locale file path from pattern.
78
+ *
79
+ * Patterns:
80
+ * "{lang}.json" → "locales/vi.json"
81
+ * "{lang}/{namespace}.json" → "locales/vi/common.json"
82
+ */
83
+ export function resolveLocalePath(localesDir, pattern, lang, namespace) {
84
+ let path = pattern
85
+ .replace("{lang}", lang)
86
+ .replace("{namespace}", namespace || "translation");
87
+ return join(localesDir, path);
88
+ }
89
+ /**
90
+ * Find missing keys: keys in source that are not in target.
91
+ */
92
+ export function findMissingKeys(source, target) {
93
+ const missing = {};
94
+ for (const [key, value] of Object.entries(source)) {
95
+ if (!(key in target) || target[key] === "") {
96
+ missing[key] = value;
97
+ }
98
+ }
99
+ return missing;
100
+ }
101
+ /**
102
+ * Merge translations into existing target, preserving existing entries.
103
+ */
104
+ export function mergeTranslations(existing, newTranslations) {
105
+ return { ...existing, ...newTranslations };
106
+ }
107
+ //# sourceMappingURL=extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/scanner/extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { CLIConfig } from "./types.js";
2
+ import { Logger } from "./logger.js";
3
+ export interface TranslateOptions {
4
+ /** Force re-translate all strings (ignore existing) */
5
+ force?: boolean;
6
+ /** Only translate specific languages */
7
+ langs?: string[];
8
+ /** Only translate specific namespaces */
9
+ namespaces?: string[];
10
+ /** Dry run - show what would be translated without doing it */
11
+ dryRun?: boolean;
12
+ }
13
+ export interface TranslateResult {
14
+ totalLangs: number;
15
+ totalTranslated: number;
16
+ totalSkipped: number;
17
+ totalFailed: number;
18
+ totalTimeMs: number;
19
+ langResults: LangResult[];
20
+ }
21
+ interface LangResult {
22
+ lang: string;
23
+ translated: number;
24
+ skipped: number;
25
+ failed: number;
26
+ timeMs: number;
27
+ }
28
+ /**
29
+ * Main translation orchestrator.
30
+ * Reads source locale files, translates missing strings, writes target files.
31
+ */
32
+ export declare function translateProject(projectDir: string, config: CLIConfig, options: TranslateOptions, logger: Logger): Promise<TranslateResult>;
33
+ export {};
34
+ //# sourceMappingURL=translator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../src/translator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAgC,MAAM,YAAY,CAAC;AAS1E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC,CAqK1B"}
@@ -0,0 +1,138 @@
1
+ import { Translomatic } from "translomatic";
2
+ import { resolveApiKey } from "./config.js";
3
+ import { readLocaleFile, writeLocaleFile, resolveLocalePath, findMissingKeys, mergeTranslations, } from "./scanner/extractor.js";
4
+ const BATCH_SIZE = 50; // Max batch size for Translomatic API
5
+ /**
6
+ * Main translation orchestrator.
7
+ * Reads source locale files, translates missing strings, writes target files.
8
+ */
9
+ export async function translateProject(projectDir, config, options, logger) {
10
+ let client = null;
11
+ if (!options.dryRun) {
12
+ const apiKey = resolveApiKey(config);
13
+ client = new Translomatic({
14
+ apiKey,
15
+ defaultModel: config.model || undefined,
16
+ defaultContext: config.context || undefined,
17
+ });
18
+ }
19
+ const targetLangs = options.langs || config.targetLangs;
20
+ if (targetLangs.length === 0) {
21
+ throw new Error("No target languages configured. Update .translomaticrc.json targetLangs.");
22
+ }
23
+ const namespaces = options.namespaces || config.namespaces || [undefined];
24
+ const hasNamespaces = config.filePattern.includes("{namespace}");
25
+ const overallStart = Date.now();
26
+ const langResults = [];
27
+ let totalTranslated = 0;
28
+ let totalSkipped = 0;
29
+ let totalFailed = 0;
30
+ for (const lang of targetLangs) {
31
+ if (lang === config.sourceLang)
32
+ continue;
33
+ const langStart = Date.now();
34
+ let langTranslated = 0;
35
+ let langSkipped = 0;
36
+ let langFailed = 0;
37
+ // Count total strings for this language
38
+ let totalStringsForLang = 0;
39
+ const nsFiles = [];
40
+ const nsToProcess = hasNamespaces ? namespaces.filter(Boolean) : [undefined];
41
+ for (const ns of nsToProcess) {
42
+ const sourcePath = resolveLocalePath(`${projectDir}/${config.localesDir}`, config.filePattern, config.sourceLang, ns);
43
+ const targetPath = resolveLocalePath(`${projectDir}/${config.localesDir}`, config.filePattern, lang, ns);
44
+ const sourceMap = await readLocaleFile(sourcePath);
45
+ totalStringsForLang += Object.keys(sourceMap).length;
46
+ nsFiles.push({ ns, sourcePath, targetPath });
47
+ }
48
+ logger.langStart(lang, totalStringsForLang);
49
+ for (const { ns, sourcePath, targetPath } of nsFiles) {
50
+ const sourceMap = await readLocaleFile(sourcePath);
51
+ if (Object.keys(sourceMap).length === 0) {
52
+ logger.debug(`Skipping empty source: ${sourcePath}`);
53
+ continue;
54
+ }
55
+ const existingTarget = options.force ? {} : await readLocaleFile(targetPath);
56
+ const missing = options.force ? sourceMap : findMissingKeys(sourceMap, existingTarget);
57
+ const skipped = Object.keys(sourceMap).length - Object.keys(missing).length;
58
+ langSkipped += skipped;
59
+ if (Object.keys(missing).length === 0) {
60
+ logger.debug(`${ns ? `[${ns}] ` : ""}All strings already translated`);
61
+ continue;
62
+ }
63
+ if (options.dryRun) {
64
+ logger.info(`${ns ? `[${ns}] ` : ""}Would translate ${Object.keys(missing).length} strings → ${lang}`);
65
+ langTranslated += Object.keys(missing).length;
66
+ continue;
67
+ }
68
+ // Translate in batches
69
+ const keys = Object.keys(missing);
70
+ const values = Object.values(missing);
71
+ const totalBatches = Math.ceil(values.length / BATCH_SIZE);
72
+ const translatedMap = {};
73
+ for (let i = 0; i < values.length; i += BATCH_SIZE) {
74
+ const batchKeys = keys.slice(i, i + BATCH_SIZE);
75
+ const batchValues = values.slice(i, i + BATCH_SIZE);
76
+ const batchNum = Math.floor(i / BATCH_SIZE) + 1;
77
+ const progress = {
78
+ lang,
79
+ namespace: ns,
80
+ translated: langTranslated + Object.keys(translatedMap).length,
81
+ total: totalStringsForLang,
82
+ skipped: langSkipped,
83
+ failed: langFailed,
84
+ currentBatch: batchNum,
85
+ totalBatches,
86
+ };
87
+ logger.batchProgress(progress);
88
+ try {
89
+ const glossary = config.glossary?.[lang] || undefined;
90
+ const result = await client.translateBatch({
91
+ texts: batchValues,
92
+ targetLang: lang,
93
+ sourceLang: config.sourceLang,
94
+ glossary,
95
+ });
96
+ for (let j = 0; j < batchKeys.length; j++) {
97
+ translatedMap[batchKeys[j]] = result.translations[j];
98
+ }
99
+ }
100
+ catch (err) {
101
+ langFailed += batchKeys.length;
102
+ logger.clearLine();
103
+ logger.error(`${ns ? `[${ns}] ` : ""}Batch ${batchNum} failed: ${err.message}`);
104
+ }
105
+ }
106
+ logger.clearLine();
107
+ // Merge and write
108
+ if (Object.keys(translatedMap).length > 0) {
109
+ const merged = mergeTranslations(existingTarget, translatedMap);
110
+ const isNested = config.fileFormat === "nested-json";
111
+ await writeLocaleFile(targetPath, merged, isNested);
112
+ langTranslated += Object.keys(translatedMap).length;
113
+ }
114
+ }
115
+ const langTimeMs = Date.now() - langStart;
116
+ logger.langComplete(lang, langTranslated, langSkipped, langFailed, langTimeMs);
117
+ langResults.push({
118
+ lang,
119
+ translated: langTranslated,
120
+ skipped: langSkipped,
121
+ failed: langFailed,
122
+ timeMs: langTimeMs,
123
+ });
124
+ totalTranslated += langTranslated;
125
+ totalSkipped += langSkipped;
126
+ totalFailed += langFailed;
127
+ }
128
+ const totalTimeMs = Date.now() - overallStart;
129
+ return {
130
+ totalLangs: langResults.length,
131
+ totalTranslated,
132
+ totalSkipped,
133
+ totalFailed,
134
+ totalTimeMs,
135
+ langResults,
136
+ };
137
+ }
138
+ //# sourceMappingURL=translator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translator.js","sourceRoot":"","sources":["../src/translator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * CLI configuration file (.translomaticrc.json)
3
+ */
4
+ export interface CLIConfig {
5
+ /** API key (or use env TRANSLOMATIC_API_KEY) */
6
+ apiKey?: string;
7
+ /** Source language code (default: "en") */
8
+ sourceLang: string;
9
+ /** Target language codes to translate to */
10
+ targetLangs: string[];
11
+ /** Path to locales directory (relative to project root) */
12
+ localesDir: string;
13
+ /** File format: "json" or "nested-json" */
14
+ fileFormat: "json" | "nested-json";
15
+ /** i18n framework detected (informational) */
16
+ framework?: string;
17
+ /** File naming pattern: "{lang}.json" or "{lang}/{namespace}.json" */
18
+ filePattern: string;
19
+ /** Namespaces (for multi-file setups like react-i18next) */
20
+ namespaces?: string[];
21
+ /** Translation model to use */
22
+ model?: string;
23
+ /** Context description for AI translation */
24
+ context?: string;
25
+ /** Per-language glossaries: { "vi": { "Free": "Miễn phí" } } */
26
+ glossary?: Record<string, Record<string, string>>;
27
+ }
28
+ /**
29
+ * Detected i18n framework information
30
+ */
31
+ export interface FrameworkInfo {
32
+ /** Framework name */
33
+ name: string;
34
+ /** Display name */
35
+ displayName: string;
36
+ /** Detected locales directory */
37
+ localesDir: string;
38
+ /** File pattern */
39
+ filePattern: string;
40
+ /** Source language detected */
41
+ sourceLang: string;
42
+ /** Existing languages found */
43
+ existingLangs: string[];
44
+ /** Namespaces found (if applicable) */
45
+ namespaces?: string[];
46
+ /** Confidence score (0-1) */
47
+ confidence: number;
48
+ }
49
+ /**
50
+ * Translation progress event
51
+ */
52
+ export interface TranslationProgress {
53
+ /** Current language being translated */
54
+ lang: string;
55
+ /** Current namespace (if applicable) */
56
+ namespace?: string;
57
+ /** Number of strings translated so far */
58
+ translated: number;
59
+ /** Total strings to translate */
60
+ total: number;
61
+ /** Number of strings skipped (already translated) */
62
+ skipped: number;
63
+ /** Number of strings that failed */
64
+ failed: number;
65
+ /** Current batch being processed */
66
+ currentBatch: number;
67
+ /** Total batches */
68
+ totalBatches: number;
69
+ }
70
+ /**
71
+ * Translation status for a language
72
+ */
73
+ export interface LangStatus {
74
+ lang: string;
75
+ totalKeys: number;
76
+ translatedKeys: number;
77
+ missingKeys: number;
78
+ coverage: number;
79
+ namespaces?: NamespaceStatus[];
80
+ }
81
+ export interface NamespaceStatus {
82
+ namespace: string;
83
+ totalKeys: number;
84
+ translatedKeys: number;
85
+ missingKeys: number;
86
+ coverage: number;
87
+ }
88
+ /**
89
+ * Flat key-value map (dotted keys for nested JSON)
90
+ */
91
+ export type FlatMap = Record<string, string>;
92
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "translomatic-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to auto-scan and translate entire projects using Translomatic API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "translomatic": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "translation",
21
+ "i18n",
22
+ "internationalization",
23
+ "cli",
24
+ "translomatic",
25
+ "translate",
26
+ "localization",
27
+ "l10n",
28
+ "react-i18next",
29
+ "vue-i18n",
30
+ "next-intl"
31
+ ],
32
+ "author": "Translomatic",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18.17.0"
36
+ },
37
+ "dependencies": {
38
+ "commander": "^12.1.0",
39
+ "chalk": "^5.3.0",
40
+ "translomatic": "^1.2.0"
41
+ },
42
+ "devDependencies": {
43
+ "typescript": "^5.3.0",
44
+ "@types/node": "^20.0.0"
45
+ }
46
+ }