@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/README.md CHANGED
@@ -1,597 +1,172 @@
1
1
  # @shipi18n/cli
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@shipi18n/cli)](https://www.npmjs.com/package/@shipi18n/cli)
4
- [![npm downloads](https://img.shields.io/npm/dw/@shipi18n/cli)](https://www.npmjs.com/package/@shipi18n/cli)
5
- [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
- [![GitHub last commit](https://img.shields.io/github/last-commit/Shipi18n/shipi18n-cli)](https://github.com/Shipi18n/shipi18n-cli)
7
- [![CI](https://github.com/Shipi18n/shipi18n-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Shipi18n/shipi18n-cli/actions)
8
- [![codecov](https://codecov.io/gh/Shipi18n/shipi18n-cli/branch/main/graph/badge.svg)](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 install -g @shipi18n/cli
7
+ npm i -g @shipi18n/cli @anthropic-ai/sdk # or add `openai` for the OpenAI provider
40
8
  ```
41
9
 
42
- ### 2. Get Your Free API Key
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
- Done! You now have `es.json`, `fr.json`, and `de.json` in your `./locales` folder.
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 (`&amp;` → `&`)
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
- # Skip specific keys from translation (e.g., US state names, brand names)
136
- shipi18n translate en.json --target es --skip-keys "states.CA,states.NY,company.name"
137
-
138
- # Skip keys using glob patterns (e.g., all states, all config secrets)
139
- shipi18n translate en.json --target es --skip-paths "states.*,config.*.secret"
140
-
141
- # Combined - skip exact keys and patterns
142
- shipi18n translate en.json --target es,fr \
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
- **Disable fallback:**
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
- # List all saved keys
298
- shipi18n keys list
38
+ # Anthropic (default), multiple languages
39
+ shipi18n translate locales/en.json -t es,fr,ja
299
40
 
300
- # Export keys to JSON
301
- shipi18n keys export --format json --output keys.json
41
+ # OpenAI provider
42
+ shipi18n translate en.json -p openai -t de --api-key $OPENAI_API_KEY
302
43
 
303
- # Delete a specific key
304
- shipi18n keys delete <keyId>
44
+ # Incremental only translate keys not already in the target file
45
+ shipi18n translate en.json -t es --incremental
305
46
  ```
306
47
 
307
- ### Configuration
48
+ ## Check — validate translations in CI (no LLM, no key)
308
49
 
309
- Manage CLI settings:
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
- # Show current configuration
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
- ### Help
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
- ```bash
328
- # General help
329
- shipi18n --help
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
- # Command-specific help
332
- shipi18n translate --help
333
- shipi18n keys --help
334
- shipi18n config --help
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
- ## Configuration File
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
- The CLI stores settings in `~/.shipi18n/config.yml`:
77
+ ### GitHub Actions with PR annotations
340
78
 
341
79
  ```yaml
342
- apiKey: sk_live_your_api_key_here
343
- sourceLanguage: en
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
- ## Environment Variables
355
-
356
- You can also configure via environment variables:
357
-
358
- ```bash
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
- ## Supported Languages
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
- ## Examples
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
- # Your project structure
400
- my-app/
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
- ### Input File (`en.json`)
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
- ```json
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
- ### Output (`es.json`)
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
- ```json
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
- "scripts": {
499
- "translate": "shipi18n translate locales/en.json --target es,fr,de",
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
- Then run:
507
- ```bash
508
- npm run translate
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
- ## Troubleshooting
130
+ ## Protect hand-edited translations — `shipi18n lock`
512
131
 
513
- ### "API key not found"
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
- # Set your API key
517
- shipi18n config set apiKey YOUR_KEY
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
- # Get your key at https://shipi18n.com
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
- ### "Language limit exceeded"
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
- ### "Rate limit exceeded"
147
+ Afterwards `check` reports two new findings:
532
148
 
533
- Wait a minute or upgrade your plan for higher rate limits.
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
- ### "Invalid JSON"
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
- # Validate JSON
540
- cat en.json | jq .
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
- ## Documentation & Resources
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
- ## Related Packages
165
+ ## Bring your own LLM
573
166
 
574
- | Package | Description |
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
- Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
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