@shipi18n/cli 1.1.5 → 2.3.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/CHANGELOG.md +42 -0
- package/NOTICE +7 -1
- package/README.md +101 -526
- package/bin/shipi18n.js +36 -50
- package/package.json +43 -50
- package/src/commands/check.js +160 -0
- package/src/commands/lock.js +131 -0
- package/src/commands/translate.js +73 -316
- package/src/reporters.js +189 -0
- package/src/commands/config.js +0 -94
- package/src/commands/init.js +0 -443
- package/src/commands/keys.js +0 -128
- package/src/lib/api.js +0 -342
- package/src/lib/config.js +0 -74
- package/src/utils/incremental.js +0 -90
- package/src/utils/logger.js +0 -46
package/README.md
CHANGED
|
@@ -1,597 +1,172 @@
|
|
|
1
1
|
# @shipi18n/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://opensource.org/licenses/Apache-2.0)
|
|
6
|
-
[](https://github.com/Shipi18n/shipi18n-cli)
|
|
7
|
-
[](https://github.com/Shipi18n/shipi18n-cli/actions)
|
|
8
|
-
[](https://codecov.io/gh/Shipi18n/shipi18n-cli)
|
|
9
|
-
|
|
10
|
-
Command-line tool for translating locale files with [Shipi18n](https://shipi18n.com).
|
|
11
|
-
|
|
12
|
-
> **🚀 Translate JSON files in seconds** - One command, multiple languages!
|
|
13
|
-
|
|
14
|
-
## Why Shipi18n CLI?
|
|
15
|
-
|
|
16
|
-
- **Stop copy-pasting into Google Translate** - One command translates to 100+ languages
|
|
17
|
-
- **Placeholders stay intact** - `{name}`, `{{count}}`, `%s` are preserved automatically
|
|
18
|
-
- **Works with your existing setup** - Drop into any React, Vue, Next.js, i18next project
|
|
19
|
-
- **90-day Translation Memory** - Same content? Cached. No extra cost.
|
|
20
|
-
- **Free tier included** - 100 keys, 3 languages, no credit card required
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- ✅ **Translate JSON files** to 100+ languages with one command
|
|
25
|
-
- ✅ **Preserve JSON structure** - Nested objects, arrays, everything
|
|
26
|
-
- ✅ **Placeholder preservation** - Keep `{name}`, `{{value}}`, `%s`, etc. intact
|
|
27
|
-
- ✅ **Key-based pricing** - 100 free translation keys (unlimited characters!)
|
|
28
|
-
- ✅ **Language limits enforced** - FREE: 3 languages, STARTER: 10, PRO: unlimited
|
|
29
|
-
- ✅ **ZIP output** - Bundle translations into a single downloadable ZIP file
|
|
30
|
-
- ✅ **Config file support** - Save settings in `~/.shipi18n/config.yml`
|
|
31
|
-
- ✅ **Translation Memory** - Manage keys with `shipi18n keys` commands
|
|
32
|
-
- ✅ **Beautiful output** - Colored, formatted terminal output
|
|
33
|
-
|
|
34
|
-
## Quick Start
|
|
35
|
-
|
|
36
|
-
### 1. Install
|
|
3
|
+
Open-source, **bring-your-own-LLM** i18n translation CLI. Translate your locale files with your own
|
|
4
|
+
OpenAI or Anthropic key — no Shipi18n account, no hosted API.
|
|
37
5
|
|
|
38
6
|
```bash
|
|
39
|
-
npm
|
|
7
|
+
npm i -g @shipi18n/cli @anthropic-ai/sdk # or add `openai` for the OpenAI provider
|
|
40
8
|
```
|
|
41
9
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Sign up at [shipi18n.com](https://shipi18n.com) - it takes 30 seconds!
|
|
45
|
-
|
|
46
|
-
**Free tier includes:**
|
|
47
|
-
- 100 translation keys
|
|
48
|
-
- 3 languages
|
|
49
|
-
- 10 requests/minute
|
|
50
|
-
- Unlimited characters
|
|
51
|
-
|
|
52
|
-
### 3. Configure
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
shipi18n config set apiKey YOUR_API_KEY
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 4. Translate!
|
|
10
|
+
## Quickstart
|
|
59
11
|
|
|
60
12
|
```bash
|
|
13
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
61
14
|
shipi18n translate en.json --target es,fr,de
|
|
62
15
|
```
|
|
63
16
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
67
|
-
|
|
68
|
-
### Global (recommended)
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
npm install -g @shipi18n/cli
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Local project
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
npm install --save-dev @shipi18n/cli
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Then use via npx:
|
|
81
|
-
```bash
|
|
82
|
-
npx shipi18n translate en.json --target es,fr
|
|
83
|
-
```
|
|
17
|
+
Writes `es.json`, `fr.json`, `de.json` into the output directory (default `./locales`), preserving
|
|
18
|
+
structure and placeholders.
|
|
84
19
|
|
|
85
20
|
## Usage
|
|
86
21
|
|
|
87
|
-
### Translate Command
|
|
88
|
-
|
|
89
|
-
Translate a JSON locale file to multiple languages:
|
|
90
|
-
|
|
91
22
|
```bash
|
|
92
|
-
shipi18n translate <input> [options]
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Options:**
|
|
96
|
-
- `-t, --target <languages>` - Target languages (comma-separated, default: `es,fr`)
|
|
97
|
-
- `-s, --source <language>` - Source language (default: `en`)
|
|
98
|
-
- `-o, --output <dir>` - Output directory (default: `./locales`)
|
|
99
|
-
- `--api-key <key>` - API key (overrides config)
|
|
100
|
-
- `--preserve-placeholders` - Preserve placeholders (default: `true`)
|
|
101
|
-
- `--html-handling <mode>` - How to handle HTML in source text (default: `none`)
|
|
102
|
-
- `none` - Leave HTML as-is
|
|
103
|
-
- `strip` - Remove all HTML tags
|
|
104
|
-
- `decode` - Decode HTML entities (`&` → `&`)
|
|
105
|
-
- `preserve` - Keep HTML tags and translate text between them
|
|
106
|
-
- `--no-fallback` - Disable fallback to source for missing translations
|
|
107
|
-
- `--no-regional-fallback` - Disable regional fallback (e.g., pt-BR → pt)
|
|
108
|
-
- `-i, --incremental` - Only translate new/missing keys (skip existing translations)
|
|
109
|
-
- `--skip-keys <keys>` - Keys to skip from translation (comma-separated exact paths)
|
|
110
|
-
- `--skip-paths <patterns>` - Path patterns to skip (comma-separated, supports wildcards like `nav.*`)
|
|
111
|
-
- `--context-file <path>` - JSON file with context annotations for disambiguation
|
|
112
|
-
- `--zip [filename]` - Output translations as a single ZIP file (default: `translations.zip`)
|
|
113
|
-
|
|
114
|
-
**Examples:**
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
# Basic usage
|
|
118
|
-
shipi18n translate en.json --target es,fr
|
|
119
|
-
|
|
120
|
-
# Custom output directory
|
|
121
|
-
shipi18n translate en.json --target es,fr,de --output ./translations
|
|
122
|
-
|
|
123
|
-
# Specify source language
|
|
124
|
-
shipi18n translate ja.json --source ja --target en,es
|
|
125
|
-
|
|
126
|
-
# Use inline API key
|
|
127
|
-
shipi18n translate en.json --target es --api-key sk_live_...
|
|
128
|
-
|
|
129
|
-
# Translate with regional variants (pt-BR will fallback to pt if needed)
|
|
130
|
-
shipi18n translate en.json --target es,pt-BR,zh-TW
|
|
131
|
-
|
|
132
|
-
# Disable fallback (strict mode - fail if translation missing)
|
|
133
|
-
shipi18n translate en.json --target es --no-fallback
|
|
23
|
+
shipi18n translate <input> --target <langs> [options]
|
|
134
24
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
--skip-keys "brandName" \
|
|
144
|
-
--skip-paths "states.*,internal.*"
|
|
145
|
-
|
|
146
|
-
# Output as ZIP file (default name: translations.zip)
|
|
147
|
-
shipi18n translate en.json --target es,fr,de --zip
|
|
148
|
-
|
|
149
|
-
# Output as ZIP with custom filename
|
|
150
|
-
shipi18n translate en.json --target es,fr,de --zip my-translations.zip
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Fallback Behavior
|
|
154
|
-
|
|
155
|
-
By default, the CLI handles missing translations gracefully:
|
|
156
|
-
|
|
157
|
-
| Scenario | Default Behavior |
|
|
158
|
-
|----------|-----------------|
|
|
159
|
-
| Missing translation for a language | Uses source content (English) |
|
|
160
|
-
| Missing regional variant (pt-BR) | Falls back to base language (pt), then source |
|
|
161
|
-
| Missing translation for a key | Fills from source content |
|
|
162
|
-
|
|
163
|
-
**Fallback output example:**
|
|
164
|
-
```
|
|
165
|
-
✓ Translated to 3 languages!
|
|
166
|
-
✓ Saved: ./locales/es.json
|
|
167
|
-
✓ Saved: ./locales/pt-BR.json
|
|
168
|
-
✓ Saved: ./locales/zh-TW.json
|
|
169
|
-
|
|
170
|
-
Fallback information:
|
|
171
|
-
• pt-BR → pt (regional fallback)
|
|
172
|
-
• zh-TW → en (source fallback)
|
|
173
|
-
• es: 2 keys used fallback
|
|
174
|
-
- checkout.terms
|
|
175
|
-
- checkout.privacy
|
|
176
|
-
|
|
177
|
-
✨ Successfully translated 3 files!
|
|
25
|
+
Options:
|
|
26
|
+
-t, --target <langs> Comma-separated target language codes (default: es,fr)
|
|
27
|
+
-s, --source <language> Source language code (default: en)
|
|
28
|
+
-o, --output <dir> Output directory (default: ./locales)
|
|
29
|
+
-p, --provider <name> LLM provider: anthropic (default) or openai
|
|
30
|
+
--api-key <key> LLM API key (else ANTHROPIC_API_KEY / OPENAI_API_KEY env)
|
|
31
|
+
--model <model> Override the provider's default model
|
|
32
|
+
-i, --incremental Reuse existing output files; only translate new/missing keys
|
|
178
33
|
```
|
|
179
34
|
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
# Strict mode - no fallback to source
|
|
183
|
-
shipi18n translate en.json --target es --no-fallback
|
|
184
|
-
|
|
185
|
-
# Disable regional fallback only (pt-BR won't fall back to pt)
|
|
186
|
-
shipi18n translate en.json --target pt-BR --no-regional-fallback
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Skipping Keys
|
|
190
|
-
|
|
191
|
-
Exclude specific keys or patterns from translation - useful for brand names, US state codes, or config values that should stay in English:
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
# Skip exact key paths
|
|
195
|
-
shipi18n translate en.json --target es --skip-keys "company.name,legal.terms"
|
|
196
|
-
|
|
197
|
-
# Skip using glob patterns
|
|
198
|
-
shipi18n translate en.json --target es --skip-paths "states.*,config.*.internal"
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
**Pattern Matching:**
|
|
202
|
-
| Pattern | Matches |
|
|
203
|
-
|---------|---------|
|
|
204
|
-
| `states.CA` | Exact path only |
|
|
205
|
-
| `states.*` | `states.CA`, `states.NY`, etc. (single level) |
|
|
206
|
-
| `config.*.secret` | `config.api.secret`, `config.db.secret` |
|
|
207
|
-
| `**.internal` | Any path ending with `.internal` |
|
|
208
|
-
|
|
209
|
-
**Example output with skipped keys:**
|
|
210
|
-
```
|
|
211
|
-
✓ Translated 45 keys to 2 languages!
|
|
212
|
-
ℹ Skipped 5 key(s) from translation:
|
|
213
|
-
• states.CA
|
|
214
|
-
• states.NY
|
|
215
|
-
• states.TX
|
|
216
|
-
• company.name
|
|
217
|
-
• config.api.secret
|
|
218
|
-
|
|
219
|
-
✨ Successfully translated 2 files!
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Context Annotations
|
|
223
|
-
|
|
224
|
-
Improve translation quality for ambiguous words by providing context hints:
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
# Create a context file
|
|
228
|
-
echo '{"close": "button - dismiss window", "address": "form field - location"}' > context.json
|
|
229
|
-
|
|
230
|
-
# Translate with context
|
|
231
|
-
shipi18n translate en.json --target es --context-file context.json
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
**Example context.json:**
|
|
235
|
-
```json
|
|
236
|
-
{
|
|
237
|
-
"close": "button label - dismiss/shut a dialog",
|
|
238
|
-
"address": "form field - physical location/street address",
|
|
239
|
-
"post": "verb - publish content"
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**Result:** "close" → "Cerrar" (not "Cerca"), "address" → "Dirección" (not "Dirigirse")
|
|
244
|
-
|
|
245
|
-
### Legal Content Warning
|
|
246
|
-
|
|
247
|
-
The CLI automatically warns when translating keys that may contain legal content:
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
⚠️ Legal content detected - review these keys:
|
|
251
|
-
• terms_of_service
|
|
252
|
-
• privacy_policy
|
|
253
|
-
• disclaimer
|
|
254
|
-
Machine-translated legal text may not be legally binding.
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**Detected patterns:** terms, privacy, disclaimer, legal, tos, eula, copyright, license, gdpr, cookie_policy, compliance, data_protection, refund, warranty
|
|
258
|
-
|
|
259
|
-
### ZIP Output
|
|
260
|
-
|
|
261
|
-
Bundle all translations into a single ZIP file for easy distribution:
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
# Default filename (translations.zip)
|
|
265
|
-
shipi18n translate en.json --target es,fr,de,ja --zip
|
|
266
|
-
|
|
267
|
-
# Custom filename
|
|
268
|
-
shipi18n translate en.json --target es,fr,de,ja --zip locales-v2.zip
|
|
269
|
-
|
|
270
|
-
# With custom output directory
|
|
271
|
-
shipi18n translate en.json --target es,fr --zip --output ./dist/i18n
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**Output:**
|
|
275
|
-
```
|
|
276
|
-
✔ Translated 50 keys to 4 languages!
|
|
277
|
-
✓ Saved: ./locales/translations.zip (4 files)
|
|
278
|
-
|
|
279
|
-
✨ Successfully translated 4 files!
|
|
280
|
-
Output: ./locales
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**ZIP structure:**
|
|
284
|
-
```
|
|
285
|
-
translations.zip
|
|
286
|
-
├── es.json
|
|
287
|
-
├── fr.json
|
|
288
|
-
├── de.json
|
|
289
|
-
└── ja.json
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Keys Management
|
|
293
|
-
|
|
294
|
-
Manage your translation keys in Translation Memory:
|
|
35
|
+
## Examples
|
|
295
36
|
|
|
296
37
|
```bash
|
|
297
|
-
#
|
|
298
|
-
shipi18n
|
|
38
|
+
# Anthropic (default), multiple languages
|
|
39
|
+
shipi18n translate locales/en.json -t es,fr,ja
|
|
299
40
|
|
|
300
|
-
#
|
|
301
|
-
shipi18n
|
|
41
|
+
# OpenAI provider
|
|
42
|
+
shipi18n translate en.json -p openai -t de --api-key $OPENAI_API_KEY
|
|
302
43
|
|
|
303
|
-
#
|
|
304
|
-
shipi18n
|
|
44
|
+
# Incremental — only translate keys not already in the target file
|
|
45
|
+
shipi18n translate en.json -t es --incremental
|
|
305
46
|
```
|
|
306
47
|
|
|
307
|
-
|
|
48
|
+
## Check — validate translations in CI (no LLM, no key)
|
|
308
49
|
|
|
309
|
-
|
|
50
|
+
`shipi18n check` is a deterministic QA gate for translated locale files. It works on output from
|
|
51
|
+
**any** translator — this CLI, another tool, an agent, or a human — and needs no API key, so it can
|
|
52
|
+
run on every push.
|
|
310
53
|
|
|
311
54
|
```bash
|
|
312
|
-
|
|
313
|
-
shipi18n config get
|
|
314
|
-
|
|
315
|
-
# Set API key
|
|
316
|
-
shipi18n config set apiKey YOUR_KEY
|
|
317
|
-
|
|
318
|
-
# Set default target languages
|
|
319
|
-
shipi18n config set targetLanguages es,fr,de
|
|
320
|
-
|
|
321
|
-
# Initialize config file with defaults
|
|
322
|
-
shipi18n config init
|
|
55
|
+
npx @shipi18n/cli check ./locales --source en
|
|
323
56
|
```
|
|
324
57
|
|
|
325
|
-
|
|
58
|
+
It detects both common layouts (`locales/en.json` and `locales/en/<ns>.json`), plus Flutter ARB
|
|
59
|
+
directories and Apple String Catalogs (`shipi18n check Localizable.xcstrings`).
|
|
326
60
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
61
|
+
**What it catches:** missing and orphaned keys · dropped or invented placeholders (`{{name}}`,
|
|
62
|
+
`{count}`, `%s`, `%1$s`, `%@`, `%lld`, `$t(...)`, `%{name}`, HTML tags) · collapsed vue-i18n pipe
|
|
63
|
+
plurals · empty values · untranslated copy · stale `.xcstrings` states.
|
|
330
64
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
65
|
+
| Flag | Default | Meaning |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `-s, --source <lang>` | `en` | Source language |
|
|
68
|
+
| `-r, --reporter <name>` | `human` | `human` \| `json` \| `sarif` \| `junit` |
|
|
69
|
+
| `-o, --output <file>` | stdout | Write the report to a file |
|
|
70
|
+
| `--ignore-keys <globs>` | — | Silence keys: `'*.copyright,home:mcp.badge'` |
|
|
71
|
+
| `--fail-on <level>` | `error` | `error` \| `warning` \| `none` |
|
|
72
|
+
| `--min-coverage <pct>` | — | Fail any language below this coverage |
|
|
336
73
|
|
|
337
|
-
|
|
74
|
+
Exit codes: `0` pass, `1` findings at the fail level, `2` usage error. Errors may fail CI; warnings
|
|
75
|
+
never do by default — a warning that blocks PRs gets the tool uninstalled.
|
|
338
76
|
|
|
339
|
-
|
|
77
|
+
### GitHub Actions with PR annotations
|
|
340
78
|
|
|
341
79
|
```yaml
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
targetLanguages:
|
|
345
|
-
- es
|
|
346
|
-
- fr
|
|
347
|
-
- de
|
|
348
|
-
outputDir: ./locales
|
|
349
|
-
saveKeys: true
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
**Priority:** Environment variables > Config file > Command-line options
|
|
80
|
+
- name: Check translations
|
|
81
|
+
run: npx @shipi18n/cli check ./locales -s en --reporter sarif --output i18n.sarif
|
|
353
82
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
export SHIPI18N_API_KEY=sk_live_your_api_key_here
|
|
360
|
-
export SHIPI18N_SOURCE_LANG=en
|
|
361
|
-
export SHIPI18N_TARGET_LANGS=es,fr,de
|
|
362
|
-
export SHIPI18N_OUTPUT_DIR=./locales
|
|
83
|
+
- name: Upload findings
|
|
84
|
+
if: always()
|
|
85
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
86
|
+
with:
|
|
87
|
+
sarif_file: i18n.sarif
|
|
363
88
|
```
|
|
364
89
|
|
|
365
|
-
##
|
|
366
|
-
|
|
367
|
-
Shipi18n supports **100+ languages** including:
|
|
368
|
-
|
|
369
|
-
**Popular:**
|
|
370
|
-
- 🇪🇸 Spanish (es)
|
|
371
|
-
- 🇫🇷 French (fr)
|
|
372
|
-
- 🇩🇪 German (de)
|
|
373
|
-
- 🇯🇵 Japanese (ja)
|
|
374
|
-
- 🇨🇳 Chinese Simplified (zh)
|
|
375
|
-
- 🇨🇳 Chinese Traditional (zh-TW)
|
|
376
|
-
- 🇵🇹 Portuguese (pt)
|
|
377
|
-
- 🇷🇺 Russian (ru)
|
|
378
|
-
- 🇰🇷 Korean (ko)
|
|
379
|
-
- 🇮🇹 Italian (it)
|
|
380
|
-
|
|
381
|
-
[See full list of 100+ supported languages](https://shipi18n.com/docs/languages)
|
|
382
|
-
|
|
383
|
-
## Pricing
|
|
384
|
-
|
|
385
|
-
| Tier | Price | Keys | Languages | Rate Limit |
|
|
386
|
-
|------|-------|------|-----------|------------|
|
|
387
|
-
| **FREE** | $0/mo | 100 | 3 | 10 req/min |
|
|
388
|
-
| **STARTER** | $9/mo | 500 | 10 | 60 req/min |
|
|
389
|
-
| **PRO** | $29/mo | 10K | 100+ | 300 req/min |
|
|
390
|
-
| **ENTERPRISE** | Custom | Unlimited | Custom | 1000+ req/min |
|
|
391
|
-
|
|
392
|
-
**What's a "key"?** Each unique translation path (e.g., `app.welcome`) counts as one key. Translating to multiple languages doesn't multiply the count!
|
|
90
|
+
## Semantic QA — `--semantic` (the judge)
|
|
393
91
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
### Real-World Workflow
|
|
92
|
+
The structural check cannot see a translation that is *fluent but wrong*. `--semantic` adds an
|
|
93
|
+
LLM-as-judge pass with your own key:
|
|
397
94
|
|
|
398
95
|
```bash
|
|
399
|
-
#
|
|
400
|
-
|
|
401
|
-
├── locales/
|
|
402
|
-
│ └── en.json # ✅ You have this
|
|
403
|
-
└── src/
|
|
404
|
-
|
|
405
|
-
# Translate to multiple languages
|
|
406
|
-
$ shipi18n translate locales/en.json --target es,fr,de,ja
|
|
407
|
-
|
|
408
|
-
# Result
|
|
409
|
-
my-app/
|
|
410
|
-
├── locales/
|
|
411
|
-
│ ├── en.json # ✅ Original
|
|
412
|
-
│ ├── es.json # ✅ Spanish
|
|
413
|
-
│ ├── fr.json # ✅ French
|
|
414
|
-
│ ├── de.json # ✅ German
|
|
415
|
-
│ └── ja.json # ✅ Japanese
|
|
416
|
-
└── src/
|
|
96
|
+
npx @shipi18n/cli check ./locales -s en --semantic # advisory: warnings only
|
|
97
|
+
npx @shipi18n/cli check ./locales -s en --semantic --glossary glossary.json
|
|
417
98
|
```
|
|
418
99
|
|
|
419
|
-
|
|
100
|
+
**Honest limitations, up front:** the judge is probabilistic. Every key is judged across 3 passes
|
|
101
|
+
and flagged only on a majority vote, unparseable passes are discarded, and semantic findings are
|
|
102
|
+
**warnings by default** — they never fail CI unless you opt in with `--semantic-fail`. It augments
|
|
103
|
+
review; it does not replace it. You pay your provider for the tokens; the verdict cache
|
|
104
|
+
(`.shipi18n/semantic-cache.json`, safe to commit) makes unchanged re-runs free, and keys that
|
|
105
|
+
already failed the structural check are never sent to the judge.
|
|
420
106
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
"app": {
|
|
424
|
-
"title": "My Application",
|
|
425
|
-
"welcome": "Welcome, {username}!",
|
|
426
|
-
"description": "This is a demo"
|
|
427
|
-
},
|
|
428
|
-
"auth": {
|
|
429
|
-
"login": "Log In",
|
|
430
|
-
"logout": "Log Out"
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
```
|
|
107
|
+
What it flags: `semantic-mistranslation` (says something different), `semantic-omission` (meaning
|
|
108
|
+
dropped), `semantic-addition` (meaning invented).
|
|
434
109
|
|
|
435
|
-
|
|
110
|
+
**Measured** (2026-08-16, committed 228-pair corpus, thresholds fixed before the judge was built,
|
|
111
|
+
default judge `claude-haiku-4-5`, 3 passes): **100%** of planted errors caught (100% per category,
|
|
112
|
+
100% label accuracy), **7.1%** false positives on clean pairs, 6/6 glossary violations with 0 false,
|
|
113
|
+
~62k tokens in 141s. On a real 478-pair production tree it flagged 3.6% of keys; the warm-cache
|
|
114
|
+
rerun made **zero** model calls. Full harness: `evals/semantic/` in the repo — run it against your
|
|
115
|
+
own model.
|
|
436
116
|
|
|
437
|
-
|
|
438
|
-
{
|
|
439
|
-
"app": {
|
|
440
|
-
"title": "Mi Aplicación",
|
|
441
|
-
"welcome": "¡Bienvenido, {username}!",
|
|
442
|
-
"description": "Esta es una demostración"
|
|
443
|
-
},
|
|
444
|
-
"auth": {
|
|
445
|
-
"login": "Iniciar Sesión",
|
|
446
|
-
"logout": "Cerrar Sesión"
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
Notice how:
|
|
452
|
-
- ✅ JSON structure is preserved
|
|
453
|
-
- ✅ Placeholders like `{username}` are kept intact
|
|
454
|
-
- ✅ Only values are translated, keys stay in English
|
|
455
|
-
|
|
456
|
-
## CI/CD Integration
|
|
457
|
-
|
|
458
|
-
### GitHub Actions
|
|
459
|
-
|
|
460
|
-
```yaml
|
|
461
|
-
name: Translate Locales
|
|
462
|
-
on: [push]
|
|
463
|
-
|
|
464
|
-
jobs:
|
|
465
|
-
translate:
|
|
466
|
-
runs-on: ubuntu-latest
|
|
467
|
-
steps:
|
|
468
|
-
- uses: actions/checkout@v3
|
|
469
|
-
|
|
470
|
-
- name: Setup Node.js
|
|
471
|
-
uses: actions/setup-node@v3
|
|
472
|
-
with:
|
|
473
|
-
node-version: '18'
|
|
474
|
-
|
|
475
|
-
- name: Install Shipi18n CLI
|
|
476
|
-
run: npm install -g @shipi18n/cli
|
|
477
|
-
|
|
478
|
-
- name: Translate
|
|
479
|
-
env:
|
|
480
|
-
SHIPI18N_API_KEY: ${{ secrets.SHIPI18N_API_KEY }}
|
|
481
|
-
run: shipi18n translate locales/en.json --target es,fr,de
|
|
482
|
-
|
|
483
|
-
- name: Commit translations
|
|
484
|
-
run: |
|
|
485
|
-
git config user.name "github-actions"
|
|
486
|
-
git config user.email "github-actions@github.com"
|
|
487
|
-
git add locales/
|
|
488
|
-
git commit -m "Update translations" || echo "No changes"
|
|
489
|
-
git push
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
### NPM Scripts
|
|
493
|
-
|
|
494
|
-
Add to your `package.json`:
|
|
117
|
+
### Glossary (deterministic — no LLM)
|
|
495
118
|
|
|
496
119
|
```json
|
|
497
120
|
{
|
|
498
|
-
"
|
|
499
|
-
|
|
500
|
-
"translate:dev": "shipi18n translate locales/en.json --target es",
|
|
501
|
-
"translate:all": "shipi18n translate locales/en.json --target es,fr,de,ja,zh,pt,ru,ko"
|
|
502
|
-
}
|
|
121
|
+
"Shipi18n": { "dnt": true },
|
|
122
|
+
"dashboard": { "es": "panel", "de": "Dashboard", "ja": "ダッシュボード" }
|
|
503
123
|
}
|
|
504
124
|
```
|
|
505
125
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
```
|
|
126
|
+
`"dnt"` terms must survive verbatim; language entries are required translations. Violations are
|
|
127
|
+
`glossary-violation` **errors**, caught by string matching at zero cost, and the glossary is also
|
|
128
|
+
given to the judge as context.
|
|
510
129
|
|
|
511
|
-
##
|
|
130
|
+
## Protect hand-edited translations — `shipi18n lock`
|
|
512
131
|
|
|
513
|
-
|
|
132
|
+
The oldest complaint about machine translation: you fix a string by hand, the tool runs again, and
|
|
133
|
+
your fix is gone. Lock the translations a human has blessed, and `check` tells you when that happens.
|
|
514
134
|
|
|
515
135
|
```bash
|
|
516
|
-
#
|
|
517
|
-
shipi18n
|
|
518
|
-
|
|
519
|
-
# Or use environment variable
|
|
520
|
-
export SHIPI18N_API_KEY=YOUR_KEY
|
|
136
|
+
# bless everything currently in the tree
|
|
137
|
+
npx @shipi18n/cli lock ./locales
|
|
521
138
|
|
|
522
|
-
#
|
|
139
|
+
# or just the strings you actually hand-edited
|
|
140
|
+
npx @shipi18n/cli lock ./locales --keys 'legal.*,checkout.cta'
|
|
141
|
+
npx @shipi18n/cli lock ./locales --lang de,ja # narrow to some languages
|
|
523
142
|
```
|
|
524
143
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
The FREE tier allows 3 languages. Upgrade your plan:
|
|
528
|
-
- **STARTER** ($9/mo) - 10 languages
|
|
529
|
-
- **PRO** ($29/mo) - 100+ languages
|
|
144
|
+
This writes `.shipi18n/locks.json` — **commit it**, it is the record of which translations a person
|
|
145
|
+
reviewed. It stores only hashes, never your strings.
|
|
530
146
|
|
|
531
|
-
|
|
147
|
+
Afterwards `check` reports two new findings:
|
|
532
148
|
|
|
533
|
-
|
|
149
|
+
| Finding | Meaning |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| `manual-translation-clobbered` | the locked translation's text changed — someone re-translated over a human edit |
|
|
152
|
+
| `manual-translation-stale` | the **source** changed underneath a locked translation, so the human edit may no longer be right |
|
|
534
153
|
|
|
535
|
-
|
|
154
|
+
Both are **warnings, never errors**: this feature exists to protect people's work, not to block their
|
|
155
|
+
pipeline. A lock that failed CI would just get deleted. Use `--fail-on warning` if you disagree, or
|
|
156
|
+
`--no-locks` to ignore the lock file entirely.
|
|
536
157
|
|
|
537
|
-
Make sure your input file is valid JSON:
|
|
538
158
|
```bash
|
|
539
|
-
#
|
|
540
|
-
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
## Development
|
|
544
|
-
|
|
545
|
-
```bash
|
|
546
|
-
# Clone the repo
|
|
547
|
-
git clone https://github.com/Shipi18n/shipi18n-cli.git
|
|
548
|
-
cd shipi18n-cli
|
|
549
|
-
|
|
550
|
-
# Install dependencies
|
|
551
|
-
npm install
|
|
552
|
-
|
|
553
|
-
# Test locally
|
|
554
|
-
node bin/shipi18n.js translate test.json --target es,fr
|
|
555
|
-
|
|
556
|
-
# Link globally for testing
|
|
557
|
-
npm link
|
|
558
|
-
shipi18n --help
|
|
159
|
+
# accept the current state as the new blessed baseline
|
|
160
|
+
npx @shipi18n/cli lock ./locales --relock
|
|
559
161
|
```
|
|
560
162
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
📚 **Full Documentation:** [shipi18n.com/integrations/cli](https://shipi18n.com/integrations/cli)
|
|
564
|
-
|
|
565
|
-
| Resource | Link |
|
|
566
|
-
|----------|------|
|
|
567
|
-
| **Getting Started** | [shipi18n.com](https://shipi18n.com) |
|
|
568
|
-
| **API Reference** | [shipi18n.com/api](https://shipi18n.com/api) |
|
|
569
|
-
| **i18next Best Practices** | [shipi18n.com/integrations/react](https://shipi18n.com/integrations/react) |
|
|
570
|
-
| **Blog & Tutorials** | [shipi18n.com/blog](https://shipi18n.com/blog) |
|
|
163
|
+
A missing or corrupt lock file is a cold start, not a crash.
|
|
571
164
|
|
|
572
|
-
##
|
|
165
|
+
## Bring your own LLM
|
|
573
166
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
| [@shipi18n/api](https://www.npmjs.com/package/@shipi18n/api) | Node.js SDK for programmatic use |
|
|
577
|
-
| [vite-plugin-shipi18n](https://www.npmjs.com/package/vite-plugin-shipi18n) | Vite plugin for build-time translation |
|
|
578
|
-
| [i18next-shipi18n-backend](https://www.npmjs.com/package/i18next-shipi18n-backend) | i18next backend for dynamic loading |
|
|
579
|
-
| [shipi18n-github-action](https://github.com/marketplace/actions/shipi18n-auto-translate) | GitHub Action for CI/CD |
|
|
580
|
-
|
|
581
|
-
## Examples
|
|
582
|
-
|
|
583
|
-
- [Node.js Example](https://github.com/Shipi18n/shipi18n-nodejs-example) - Basic usage examples
|
|
584
|
-
- [Vue Example](https://github.com/Shipi18n/shipi18n-vue-example) - Vue 3 + vue-i18n integration
|
|
167
|
+
Set `ANTHROPIC_API_KEY` (default provider) or use `-p openai` with `OPENAI_API_KEY`. Your keys, your
|
|
168
|
+
models — nothing is sent to a Shipi18n server. Built on [`@shipi18n/core`](https://www.npmjs.com/package/@shipi18n/core).
|
|
585
169
|
|
|
586
170
|
## License
|
|
587
171
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
---
|
|
591
|
-
|
|
592
|
-
<p align="center">
|
|
593
|
-
<a href="https://shipi18n.com">shipi18n.com</a> ·
|
|
594
|
-
<a href="https://github.com/Shipi18n">GitHub</a> ·
|
|
595
|
-
<a href="https://shipi18n.com/pricing">Pricing</a>
|
|
596
|
-
</p>
|
|
597
|
-
|
|
172
|
+
Apache-2.0
|