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.
- package/README.md +216 -0
- package/dist/commands/analyze.d.ts +11 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +108 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +100 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +116 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/translate.d.ts +15 -0
- package/dist/commands/translate.d.ts.map +1 -0
- package/dist/commands/translate.js +58 -0
- package/dist/commands/translate.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +25 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +122 -0
- package/dist/logger.js.map +1 -0
- package/dist/scanner/detector.d.ts +6 -0
- package/dist/scanner/detector.d.ts.map +1 -0
- package/dist/scanner/detector.js +236 -0
- package/dist/scanner/detector.js.map +1 -0
- package/dist/scanner/extractor.d.ts +43 -0
- package/dist/scanner/extractor.d.ts.map +1 -0
- package/dist/scanner/extractor.js +107 -0
- package/dist/scanner/extractor.js.map +1 -0
- package/dist/translator.d.ts +34 -0
- package/dist/translator.d.ts.map +1 -0
- package/dist/translator.js +138 -0
- package/dist/translator.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Translomatic CLI
|
|
2
|
+
|
|
3
|
+
Auto-scan and translate your entire project from the command line.
|
|
4
|
+
|
|
5
|
+
Works with **react-i18next**, **next-intl**, **vue-i18n**, **angular**, **svelte-i18n**, and any JSON-based i18n setup.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g translomatic-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Initialize — auto-detect your i18n setup
|
|
17
|
+
translomatic init
|
|
18
|
+
|
|
19
|
+
# 2. Set your API key
|
|
20
|
+
export TRANSLOMATIC_API_KEY=tr_live_YOUR_KEY
|
|
21
|
+
|
|
22
|
+
# 3. (Optional) Auto-analyze context for smarter translations
|
|
23
|
+
translomatic analyze
|
|
24
|
+
|
|
25
|
+
# 4. Translate all missing strings
|
|
26
|
+
translomatic translate
|
|
27
|
+
|
|
28
|
+
# 5. Check translation coverage
|
|
29
|
+
translomatic status
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
### `translomatic init`
|
|
35
|
+
|
|
36
|
+
Scans your project to auto-detect the i18n framework, locale directory, and existing languages. Creates a `.translomaticrc.json` config file.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
translomatic init # Scan and create config
|
|
40
|
+
translomatic init --force # Overwrite existing config
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Auto-detects:**
|
|
44
|
+
- Framework: react-i18next, next-intl, vue-i18n, angular, svelte-i18n
|
|
45
|
+
- Locale directory: `locales/`, `messages/`, `src/locales/`, etc.
|
|
46
|
+
- File pattern: `{lang}.json` or `{lang}/{namespace}.json`
|
|
47
|
+
- Source language and existing translations
|
|
48
|
+
|
|
49
|
+
### `translomatic translate`
|
|
50
|
+
|
|
51
|
+
Translates all missing strings in your project. Only translates new/changed strings by default — existing translations are preserved.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
translomatic translate # Translate all missing strings
|
|
55
|
+
translomatic translate --force # Re-translate everything
|
|
56
|
+
translomatic translate -l vi,ja # Only translate specific languages
|
|
57
|
+
translomatic translate -n common # Only translate specific namespaces
|
|
58
|
+
translomatic translate --dry-run # Preview without changes
|
|
59
|
+
translomatic translate -v # Verbose output
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Features:**
|
|
63
|
+
- Smart batching (50 strings per API call)
|
|
64
|
+
- Progress bar with real-time updates
|
|
65
|
+
- Preserves existing translations (use `--force` to re-translate)
|
|
66
|
+
- Context-aware translation using AI models
|
|
67
|
+
- Per-language glossary support
|
|
68
|
+
|
|
69
|
+
### `translomatic analyze`
|
|
70
|
+
|
|
71
|
+
Uses AI to analyze your source strings and auto-generate:
|
|
72
|
+
- **Context description**: What your app is about (e.g., "mobile app store")
|
|
73
|
+
- **Recommended model**: Best translation model for your domain
|
|
74
|
+
- **Per-language glossaries**: Term mappings for accurate translations
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
translomatic analyze # Analyze and update config
|
|
78
|
+
translomatic analyze -v # Verbose output
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This solves the common problem of ambiguous translations:
|
|
82
|
+
- "Free" → "Miễn phí" (not "Tự do") for an app store
|
|
83
|
+
- "Home" → "Trang chủ" (not "Nhà") for a website
|
|
84
|
+
|
|
85
|
+
### `translomatic status`
|
|
86
|
+
|
|
87
|
+
Shows translation coverage for all configured target languages.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
translomatic status # Show coverage table
|
|
91
|
+
translomatic status -v # Include namespace breakdown
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Example output:**
|
|
95
|
+
```
|
|
96
|
+
Translation Status
|
|
97
|
+
────────────────────────────────────────
|
|
98
|
+
ℹ Source strings: 150
|
|
99
|
+
|
|
100
|
+
Lang Coverage Translated Missing
|
|
101
|
+
─────────────────────────────────────────────
|
|
102
|
+
vi ████████░░ 80% 120/150 30
|
|
103
|
+
ja ██████████ 100% 150/150 0
|
|
104
|
+
ko ██████░░░░ 60% 90/150 60
|
|
105
|
+
fr ░░░░░░░░░░ 0% 0/150 150
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Config File
|
|
109
|
+
|
|
110
|
+
`.translomaticrc.json` — created by `translomatic init`, edit as needed:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"apiKey": "tr_live_YOUR_KEY",
|
|
115
|
+
"sourceLang": "en",
|
|
116
|
+
"targetLangs": ["vi", "ja", "ko", "fr", "es"],
|
|
117
|
+
"localesDir": "./locales",
|
|
118
|
+
"fileFormat": "nested-json",
|
|
119
|
+
"filePattern": "{lang}.json",
|
|
120
|
+
"framework": "react-i18next",
|
|
121
|
+
"namespaces": ["common", "home", "settings"],
|
|
122
|
+
"model": "transloai-pro",
|
|
123
|
+
"context": "This is a mobile app store website...",
|
|
124
|
+
"glossary": {
|
|
125
|
+
"vi": { "Free": "Miễn phí", "Home": "Trang chủ" },
|
|
126
|
+
"ja": { "Free": "無料", "Home": "ホーム" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
| Field | Description |
|
|
132
|
+
|-------|-------------|
|
|
133
|
+
| `apiKey` | API key (or use `TRANSLOMATIC_API_KEY` env var) |
|
|
134
|
+
| `sourceLang` | Source language code (default: `"en"`) |
|
|
135
|
+
| `targetLangs` | Languages to translate to |
|
|
136
|
+
| `localesDir` | Path to locale files directory |
|
|
137
|
+
| `fileFormat` | `"json"` (flat) or `"nested-json"` (nested keys) |
|
|
138
|
+
| `filePattern` | File naming: `"{lang}.json"` or `"{lang}/{namespace}.json"` |
|
|
139
|
+
| `framework` | Detected i18n framework (informational) |
|
|
140
|
+
| `namespaces` | Namespace list for multi-file setups |
|
|
141
|
+
| `model` | Translation model (`"transloai-pro"` for context-aware) |
|
|
142
|
+
| `context` | Domain description for AI translation |
|
|
143
|
+
| `glossary` | Per-language forced term mappings |
|
|
144
|
+
|
|
145
|
+
## Supported Frameworks
|
|
146
|
+
|
|
147
|
+
| Framework | Detection | Default Locale Dir |
|
|
148
|
+
|-----------|-----------|-------------------|
|
|
149
|
+
| react-i18next | `react-i18next` in deps | `locales/` or `public/locales/` |
|
|
150
|
+
| next-intl | `next-intl` in deps | `messages/` |
|
|
151
|
+
| vue-i18n | `vue-i18n` in deps | `locales/` or `src/locales/` |
|
|
152
|
+
| Angular i18n | `@angular/localize` in deps | `src/assets/i18n/` |
|
|
153
|
+
| svelte-i18n | `svelte-i18n` in deps | `src/locales/` |
|
|
154
|
+
| Generic | Any JSON locale files | Auto-detected |
|
|
155
|
+
|
|
156
|
+
## Supported File Patterns
|
|
157
|
+
|
|
158
|
+
### Single file per language
|
|
159
|
+
```
|
|
160
|
+
locales/
|
|
161
|
+
├── en.json ← source
|
|
162
|
+
├── vi.json ← auto-generated
|
|
163
|
+
├── ja.json ← auto-generated
|
|
164
|
+
└── ko.json ← auto-generated
|
|
165
|
+
```
|
|
166
|
+
Config: `"filePattern": "{lang}.json"`
|
|
167
|
+
|
|
168
|
+
### Namespace files per language
|
|
169
|
+
```
|
|
170
|
+
locales/
|
|
171
|
+
├── en/
|
|
172
|
+
│ ├── common.json
|
|
173
|
+
│ ├── home.json
|
|
174
|
+
│ └── settings.json
|
|
175
|
+
├── vi/
|
|
176
|
+
│ ├── common.json ← auto-generated
|
|
177
|
+
│ ├── home.json ← auto-generated
|
|
178
|
+
│ └── settings.json ← auto-generated
|
|
179
|
+
```
|
|
180
|
+
Config: `"filePattern": "{lang}/{namespace}.json"`
|
|
181
|
+
|
|
182
|
+
## Nested JSON Support
|
|
183
|
+
|
|
184
|
+
The CLI handles nested JSON structures automatically:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"common": {
|
|
189
|
+
"buttons": {
|
|
190
|
+
"save": "Save",
|
|
191
|
+
"cancel": "Cancel"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"home": {
|
|
195
|
+
"title": "Welcome",
|
|
196
|
+
"subtitle": "Get started today"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
All leaf strings are extracted, translated, and written back preserving the nested structure.
|
|
202
|
+
|
|
203
|
+
## Environment Variables
|
|
204
|
+
|
|
205
|
+
| Variable | Description |
|
|
206
|
+
|----------|-------------|
|
|
207
|
+
| `TRANSLOMATIC_API_KEY` | API key (alternative to config file) |
|
|
208
|
+
|
|
209
|
+
## Requirements
|
|
210
|
+
|
|
211
|
+
- Node.js 18.17 or later
|
|
212
|
+
- A [Translomatic](https://translomatic.com) API key
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `translomatic analyze` command
|
|
3
|
+
*
|
|
4
|
+
* Reads sample strings from the source language file,
|
|
5
|
+
* sends them to the AI for context analysis, and updates
|
|
6
|
+
* the config with auto-generated context + glossary.
|
|
7
|
+
*/
|
|
8
|
+
export declare function analyzeCommand(projectDir: string, options: {
|
|
9
|
+
verbose?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AASA;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,IAAI,CAAC,CA+Ff"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Translomatic } from "translomatic";
|
|
2
|
+
import { loadConfig, saveConfig, resolveApiKey } from "../config.js";
|
|
3
|
+
import { readLocaleFile, resolveLocalePath, } from "../scanner/extractor.js";
|
|
4
|
+
import { Logger } from "../logger.js";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* `translomatic analyze` command
|
|
8
|
+
*
|
|
9
|
+
* Reads sample strings from the source language file,
|
|
10
|
+
* sends them to the AI for context analysis, and updates
|
|
11
|
+
* the config with auto-generated context + glossary.
|
|
12
|
+
*/
|
|
13
|
+
export async function analyzeCommand(projectDir, options) {
|
|
14
|
+
const logger = new Logger(options.verbose);
|
|
15
|
+
logger.banner();
|
|
16
|
+
logger.section("Analyzing Project Context");
|
|
17
|
+
// Load config
|
|
18
|
+
const config = await loadConfig(projectDir);
|
|
19
|
+
const apiKey = resolveApiKey(config);
|
|
20
|
+
logger.info(`Source language: ${config.sourceLang}`);
|
|
21
|
+
logger.info(`Target languages: ${config.targetLangs.join(", ")}`);
|
|
22
|
+
if (config.targetLangs.length === 0) {
|
|
23
|
+
logger.error("No target languages configured. Edit .translomaticrc.json first.");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
// Collect sample strings from source language files
|
|
27
|
+
logger.info("Collecting sample strings...");
|
|
28
|
+
const sampleTexts = [];
|
|
29
|
+
const hasNamespaces = config.filePattern.includes("{namespace}");
|
|
30
|
+
const namespacesToRead = hasNamespaces
|
|
31
|
+
? config.namespaces || ["translation"]
|
|
32
|
+
: [undefined];
|
|
33
|
+
for (const ns of namespacesToRead) {
|
|
34
|
+
const sourcePath = resolveLocalePath(join(projectDir, config.localesDir), config.filePattern, config.sourceLang, ns);
|
|
35
|
+
const flat = await readLocaleFile(sourcePath);
|
|
36
|
+
const values = Object.values(flat);
|
|
37
|
+
sampleTexts.push(...values);
|
|
38
|
+
}
|
|
39
|
+
if (sampleTexts.length === 0) {
|
|
40
|
+
logger.error("No source strings found. Check your localesDir and filePattern settings.");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
// Take a representative sample (max 100 strings)
|
|
44
|
+
const sample = sampleTexts.length > 100
|
|
45
|
+
? pickRepresentativeSample(sampleTexts, 100)
|
|
46
|
+
: sampleTexts;
|
|
47
|
+
logger.info(`Found ${sampleTexts.length} strings, using ${sample.length} for analysis`);
|
|
48
|
+
// Call analyzeContext API
|
|
49
|
+
logger.info("Analyzing with AI...");
|
|
50
|
+
const client = new Translomatic({ apiKey });
|
|
51
|
+
try {
|
|
52
|
+
const analysis = await client.analyzeContext({
|
|
53
|
+
texts: sample,
|
|
54
|
+
targetLangs: config.targetLangs,
|
|
55
|
+
sourceLang: config.sourceLang,
|
|
56
|
+
});
|
|
57
|
+
logger.success(`Domain detected: ${analysis.domain}`);
|
|
58
|
+
logger.info(`Context: ${analysis.context.slice(0, 100)}${analysis.context.length > 100 ? "..." : ""}`);
|
|
59
|
+
logger.info(`Recommended model: ${analysis.recommendedModel}`);
|
|
60
|
+
// Build per-language glossary map
|
|
61
|
+
const glossary = {};
|
|
62
|
+
for (const g of analysis.glossaries) {
|
|
63
|
+
const termCount = Object.keys(g.glossary).length;
|
|
64
|
+
if (termCount > 0) {
|
|
65
|
+
glossary[g.lang] = g.glossary;
|
|
66
|
+
logger.info(`Glossary for ${g.lang}: ${termCount} terms`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Update config
|
|
70
|
+
config.model = analysis.recommendedModel;
|
|
71
|
+
config.context = analysis.context;
|
|
72
|
+
config.glossary = glossary;
|
|
73
|
+
const configPath = await saveConfig(projectDir, config);
|
|
74
|
+
logger.success(`Updated ${configPath}`);
|
|
75
|
+
logger.section("Analysis Complete");
|
|
76
|
+
logger.info("Your config has been updated with:");
|
|
77
|
+
logger.info(" • AI-detected context description");
|
|
78
|
+
logger.info(" • Recommended translation model");
|
|
79
|
+
logger.info(" • Per-language glossaries");
|
|
80
|
+
logger.info("");
|
|
81
|
+
logger.info("Run 'translomatic translate' to start translating!");
|
|
82
|
+
console.log();
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.error(`Analysis failed: ${err.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Pick a representative sample of strings.
|
|
91
|
+
* Prioritize diversity: short strings, medium strings, long strings.
|
|
92
|
+
*/
|
|
93
|
+
function pickRepresentativeSample(texts, count) {
|
|
94
|
+
// Remove duplicates
|
|
95
|
+
const unique = [...new Set(texts)];
|
|
96
|
+
if (unique.length <= count)
|
|
97
|
+
return unique;
|
|
98
|
+
// Sort by length and pick evenly
|
|
99
|
+
const sorted = unique.sort((a, b) => a.length - b.length);
|
|
100
|
+
const result = [];
|
|
101
|
+
const step = sorted.length / count;
|
|
102
|
+
for (let i = 0; i < count; i++) {
|
|
103
|
+
const index = Math.min(Math.floor(i * step), sorted.length - 1);
|
|
104
|
+
result.push(sorted[index]);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EACL,cAAc,EACd,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAAkB,EAClB,OAA8B;IAE9B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAE5C,cAAc;IACd,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAElE,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,gBAAgB,GAAG,aAAa;QACpC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,aAAa,CAAC;QACtC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhB,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,iBAAiB,CAClC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,UAAU,EACjB,EAAwB,CACzB,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,WAAW,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iDAAiD;IACjD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,GAAG,GAAG;QACrC,CAAC,CAAC,wBAAwB,CAAC,WAAW,EAAE,GAAG,CAAC;QAC5C,CAAC,CAAC,WAAW,CAAC;IAEhB,MAAM,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,mBAAmB,MAAM,CAAC,MAAM,eAAe,CAAC,CAAC;IAExF,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC;YAC3C,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,CAAC,oBAAoB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvG,MAAM,CAAC,IAAI,CAAC,sBAAsB,QAAQ,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAE/D,kCAAkC;QAClC,MAAM,QAAQ,GAA2C,EAAE,CAAC;QAC5D,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;YACjD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;gBAClB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,KAAK,SAAS,QAAQ,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC,gBAAgB,CAAC;QACzC,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAClC,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAE3B,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACxD,MAAM,CAAC,OAAO,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;QAExC,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,oBAAqB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,wBAAwB,CAAC,KAAe,EAAE,KAAa;IAC9D,oBAAoB;IACpB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnC,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,MAAM,CAAC;IAE1C,iCAAiC;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;IAEnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `translomatic init` command
|
|
3
|
+
*
|
|
4
|
+
* Scans the project directory to:
|
|
5
|
+
* 1. Detect i18n framework (react-i18next, vue-i18n, next-intl, etc.)
|
|
6
|
+
* 2. Find locale directory and existing language files
|
|
7
|
+
* 3. Create .translomaticrc.json config file
|
|
8
|
+
*/
|
|
9
|
+
export declare function initCommand(projectDir: string, options: {
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwGf"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { configExists, saveConfig } from "../config.js";
|
|
3
|
+
import { detectFramework } from "../scanner/detector.js";
|
|
4
|
+
import { readLocaleFile } from "../scanner/extractor.js";
|
|
5
|
+
import { Logger } from "../logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* `translomatic init` command
|
|
8
|
+
*
|
|
9
|
+
* Scans the project directory to:
|
|
10
|
+
* 1. Detect i18n framework (react-i18next, vue-i18n, next-intl, etc.)
|
|
11
|
+
* 2. Find locale directory and existing language files
|
|
12
|
+
* 3. Create .translomaticrc.json config file
|
|
13
|
+
*/
|
|
14
|
+
export async function initCommand(projectDir, options) {
|
|
15
|
+
const logger = new Logger();
|
|
16
|
+
logger.banner();
|
|
17
|
+
logger.section("Initializing Translomatic CLI");
|
|
18
|
+
// Check existing config
|
|
19
|
+
if (!options.force && (await configExists(projectDir))) {
|
|
20
|
+
logger.warn("Config file .translomaticrc.json already exists.");
|
|
21
|
+
logger.info("Use --force to overwrite.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Step 1: Detect i18n framework
|
|
25
|
+
logger.info("Scanning project structure...");
|
|
26
|
+
const framework = await detectFramework(projectDir);
|
|
27
|
+
if (!framework) {
|
|
28
|
+
logger.warn("Could not detect i18n framework or locale files.");
|
|
29
|
+
logger.info("Creating default config. Edit .translomaticrc.json manually.");
|
|
30
|
+
const defaultConfig = {
|
|
31
|
+
sourceLang: "en",
|
|
32
|
+
targetLangs: [],
|
|
33
|
+
localesDir: "./locales",
|
|
34
|
+
fileFormat: "nested-json",
|
|
35
|
+
filePattern: "{lang}.json",
|
|
36
|
+
};
|
|
37
|
+
const configPath = await saveConfig(projectDir, defaultConfig);
|
|
38
|
+
logger.success(`Created ${configPath}`);
|
|
39
|
+
logger.info("Edit the config file to set your API key and target languages.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Step 2: Show detected info
|
|
43
|
+
logger.success(`Detected framework: ${framework.displayName}`);
|
|
44
|
+
logger.info(`Locales directory: ${framework.localesDir}`);
|
|
45
|
+
logger.info(`File pattern: ${framework.filePattern}`);
|
|
46
|
+
logger.info(`Source language: ${framework.sourceLang}`);
|
|
47
|
+
logger.info(`Existing languages: ${framework.existingLangs.join(", ")}`);
|
|
48
|
+
if (framework.namespaces && framework.namespaces.length > 0) {
|
|
49
|
+
logger.info(`Namespaces: ${framework.namespaces.join(", ")}`);
|
|
50
|
+
}
|
|
51
|
+
// Step 3: Count source strings
|
|
52
|
+
const isNamespaced = framework.filePattern.includes("{namespace}");
|
|
53
|
+
let totalKeys = 0;
|
|
54
|
+
if (isNamespaced && framework.namespaces) {
|
|
55
|
+
for (const ns of framework.namespaces) {
|
|
56
|
+
const filePath = join(projectDir, framework.localesDir, framework.filePattern
|
|
57
|
+
.replace("{lang}", framework.sourceLang)
|
|
58
|
+
.replace("{namespace}", ns));
|
|
59
|
+
const keys = await readLocaleFile(filePath);
|
|
60
|
+
totalKeys += Object.keys(keys).length;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const filePath = join(projectDir, framework.localesDir, framework.filePattern.replace("{lang}", framework.sourceLang));
|
|
65
|
+
const keys = await readLocaleFile(filePath);
|
|
66
|
+
totalKeys = Object.keys(keys).length;
|
|
67
|
+
}
|
|
68
|
+
logger.info(`Source strings found: ${totalKeys}`);
|
|
69
|
+
// Step 4: Create config
|
|
70
|
+
const config = {
|
|
71
|
+
sourceLang: framework.sourceLang,
|
|
72
|
+
targetLangs: framework.existingLangs.filter((l) => l !== framework.sourceLang),
|
|
73
|
+
localesDir: `./${framework.localesDir}`,
|
|
74
|
+
fileFormat: "nested-json",
|
|
75
|
+
framework: framework.name,
|
|
76
|
+
filePattern: framework.filePattern,
|
|
77
|
+
namespaces: framework.namespaces,
|
|
78
|
+
model: "transloai-pro",
|
|
79
|
+
context: "",
|
|
80
|
+
glossary: {},
|
|
81
|
+
};
|
|
82
|
+
const configPath = await saveConfig(projectDir, config);
|
|
83
|
+
logger.success(`Created ${configPath}`);
|
|
84
|
+
// Step 5: Show next steps
|
|
85
|
+
logger.section("Next Steps");
|
|
86
|
+
logger.info("1. Set your API key:");
|
|
87
|
+
logger.info(" export TRANSLOMATIC_API_KEY=tr_live_YOUR_KEY");
|
|
88
|
+
logger.info(" (or add apiKey to .translomaticrc.json)");
|
|
89
|
+
logger.info("");
|
|
90
|
+
logger.info("2. Add target languages to .translomaticrc.json:");
|
|
91
|
+
logger.info(` "targetLangs": ["vi", "ja", "ko", "fr", "es"]`);
|
|
92
|
+
logger.info("");
|
|
93
|
+
logger.info("3. (Optional) Auto-analyze context for better translations:");
|
|
94
|
+
logger.info(" translomatic analyze");
|
|
95
|
+
logger.info("");
|
|
96
|
+
logger.info("4. Translate your project:");
|
|
97
|
+
logger.info(" translomatic translate");
|
|
98
|
+
console.log();
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `translomatic status` command
|
|
3
|
+
*
|
|
4
|
+
* Shows translation coverage for all configured target languages.
|
|
5
|
+
* Displays a table with coverage percentages and missing string counts.
|
|
6
|
+
*/
|
|
7
|
+
export declare function statusCommand(projectDir: string, options: {
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,IAAI,CAAC,CA2If"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { loadConfig } from "../config.js";
|
|
3
|
+
import { readLocaleFile, resolveLocalePath, findMissingKeys, } from "../scanner/extractor.js";
|
|
4
|
+
import { Logger } from "../logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* `translomatic status` command
|
|
7
|
+
*
|
|
8
|
+
* Shows translation coverage for all configured target languages.
|
|
9
|
+
* Displays a table with coverage percentages and missing string counts.
|
|
10
|
+
*/
|
|
11
|
+
export async function statusCommand(projectDir, options) {
|
|
12
|
+
const logger = new Logger(options.verbose);
|
|
13
|
+
logger.banner();
|
|
14
|
+
logger.section("Translation Status");
|
|
15
|
+
// Load config
|
|
16
|
+
const config = await loadConfig(projectDir);
|
|
17
|
+
logger.info(`Source language: ${config.sourceLang}`);
|
|
18
|
+
if (config.targetLangs.length === 0) {
|
|
19
|
+
logger.warn("No target languages configured.");
|
|
20
|
+
logger.info("Edit .translomaticrc.json to add targetLangs.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const hasNamespaces = config.filePattern.includes("{namespace}");
|
|
24
|
+
const namespacesToRead = hasNamespaces
|
|
25
|
+
? config.namespaces || ["translation"]
|
|
26
|
+
: [undefined];
|
|
27
|
+
// Read all source strings
|
|
28
|
+
const sourceStrings = {};
|
|
29
|
+
let totalSourceKeys = 0;
|
|
30
|
+
for (const ns of namespacesToRead) {
|
|
31
|
+
const sourcePath = resolveLocalePath(join(projectDir, config.localesDir), config.filePattern, config.sourceLang, ns);
|
|
32
|
+
const flat = await readLocaleFile(sourcePath);
|
|
33
|
+
const nsKey = ns || "_default";
|
|
34
|
+
sourceStrings[nsKey] = flat;
|
|
35
|
+
totalSourceKeys += Object.keys(flat).length;
|
|
36
|
+
}
|
|
37
|
+
logger.info(`Source strings: ${totalSourceKeys}`);
|
|
38
|
+
console.log();
|
|
39
|
+
// Check each target language
|
|
40
|
+
const statuses = [];
|
|
41
|
+
for (const lang of config.targetLangs) {
|
|
42
|
+
if (lang === config.sourceLang)
|
|
43
|
+
continue;
|
|
44
|
+
let langTotal = 0;
|
|
45
|
+
let langTranslated = 0;
|
|
46
|
+
const nsStatuses = [];
|
|
47
|
+
for (const ns of namespacesToRead) {
|
|
48
|
+
const nsKey = ns || "_default";
|
|
49
|
+
const source = sourceStrings[nsKey];
|
|
50
|
+
const sourceKeyCount = Object.keys(source).length;
|
|
51
|
+
const targetPath = resolveLocalePath(join(projectDir, config.localesDir), config.filePattern, lang, ns);
|
|
52
|
+
const target = await readLocaleFile(targetPath);
|
|
53
|
+
const missing = findMissingKeys(source, target);
|
|
54
|
+
const missingCount = Object.keys(missing).length;
|
|
55
|
+
const translatedCount = sourceKeyCount - missingCount;
|
|
56
|
+
langTotal += sourceKeyCount;
|
|
57
|
+
langTranslated += translatedCount;
|
|
58
|
+
if (hasNamespaces && ns) {
|
|
59
|
+
nsStatuses.push({
|
|
60
|
+
namespace: ns,
|
|
61
|
+
totalKeys: sourceKeyCount,
|
|
62
|
+
translatedKeys: translatedCount,
|
|
63
|
+
missingKeys: missingCount,
|
|
64
|
+
coverage: sourceKeyCount > 0
|
|
65
|
+
? Math.round((translatedCount / sourceKeyCount) * 100)
|
|
66
|
+
: 100,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
statuses.push({
|
|
71
|
+
lang,
|
|
72
|
+
totalKeys: langTotal,
|
|
73
|
+
translatedKeys: langTranslated,
|
|
74
|
+
missingKeys: langTotal - langTranslated,
|
|
75
|
+
coverage: langTotal > 0
|
|
76
|
+
? Math.round((langTranslated / langTotal) * 100)
|
|
77
|
+
: 100,
|
|
78
|
+
namespaces: nsStatuses.length > 0 ? nsStatuses : undefined,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Display table
|
|
82
|
+
logger.statusTable(statuses.map((s) => ({
|
|
83
|
+
lang: s.lang,
|
|
84
|
+
coverage: s.coverage,
|
|
85
|
+
translated: s.translatedKeys,
|
|
86
|
+
total: s.totalKeys,
|
|
87
|
+
})));
|
|
88
|
+
// Show namespace details if verbose
|
|
89
|
+
if (options.verbose && hasNamespaces) {
|
|
90
|
+
for (const status of statuses) {
|
|
91
|
+
if (status.namespaces && status.namespaces.length > 1) {
|
|
92
|
+
console.log();
|
|
93
|
+
logger.info(`${status.lang} namespace details:`);
|
|
94
|
+
for (const ns of status.namespaces) {
|
|
95
|
+
const indicator = ns.coverage === 100 ? "✓" : ns.coverage >= 80 ? "~" : "✗";
|
|
96
|
+
logger.debug(` ${indicator} ${ns.namespace}: ${ns.coverage}% (${ns.translatedKeys}/${ns.totalKeys})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Summary
|
|
102
|
+
const totalCoverage = statuses.length > 0
|
|
103
|
+
? Math.round(statuses.reduce((sum, s) => sum + s.coverage, 0) / statuses.length)
|
|
104
|
+
: 0;
|
|
105
|
+
const totalMissing = statuses.reduce((sum, s) => sum + s.missingKeys, 0);
|
|
106
|
+
console.log();
|
|
107
|
+
if (totalMissing === 0) {
|
|
108
|
+
logger.success("All languages are fully translated!");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
logger.info(`Overall coverage: ${totalCoverage}% — ${totalMissing} strings missing across ${statuses.length} languages`);
|
|
112
|
+
logger.info('Run "translomatic translate" to fill in missing translations.');
|
|
113
|
+
}
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB,EAClB,OAA8B;IAE9B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAErC,cAAc;IACd,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAErD,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,gBAAgB,GAAG,aAAa;QACpC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,aAAa,CAAC;QACtC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhB,0BAA0B;IAC1B,MAAM,aAAa,GAA2C,EAAE,CAAC;IACjE,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,iBAAiB,CAClC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,UAAU,EACjB,EAAwB,CACzB,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC;QAC/B,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC5B,eAAe,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,mBAAmB,eAAe,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,6BAA6B;IAC7B,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,MAAM,CAAC,UAAU;YAAE,SAAS;QAEzC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,MAAM,UAAU,GAAsB,EAAE,CAAC;QAEzC,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC;YAC/B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;YAElD,MAAM,UAAU,GAAG,iBAAiB,CAClC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,WAAW,EAClB,IAAI,EACJ,EAAwB,CACzB,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;YAChD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;YACjD,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,CAAC;YAEtD,SAAS,IAAI,cAAc,CAAC;YAC5B,cAAc,IAAI,eAAe,CAAC;YAElC,IAAI,aAAa,IAAI,EAAE,EAAE,CAAC;gBACxB,UAAU,CAAC,IAAI,CAAC;oBACd,SAAS,EAAE,EAAE;oBACb,SAAS,EAAE,cAAc;oBACzB,cAAc,EAAE,eAAe;oBAC/B,WAAW,EAAE,YAAY;oBACzB,QAAQ,EAAE,cAAc,GAAG,CAAC;wBAC1B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;wBACtD,CAAC,CAAC,GAAG;iBACR,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI;YACJ,SAAS,EAAE,SAAS;YACpB,cAAc,EAAE,cAAc;YAC9B,WAAW,EAAE,SAAS,GAAG,cAAc;YACvC,QAAQ,EAAE,SAAS,GAAG,CAAC;gBACrB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,cAAc,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC;gBAChD,CAAC,CAAC,GAAG;YACP,UAAU,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;SAC3D,CAAC,CAAC;IACL,CAAC;IAED,gBAAgB;IAChB,MAAM,CAAC,WAAW,CAChB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,CAAC,CAAC,cAAc;QAC5B,KAAK,EAAE,CAAC,CAAC,SAAS;KACnB,CAAC,CAAC,CACJ,CAAC;IAEF,oCAAoC;IACpC,IAAI,OAAO,CAAC,OAAO,IAAI,aAAa,EAAE,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtD,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,qBAAqB,CAAC,CAAC;gBACjD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;oBACnC,MAAM,SAAS,GACb,EAAE,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAC5D,MAAM,CAAC,KAAK,CACV,KAAK,SAAS,IAAI,EAAE,CAAC,SAAS,KAAK,EAAE,CAAC,QAAQ,MAAM,EAAE,CAAC,cAAc,IAAI,EAAE,CAAC,SAAS,GAAG,CACzF,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,UAAU;IACV,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC,KAAK,CACR,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CACnE;QACH,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAEzE,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CACT,qBAAqB,aAAa,OAAO,YAAY,2BAA2B,QAAQ,CAAC,MAAM,YAAY,CAC5G,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `translomatic translate` command
|
|
3
|
+
*
|
|
4
|
+
* Translates all missing strings in the project.
|
|
5
|
+
* Reads source language file, finds missing keys in each target language,
|
|
6
|
+
* and batch-translates them using the Translomatic API.
|
|
7
|
+
*/
|
|
8
|
+
export declare function translateCommand(projectDir: string, options: {
|
|
9
|
+
force?: boolean;
|
|
10
|
+
lang?: string;
|
|
11
|
+
namespace?: string;
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/commands/translate.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,OAAO,CAAC,IAAI,CAAC,CAyDf"}
|