@l10nmonster/cli 1.0.5 → 3.0.0-alpha.10
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/.releaserc.json +31 -0
- package/CHANGELOG.md +6 -0
- package/README.md +378 -60
- package/index.js +88 -0
- package/l10n.mjs +49 -0
- package/package.json +9 -22
- package/l10n.cjs +0 -88
- package/out/l10nCommands.cjs +0 -689
package/.releaserc.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": [
|
|
3
|
+
"main",
|
|
4
|
+
{
|
|
5
|
+
"name": "next",
|
|
6
|
+
"prerelease": "alpha"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "beta",
|
|
10
|
+
"prerelease": "beta"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"tagFormat": "@l10nmonster/cli@${version}",
|
|
14
|
+
"plugins": [
|
|
15
|
+
"@semantic-release/commit-analyzer",
|
|
16
|
+
"@semantic-release/release-notes-generator",
|
|
17
|
+
{
|
|
18
|
+
"path": "@semantic-release/changelog",
|
|
19
|
+
"changelogFile": "CHANGELOG.md"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "@semantic-release/npm",
|
|
23
|
+
"npmPublish": true
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"path": "@semantic-release/git",
|
|
27
|
+
"assets": ["CHANGELOG.md", "package.json"],
|
|
28
|
+
"message": "chore(release): @l10nmonster/cli@${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
|
@@ -1,103 +1,421 @@
|
|
|
1
|
-
|
|
1
|
+
# @l10nmonster/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Command-line interface for L10n Monster v3 - continuous localization for the rest of us.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From npm
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @l10nmonster/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From source
|
|
14
|
+
|
|
15
|
+
```bash
|
|
6
16
|
git clone git@github.com:l10nmonster/l10nmonster.git
|
|
7
17
|
cd l10nmonster
|
|
8
|
-
npm
|
|
9
|
-
npm
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
npm link --global @l10nmonster/cli
|
|
10
21
|
```
|
|
11
22
|
|
|
12
|
-
|
|
23
|
+
## Getting Started
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
Create a configuration file `l10nmonster.config.mjs` at your project root, then use the CLI commands to manage translations.
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
l10n push
|
|
18
|
-
```
|
|
19
|
-
It will re-read all your source content, figure out what needs translation, and send it to your translator.
|
|
27
|
+
### Basic Configuration
|
|
20
28
|
|
|
21
|
-
```
|
|
22
|
-
|
|
29
|
+
```javascript
|
|
30
|
+
// l10nmonster.config.mjs
|
|
31
|
+
import { FsSource, FsTarget } from '@l10nmonster/core';
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
channels: [{
|
|
35
|
+
source: new FsSource({ globs: ['src/**/*.json'] }),
|
|
36
|
+
target: new FsTarget({
|
|
37
|
+
targetPath: (lang, resourceId) => resourceId.replace('/en/', `/${lang}/`)
|
|
38
|
+
})
|
|
39
|
+
}],
|
|
40
|
+
|
|
41
|
+
providers: [{
|
|
42
|
+
id: 'internal',
|
|
43
|
+
provider: 'InternalLeverage'
|
|
44
|
+
}]
|
|
45
|
+
};
|
|
23
46
|
```
|
|
24
|
-
It will give you an overview of the state of translation of your project.
|
|
25
47
|
|
|
26
|
-
|
|
48
|
+
## v3 Commands
|
|
49
|
+
|
|
50
|
+
### Core Operations
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Analyze source content
|
|
27
54
|
l10n analyze
|
|
28
55
|
```
|
|
29
|
-
|
|
56
|
+
Analyzes your sources and reports insights like repeated content, quality metrics, and translation opportunities.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Capture source content snapshot
|
|
60
|
+
l10n source snap
|
|
61
|
+
```
|
|
62
|
+
Creates a snapshot of your source content for translation workflows.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Generate translations
|
|
66
|
+
l10n translate
|
|
67
|
+
```
|
|
68
|
+
Processes translation requests through configured providers and generates translated resources.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Update operations and target resources
|
|
72
|
+
l10n ops update
|
|
73
|
+
```
|
|
74
|
+
Updates target resources with latest translations and manages operation lifecycle.
|
|
75
|
+
|
|
76
|
+
### Translation Memory
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Synchronize TM with providers
|
|
80
|
+
l10n tm syncup
|
|
81
|
+
```
|
|
82
|
+
Uploads completed translations to translation memory stores.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Download translations from TM
|
|
86
|
+
l10n tm syncdown
|
|
87
|
+
```
|
|
88
|
+
Downloads latest translations from translation memory to local stores.
|
|
30
89
|
|
|
31
|
-
```
|
|
32
|
-
|
|
90
|
+
```bash
|
|
91
|
+
# Export TM data
|
|
92
|
+
l10n tm export
|
|
33
93
|
```
|
|
34
|
-
|
|
94
|
+
Exports translation memory data for backup or external processing.
|
|
35
95
|
|
|
36
|
-
|
|
37
|
-
|
|
96
|
+
### Source Management
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# List source content
|
|
100
|
+
l10n source list
|
|
101
|
+
```
|
|
102
|
+
Lists all detected source content with metadata and statistics.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Query specific content
|
|
106
|
+
l10n source query --filter "*.json"
|
|
107
|
+
```
|
|
108
|
+
Queries source content with filters and search criteria.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Show untranslated content
|
|
112
|
+
l10n source untranslated --target es
|
|
113
|
+
```
|
|
114
|
+
Shows untranslated content for specific target languages.
|
|
115
|
+
|
|
116
|
+
### Operations Management
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# View operation details
|
|
120
|
+
l10n ops view
|
|
121
|
+
```
|
|
122
|
+
Displays detailed information about current operations and their status.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Manage jobs
|
|
126
|
+
l10n ops jobs --status pending
|
|
127
|
+
```
|
|
128
|
+
Lists and manages translation jobs by status or other criteria.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Provider operations
|
|
132
|
+
l10n ops providers
|
|
38
133
|
```
|
|
39
|
-
|
|
134
|
+
Shows configured providers and their current status.
|
|
40
135
|
|
|
41
|
-
```
|
|
136
|
+
```bash
|
|
137
|
+
# Delete operations
|
|
138
|
+
l10n ops delete --job-id abc123
|
|
139
|
+
```
|
|
140
|
+
Removes specific operations or jobs from the system.
|
|
141
|
+
|
|
142
|
+
### Legacy Commands (v2 compatibility)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Legacy push operation
|
|
146
|
+
l10n push
|
|
147
|
+
```
|
|
148
|
+
**Deprecated**: Use `l10n translate` and `l10n ops update` instead.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Legacy pull operation
|
|
42
152
|
l10n pull
|
|
43
153
|
```
|
|
44
|
-
|
|
154
|
+
**Deprecated**: Use `l10n tm syncdown` and `l10n ops update` instead.
|
|
45
155
|
|
|
46
|
-
```
|
|
47
|
-
|
|
156
|
+
```bash
|
|
157
|
+
# Legacy status command
|
|
158
|
+
l10n status
|
|
159
|
+
```
|
|
160
|
+
**Deprecated**: Use `l10n analyze` for detailed insights.
|
|
161
|
+
|
|
162
|
+
## Working Files
|
|
163
|
+
|
|
164
|
+
L10n Monster v3 maintains its working files in a `l10nmonster/` directory at the root of the project:
|
|
165
|
+
|
|
166
|
+
- **TM stores**: `l10nmonster/tm/` - Translation memory data
|
|
167
|
+
- **Operations**: `l10nmonster/ops/` - Job and task management
|
|
168
|
+
- **Snapshots**: `l10nmonster/snap/` - Source content snapshots
|
|
169
|
+
- **Providers**: `l10nmonster/providers/` - Provider-specific data
|
|
170
|
+
|
|
171
|
+
Working files are source-control friendly (JSON/JSONL files with consistent formatting) and should be checked in for team collaboration.
|
|
172
|
+
|
|
173
|
+
## Advanced CLI Options
|
|
174
|
+
|
|
175
|
+
The CLI supports additional options to control behavior:
|
|
176
|
+
|
|
177
|
+
- `-c, --config <path>`: Specify custom configuration file path
|
|
178
|
+
- `-v, --verbose`: Output additional debug information
|
|
179
|
+
- `--dry-run`: Preview operations without making changes
|
|
180
|
+
- `--parallel <number>`: Set parallelism level for operations
|
|
181
|
+
- `--filter <pattern>`: Apply filters to operations
|
|
182
|
+
|
|
183
|
+
### Example Usage
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Run with custom config and high verbosity
|
|
187
|
+
l10n translate -c ./custom.config.mjs -v
|
|
188
|
+
|
|
189
|
+
# Preview operations without executing
|
|
190
|
+
l10n ops update --dry-run
|
|
191
|
+
|
|
192
|
+
# Parallel translation processing
|
|
193
|
+
l10n translate --parallel 4
|
|
194
|
+
|
|
195
|
+
# Filter specific content
|
|
196
|
+
l10n source snap --filter "components/**/*.json"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## v3 Configuration
|
|
200
|
+
|
|
201
|
+
### ESM Configuration Format
|
|
202
|
+
|
|
203
|
+
v3 uses ESM-based configuration files (`l10nmonster.config.mjs`):
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
import { FsSource, FsTarget } from '@l10nmonster/core';
|
|
207
|
+
import { GptAgent } from '@l10nmonster/helpers-openai';
|
|
208
|
+
|
|
209
|
+
export default {
|
|
210
|
+
// Source and target channels
|
|
211
|
+
channels: [{
|
|
212
|
+
source: new FsSource({
|
|
213
|
+
globs: ['src/**/*.json'],
|
|
214
|
+
targetLangs: ['es', 'fr', 'de']
|
|
215
|
+
}),
|
|
216
|
+
target: new FsTarget({
|
|
217
|
+
targetPath: (lang, id) => id.replace('/en/', `/${lang}/`)
|
|
218
|
+
})
|
|
219
|
+
}],
|
|
220
|
+
|
|
221
|
+
// Translation providers
|
|
222
|
+
providers: [{
|
|
223
|
+
id: 'ai-translator',
|
|
224
|
+
provider: new GptAgent({ model: 'gpt-4' })
|
|
225
|
+
}, {
|
|
226
|
+
id: 'internal',
|
|
227
|
+
provider: 'InternalLeverage'
|
|
228
|
+
}],
|
|
229
|
+
|
|
230
|
+
// Content type definitions
|
|
231
|
+
contentTypes: [{
|
|
232
|
+
name: 'json',
|
|
233
|
+
resourceFilter: 'i18next'
|
|
234
|
+
}],
|
|
235
|
+
|
|
236
|
+
// Storage configuration
|
|
237
|
+
stores: {
|
|
238
|
+
tm: 'BaseJsonlTmStore',
|
|
239
|
+
ops: 'FsOpsStore'
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Multi-Channel Configuration
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
export default {
|
|
248
|
+
channels: [
|
|
249
|
+
{
|
|
250
|
+
// Web app content
|
|
251
|
+
source: new FsSource({ globs: ['web/src/**/*.json'] }),
|
|
252
|
+
target: new FsTarget({ targetPath: (lang, id) => id.replace('/src/', `/dist/${lang}/`) })
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
// Mobile app content
|
|
256
|
+
source: new FsSource({ globs: ['mobile/strings/**/*.xml'] }),
|
|
257
|
+
target: new FsTarget({ targetPath: (lang, id) => id.replace('/strings/', `/strings-${lang}/`) })
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Provider Chains
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
export default {
|
|
267
|
+
providers: [
|
|
268
|
+
{ id: 'leverage', provider: 'InternalLeverage' },
|
|
269
|
+
{ id: 'repetitions', provider: 'Repetition' },
|
|
270
|
+
{ id: 'ai', provider: new GptAgent({ model: 'gpt-4' }) },
|
|
271
|
+
{ id: 'fallback', provider: 'Invisicode' }
|
|
272
|
+
]
|
|
273
|
+
};
|
|
48
274
|
```
|
|
49
|
-
It will generate translated files based on the latest sources and translations in the TM.
|
|
50
275
|
|
|
51
|
-
|
|
276
|
+
## Error Handling
|
|
277
|
+
|
|
278
|
+
The CLI provides comprehensive error handling with actionable messages:
|
|
52
279
|
|
|
53
|
-
|
|
280
|
+
```bash
|
|
281
|
+
# Configuration errors
|
|
282
|
+
Error: Configuration file not found: l10nmonster.config.mjs
|
|
283
|
+
Tip: Run 'l10n init' to create a basic configuration
|
|
54
284
|
|
|
55
|
-
|
|
285
|
+
# Provider errors
|
|
286
|
+
Error: OpenAI API key not configured
|
|
287
|
+
Tip: Set OPENAI_API_KEY environment variable or configure apiKey in provider options
|
|
56
288
|
|
|
57
|
-
|
|
289
|
+
# Operation errors
|
|
290
|
+
Error: Translation job failed for provider 'gpt-4'
|
|
291
|
+
Tip: Check provider configuration and API limits
|
|
292
|
+
```
|
|
58
293
|
|
|
59
|
-
##
|
|
294
|
+
## Performance Optimization
|
|
60
295
|
|
|
61
|
-
|
|
296
|
+
### Parallel Processing
|
|
62
297
|
|
|
63
|
-
|
|
298
|
+
```bash
|
|
299
|
+
# Enable parallel operations
|
|
300
|
+
l10n translate --parallel 4
|
|
64
301
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* `resourceFilter`: a filter to process the specific resource format
|
|
69
|
-
* `translationProvider`: a connector to the translation vendor
|
|
70
|
-
* `target`: a target adapter to write translated resources to
|
|
71
|
-
* `adapters`, `filters`, `translators`: built-in helpers (see below)
|
|
72
|
-
* TODO: add the other properties that can be defined
|
|
302
|
+
# Provider-specific parallelism
|
|
303
|
+
l10n ops update --provider-parallel 2
|
|
304
|
+
```
|
|
73
305
|
|
|
74
|
-
|
|
306
|
+
### Filtering and Batching
|
|
75
307
|
|
|
76
|
-
|
|
308
|
+
```bash
|
|
309
|
+
# Process specific file patterns
|
|
310
|
+
l10n translate --filter "*.json"
|
|
77
311
|
|
|
78
|
-
|
|
79
|
-
|
|
312
|
+
# Batch operations by language
|
|
313
|
+
l10n translate --batch-by-language
|
|
80
314
|
|
|
81
|
-
|
|
315
|
+
# Limit operation scope
|
|
316
|
+
l10n source snap --since "2024-01-01"
|
|
317
|
+
```
|
|
82
318
|
|
|
83
|
-
##
|
|
319
|
+
## Integration Examples
|
|
84
320
|
|
|
85
|
-
|
|
321
|
+
### CI/CD Pipeline
|
|
86
322
|
|
|
87
|
-
|
|
323
|
+
```yaml
|
|
324
|
+
# GitHub Actions example
|
|
325
|
+
name: Localization
|
|
326
|
+
on: [push, pull_request]
|
|
88
327
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
328
|
+
jobs:
|
|
329
|
+
l10n:
|
|
330
|
+
runs-on: ubuntu-latest
|
|
331
|
+
steps:
|
|
332
|
+
- uses: actions/checkout@v3
|
|
333
|
+
- uses: actions/setup-node@v3
|
|
334
|
+
with:
|
|
335
|
+
node-version: '20'
|
|
336
|
+
- run: npm install -g @l10nmonster/cli
|
|
337
|
+
- run: l10n analyze
|
|
338
|
+
- run: l10n translate --dry-run
|
|
339
|
+
```
|
|
92
340
|
|
|
93
|
-
###
|
|
341
|
+
### NPM Scripts
|
|
94
342
|
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"scripts": {
|
|
346
|
+
"l10n:analyze": "l10n analyze",
|
|
347
|
+
"l10n:translate": "l10n translate",
|
|
348
|
+
"l10n:update": "l10n ops update",
|
|
349
|
+
"l10n:sync": "l10n tm syncup && l10n tm syncdown"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
99
352
|
```
|
|
100
353
|
|
|
101
|
-
|
|
354
|
+
## Troubleshooting
|
|
355
|
+
|
|
356
|
+
### Common Issues
|
|
357
|
+
|
|
358
|
+
1. **Configuration not found**
|
|
359
|
+
```bash
|
|
360
|
+
# Ensure config file exists and has correct name
|
|
361
|
+
ls l10nmonster.config.mjs
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
2. **Module import errors**
|
|
365
|
+
```bash
|
|
366
|
+
# Verify Node.js version (requires >= 22.11.0)
|
|
367
|
+
node --version
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
3. **Provider authentication**
|
|
371
|
+
```bash
|
|
372
|
+
# Check environment variables
|
|
373
|
+
echo $OPENAI_API_KEY
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
4. **Performance issues**
|
|
377
|
+
```bash
|
|
378
|
+
# Enable debug logging
|
|
379
|
+
l10n translate -v
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Migration from v2
|
|
383
|
+
|
|
384
|
+
### Configuration Updates
|
|
385
|
+
|
|
386
|
+
1. **Rename config file**: `l10nmonster.cjs` → `l10nmonster.config.mjs`
|
|
387
|
+
2. **Update imports**: Use ESM import syntax
|
|
388
|
+
3. **Update providers**: Many providers have new names and APIs
|
|
389
|
+
4. **Update commands**: Some command names have changed
|
|
390
|
+
|
|
391
|
+
### Command Mapping
|
|
392
|
+
|
|
393
|
+
| v2 Command | v3 Equivalent |
|
|
394
|
+
|------------|---------------|
|
|
395
|
+
| `l10n push` | `l10n translate && l10n ops update` |
|
|
396
|
+
| `l10n pull` | `l10n tm syncdown && l10n ops update` |
|
|
397
|
+
| `l10n status` | `l10n analyze` |
|
|
398
|
+
| `l10n grandfather` | Provider-based (configured in config) |
|
|
399
|
+
| `l10n leverage` | Provider-based (configured in config) |
|
|
400
|
+
|
|
401
|
+
For detailed migration guidance, see the [v3 Migration Guide](../v3.md).
|
|
402
|
+
|
|
403
|
+
## Help and Support
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# Get general help
|
|
407
|
+
l10n help
|
|
408
|
+
|
|
409
|
+
# Get command-specific help
|
|
410
|
+
l10n help translate
|
|
411
|
+
l10n help ops
|
|
412
|
+
l10n help source
|
|
413
|
+
|
|
414
|
+
# Get provider information
|
|
415
|
+
l10n ops providers --info
|
|
416
|
+
```
|
|
102
417
|
|
|
103
|
-
|
|
418
|
+
For more detailed documentation, see:
|
|
419
|
+
- [Main Documentation](../README.md)
|
|
420
|
+
- [Architecture Guide](../architecture.md)
|
|
421
|
+
- [Core Package Documentation](../core/README.md)
|
package/index.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
|
|
3
|
+
import { Command, Argument, Option, InvalidArgumentError } from 'commander';
|
|
4
|
+
import { getVerbosity } from '@l10nmonster/core';
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
const cliVersion = JSON.parse(readFileSync(path.join(import.meta.dirname, 'package.json'), 'utf-8')).version;
|
|
9
|
+
|
|
10
|
+
function intOptionParser(value) {
|
|
11
|
+
const parsedValue = parseInt(value, 10);
|
|
12
|
+
if (isNaN(parsedValue)) {
|
|
13
|
+
throw new InvalidArgumentError('Not an integer');
|
|
14
|
+
}
|
|
15
|
+
return parsedValue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function configureCommand(cmd, Action, l10nRunner) {
|
|
19
|
+
|
|
20
|
+
/** @this Command */
|
|
21
|
+
const actionHandler = async function actionHandler() {
|
|
22
|
+
const options = this.opts();
|
|
23
|
+
// Need to hack into the guts of commander as it doesn't seem to expose argument names
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
const args = Object.fromEntries(this._args.map((arg, idx) => [
|
|
26
|
+
arg._name,
|
|
27
|
+
arg.variadic ? this.args.slice(idx) : this.args[idx]
|
|
28
|
+
]));
|
|
29
|
+
await l10nRunner(async l10n => {
|
|
30
|
+
await l10n[Action.name]({ ...options, ...args })
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const help = Action.help;
|
|
34
|
+
cmd.description(help.description).action(actionHandler);
|
|
35
|
+
help.summary && cmd.summary(help.summary);
|
|
36
|
+
help.options && help.options.forEach(([ arg, desc, choices]) => {
|
|
37
|
+
if (choices) {
|
|
38
|
+
cmd.addOption(new Option(arg, desc).choices(choices));
|
|
39
|
+
} else {
|
|
40
|
+
cmd.option(arg, desc);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
help.requiredOptions && help.requiredOptions.forEach(opt => cmd.requiredOption(...opt));
|
|
44
|
+
help.arguments && help.arguments.forEach(([ arg, desc, choices]) => {
|
|
45
|
+
if (choices) {
|
|
46
|
+
cmd.addArgument(new Argument(arg, desc).choices(choices));
|
|
47
|
+
} else {
|
|
48
|
+
cmd.argument(arg, desc);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default async function runMonsterCLI(monsterConfig, cliCommand) {
|
|
54
|
+
const monsterCLI = new Command();
|
|
55
|
+
monsterCLI
|
|
56
|
+
.name('l10n')
|
|
57
|
+
.version(cliVersion, '--version', 'output the current version number')
|
|
58
|
+
.description('Continuous localization for the rest of us.')
|
|
59
|
+
.option('-v, --verbose [level]', '0=error, 1=warning, 2=info, 3=verbose', intOptionParser)
|
|
60
|
+
.option('--regression', 'keep variables constant during regression testing');
|
|
61
|
+
try {
|
|
62
|
+
const l10nRunner = async (cb) => {
|
|
63
|
+
const { verbose, regression } = monsterCLI.opts();
|
|
64
|
+
await monsterConfig
|
|
65
|
+
.verbose(verbose)
|
|
66
|
+
.regression(regression)
|
|
67
|
+
.run(async mm => await cb(mm.l10n));
|
|
68
|
+
};
|
|
69
|
+
monsterConfig.actions.forEach(Action => {
|
|
70
|
+
const cmd = monsterCLI.command(Action.name);
|
|
71
|
+
if (Action.subActions) {
|
|
72
|
+
Action.help.description && cmd.description(Action.help.description);
|
|
73
|
+
Action.subActions.forEach(subAction => {
|
|
74
|
+
const subName = subAction.name.split('_')[1];
|
|
75
|
+
const subCmd = cmd.command(subName);
|
|
76
|
+
configureCommand(subCmd, subAction, l10nRunner);
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
configureCommand(cmd, Action, l10nRunner);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const argv = typeof cliCommand === 'string' ? cliCommand.split(' ') : cliCommand;
|
|
83
|
+
await monsterCLI.parseAsync(argv);
|
|
84
|
+
} catch(e) {
|
|
85
|
+
console.error(`Unable to run CLI: ${(getVerbosity() > 1 ? e.stack : e.message) || e}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
package/l10n.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import runMonsterCLI from './index.js';
|
|
4
|
+
import { resolve, dirname, join } from 'path';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
function findConfigFile(startDir = process.cwd()) {
|
|
8
|
+
let currentDir = resolve(startDir);
|
|
9
|
+
const configFileName = 'l10nmonster.config.mjs';
|
|
10
|
+
|
|
11
|
+
while (true) {
|
|
12
|
+
const configPath = join(currentDir, configFileName);
|
|
13
|
+
if (existsSync(configPath)) {
|
|
14
|
+
return configPath;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const parentDir = dirname(currentDir);
|
|
18
|
+
if (parentDir === currentDir) {
|
|
19
|
+
// We've reached the root directory
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
currentDir = parentDir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const configPath = findConfigFile();
|
|
30
|
+
if (!configPath) {
|
|
31
|
+
console.error('Error: Could not find l10nmonster.config.mjs in current directory or any parent directory.');
|
|
32
|
+
console.error('Please ensure the config file exists in your project root or current working directory.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const config = await import(configPath);
|
|
37
|
+
await runMonsterCLI(config.default);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
40
|
+
console.error('Error: Could not load l10nmonster.config.mjs - the file may have syntax errors or missing dependencies.');
|
|
41
|
+
console.error('Details:', error.message);
|
|
42
|
+
} else if (error.code === 'ENOENT') {
|
|
43
|
+
console.error('Error: Config file was found but could not be accessed. Please check file permissions.');
|
|
44
|
+
console.error('Details:', error.message);
|
|
45
|
+
} else {
|
|
46
|
+
console.error('Error running l10nmonster CLI:', error.message);
|
|
47
|
+
}
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|