langctl 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +158 -1188
  3. package/dist/commands/auth.js +85 -19
  4. package/dist/commands/config.js +36 -26
  5. package/dist/commands/export.js +11 -122
  6. package/dist/commands/import.js +32 -143
  7. package/dist/commands/init.js +61 -72
  8. package/dist/commands/keys.js +145 -279
  9. package/dist/commands/org.js +45 -111
  10. package/dist/commands/projects.js +124 -312
  11. package/dist/commands/pull.js +86 -129
  12. package/dist/commands/push.js +125 -0
  13. package/dist/commands/review.js +65 -0
  14. package/dist/commands/team.js +68 -257
  15. package/dist/commands/translate.js +99 -0
  16. package/dist/core/cache.js +48 -0
  17. package/dist/core/config.js +130 -0
  18. package/dist/core/errors.js +67 -0
  19. package/dist/core/files.js +38 -0
  20. package/dist/core/http.js +156 -0
  21. package/dist/core/output.js +96 -0
  22. package/dist/core/project.js +58 -0
  23. package/dist/core/prompts.js +56 -0
  24. package/dist/formats/index.js +285 -0
  25. package/dist/index.js +277 -508
  26. package/dist/version.js +4 -0
  27. package/package.json +22 -15
  28. package/.gitattributes +0 -2
  29. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -26
  30. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  31. package/CONTRIBUTING.md +0 -33
  32. package/dist/api.d.ts +0 -20
  33. package/dist/api.js +0 -75
  34. package/dist/auth.d.ts +0 -38
  35. package/dist/auth.js +0 -106
  36. package/dist/commands/auth.d.ts +0 -3
  37. package/dist/commands/config.d.ts +0 -2
  38. package/dist/commands/debug.d.ts +0 -3
  39. package/dist/commands/debug.js +0 -61
  40. package/dist/commands/export.d.ts +0 -13
  41. package/dist/commands/import.d.ts +0 -12
  42. package/dist/commands/init.d.ts +0 -2
  43. package/dist/commands/keys.d.ts +0 -25
  44. package/dist/commands/org.d.ts +0 -13
  45. package/dist/commands/projects.d.ts +0 -33
  46. package/dist/commands/pull.d.ts +0 -9
  47. package/dist/commands/team.d.ts +0 -29
  48. package/dist/config.d.ts +0 -48
  49. package/dist/config.js +0 -69
  50. package/dist/exporters/index.d.ts +0 -36
  51. package/dist/exporters/index.js +0 -214
  52. package/dist/index.d.ts +0 -3
  53. package/dist/utils/banner.d.ts +0 -9
  54. package/dist/utils/banner.js +0 -24
  55. package/translations/en.json +0 -5
  56. package/translations/en.xml +0 -9
  57. package/translations/fr.json +0 -4
  58. package/translations/fr.xml +0 -7
  59. package/translations/hi.json +0 -4
  60. package/translations/hi.xml +0 -7
  61. package/translations/ios/en.strings +0 -9
  62. package/translations/ios/fr.strings +0 -6
  63. package/translations/ios/hi.strings +0 -6
  64. package/translations/ios/ja.strings +0 -6
  65. package/translations/ja.json +0 -4
  66. package/translations/ja.xml +0 -7
package/README.md CHANGED
@@ -1,1232 +1,202 @@
1
1
  <div align="center">
2
2
 
3
- # Langctl
3
+ # langctl
4
4
 
5
- **CLI-first translation management for developers**
5
+ **Translation management from your terminal and CI**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/langctl.svg)](https://www.npmjs.com/package/langctl)
8
- [![license](https://img.shields.io/npm/l/langctl.svg)](https://github.com/siddharthsaxena0/langctl/blob/main/LICENSE)
9
- [![npm downloads](https://img.shields.io/npm/dm/langctl.svg)](https://www.npmjs.com/package/langctl)
8
+ [![CI](https://github.com/litcode-pvt-ltd/langctl-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/litcode-pvt-ltd/langctl-cli/actions/workflows/ci.yml)
9
+ [![license](https://img.shields.io/npm/l/langctl.svg)](LICENSE)
10
10
 
11
- **[Website](https://langctl.com)** · **[Documentation](https://langctl.com/docs)** · **[Get Started](https://app.langctl.com/signup)**
12
-
13
- <!-- Add a demo GIF here: -->
14
- <!-- ![langctl demo](assets/demo.gif) -->
11
+ [Website](https://langctl.com) · [Docs](https://langctl.com/docs) · [Sign up](https://app.langctl.com/signup)
15
12
 
16
13
  </div>
17
14
 
18
- ---
19
-
20
- Langctl is a powerful command-line tool that lets you manage translations directly from your terminal. Create projects, manage translation keys, invite team members, export in multiple formats, and integrate seamlessly into your CI/CD pipeline.
21
-
22
- ## Features
23
-
24
- - 🚀 **Complete Project Management** - Create, update, and manage translation projects
25
- - 🔑 **Translation Key CRUD** - Full control over translation keys and values
26
- - 👥 **Team Management** - Invite members, manage roles, and handle invitations
27
- - 📊 **Organization Insights** - View stats, plan limits, and usage metrics
28
- - 📦 **Multi-Format Export** - Support for JSON, iOS, Android, Flutter, and i18n
29
- - 🔄 **Import Translations** - Bulk import from JSON files
30
- - 🤖 **CI/CD Ready** - Perfect for automated workflows
31
- - 🌍 **Multi-Language Support** - Manage unlimited languages per project
15
+ `langctl` keeps the translation files in your repository in sync with [Langctl](https://langctl.com):
16
+ **pull** translations into JSON, Android, iOS or Flutter files, **push** new source strings,
17
+ and **check** in CI that committed files are up to date.
32
18
 
33
- ## Installation
34
-
35
- ```bash
36
- npm install -g langctl
19
+ ```console
20
+ $ langctl init # once per repo: pick a project, format and path → langctl.json
21
+ $ langctl pull # download translations
22
+ + src/locales/en.json created
23
+ + src/locales/es.json created 41/45 translated
24
+ $ langctl push # upload new strings from your source-language file
25
+ en src/locales/en.json 3 new, 0 updated, 42 unchanged
37
26
  ```
38
27
 
39
- Or use with npx (no installation required):
28
+ ## Install
40
29
 
41
30
  ```bash
31
+ npm install -g langctl # Node.js 20 or newer
32
+ # or, without installing:
42
33
  npx langctl --help
43
34
  ```
44
35
 
45
- ## Quick Start
46
-
47
- > **📌 First time here?** Check out our [Getting Started Guide](https://langctl.com/docs/getting-started/introduction) on the website for a complete walkthrough!
48
-
49
- ### 1. Get Your API Key
50
-
51
- 1. Visit **[app.langctl.com](https://app.langctl.com/signup)** and create a free account
52
- 2. Go to **Settings → API Keys**
53
- 3. Click **"Generate New Key"**
54
- 4. Copy the key (it's only shown once!)
55
-
56
- > **💡 Tip:** Learn more about API keys and authentication at [langctl.com/docs/cli/authentication](https://langctl.com/docs/cli/authentication)
57
-
58
- ### 2. Authenticate
59
-
60
- ```bash
61
- # Interactive setup
62
- langctl init
63
-
64
- # Or authenticate directly
65
- langctl auth lc_your_api_key_here
66
- ```
67
-
68
- ### 3. Explore Your Organization
69
-
70
- ```bash
71
- # View organization info
72
- langctl org info
73
-
74
- # Check statistics
75
- langctl org stats
76
-
77
- # List your projects
78
- langctl projects list
79
- ```
80
-
81
- ### 4. Start Working with Translations
82
-
83
- ```bash
84
- # List translation keys
85
- langctl keys list my-project
86
-
87
- # Export translations
88
- langctl export my-project --language en --format json
89
-
90
- # Create a new key
91
- langctl keys create my-project home.welcome \
92
- --value-en "Welcome!" \
93
- --value-es "¡Bienvenido!"
94
- ```
95
-
96
- ---
97
-
98
- ## Commands Overview
99
-
100
- ### Authentication
101
- - `langctl init` - Interactive setup wizard
102
- - `langctl auth <api-key>` - Authenticate with API key
103
- - `langctl logout` - Clear credentials
104
- - `langctl config` - View current configuration
105
-
106
- ### Organization
107
- - `langctl org info` - View organization details
108
- - `langctl org stats` - View organization statistics
109
- - `langctl org plan` - View subscription plan and limits
110
-
111
- ### Projects
112
- - `langctl projects list` - List all projects
113
- - `langctl projects create <name>` - Create new project
114
- - `langctl projects get <slug>` - Get project details
115
- - `langctl projects update <slug>` - Update project
116
- - `langctl projects delete <slug>` - Delete project
117
- - `langctl projects add-language <slug> <language>` - Add language
118
- - `langctl projects remove-language <slug> <language>` - Remove language
119
- - `langctl projects stats <slug>` - View project statistics
120
-
121
- ### Translation Keys
122
- - `langctl keys list <project>` - List translation keys
123
- - `langctl keys get <project> <key>` - Get key details
124
- - `langctl keys create <project> <key>` - Create new key
125
- - `langctl keys delete <project> <key>` - Delete key
126
- - `langctl keys translate <project> <key>` - Update translation
127
- - `langctl keys publish <project> <keys...>` - Publish/unpublish keys
128
-
129
- ### Team
130
- - `langctl team list` - List team members
131
- - `langctl team get <email>` - Get member details
132
- - `langctl team invite <email>` - Invite team member
133
- - `langctl team remove <email>` - Remove team member
134
- - `langctl team update-role <email> <role>` - Update member role
135
- - `langctl team invitations` - List invitations
136
- - `langctl team revoke-invitation <email>` - Revoke invitation
137
-
138
- ### Import/Export
139
- - `langctl export <project>` - Export translations
140
- - `langctl import <project> <file>` - Import translations
141
-
142
- ---
143
-
144
- ## Detailed Command Reference
145
-
146
- ### Authentication Commands
147
-
148
- #### `langctl init`
149
-
150
- Interactive setup wizard that guides you through authentication and configuration.
151
-
152
- ```bash
153
- langctl init
154
- ```
155
-
156
- #### `langctl auth <api-key>`
157
-
158
- Authenticate with an API key from your dashboard.
159
-
160
- ```bash
161
- langctl auth lc_abc123...
162
- ```
163
-
164
- #### `langctl logout`
165
-
166
- Clear stored authentication credentials.
167
-
168
- ```bash
169
- langctl logout
170
- ```
171
-
172
- #### `langctl config`
173
-
174
- Display current configuration (API key, organization, etc.).
175
-
176
- ```bash
177
- langctl config
178
- ```
179
-
180
- ---
181
-
182
- ### Organization Commands
183
-
184
- #### `langctl org info`
185
-
186
- View your organization details.
187
-
188
- ```bash
189
- langctl org info
190
- ```
191
-
192
- **Output:**
193
- - Organization name and ID
194
- - Slug
195
- - Subscription plan
196
- - Creation date
197
-
198
- #### `langctl org stats`
199
-
200
- View comprehensive organization statistics.
201
-
202
- ```bash
203
- langctl org stats
204
- ```
205
-
206
- **Output:**
207
- - Total team members
208
- - Number of projects
209
- - Translation key counts (total, published, unpublished)
210
- - Languages used across projects
211
- - API keys and webhooks
212
-
213
- #### `langctl org plan`
214
-
215
- View subscription plan details and resource limits.
216
-
217
- ```bash
218
- langctl org plan
219
- ```
220
-
221
- **Output:**
222
- - Current plan (Free, Pro, Team, Enterprise)
223
- - Max members allowed
224
- - Max projects allowed
225
- - Max keys per project
226
- - Max API keys allowed
227
-
228
- ---
229
-
230
- ### Project Management Commands
231
-
232
- #### `langctl projects list`
233
-
234
- List all projects you have access to.
235
-
236
- ```bash
237
- langctl projects list
238
- ```
239
-
240
- **Output:**
241
- - Project name and slug
242
- - Description
243
- - Supported languages
244
- - Default language
245
- - Available modules
246
-
247
- #### `langctl projects create <name>`
248
-
249
- Create a new translation project.
250
-
251
- ```bash
252
- langctl projects create "Mobile App" \
253
- --description "iOS and Android translations" \
254
- --languages en,es,fr,de \
255
- --default-language en
256
- ```
257
-
258
- **Options:**
259
- - `-d, --description <text>` - Project description
260
- - `-l, --languages <langs>` - Comma-separated language codes (default: `en`)
261
- - `--default-language <code>` - Default language (default: first language)
262
-
263
- **Examples:**
264
-
265
- ```bash
266
- # Simple project with English only
267
- langctl projects create "My App"
268
-
269
- # Multi-language project
270
- langctl projects create "Global App" \
271
- -l en,es,fr,de,ja \
272
- --default-language en
273
-
274
- # With description
275
- langctl projects create "Mobile App" \
276
- -d "Translation keys for mobile application" \
277
- -l en,es
278
- ```
279
-
280
- #### `langctl projects get <slug>`
281
-
282
- Get detailed information about a specific project.
283
-
284
- ```bash
285
- langctl projects get my-app
286
- ```
287
-
288
- **Output:**
289
- - Project name, ID, and slug
290
- - Description
291
- - All supported languages
292
- - Default language
293
- - List of modules
294
-
295
- #### `langctl projects update <slug>`
296
-
297
- Update project details.
298
-
299
- ```bash
300
- langctl projects update my-app \
301
- --name "New Name" \
302
- --description "Updated description" \
303
- --languages en,es,fr,de,ja \
304
- --default-language en
305
- ```
306
-
307
- **Options:**
308
- - `-n, --name <name>` - Update project name
309
- - `-d, --description <text>` - Update description
310
- - `-l, --languages <langs>` - Update supported languages
311
- - `--default-language <code>` - Update default language
312
-
313
- **Examples:**
314
-
315
- ```bash
316
- # Change project name
317
- langctl projects update my-app --name "Better Name"
318
-
319
- # Add more languages
320
- langctl projects update my-app -l en,es,fr,de,ja,zh
321
-
322
- # Update description only
323
- langctl projects update my-app -d "New project description"
324
- ```
325
-
326
- #### `langctl projects delete <slug>`
36
+ ## Quick start
327
37
 
328
- Delete a project (soft delete - can be recovered).
329
-
330
- ```bash
331
- langctl projects delete my-app
332
- ```
333
-
334
- **Warning:** This will mark the project as deleted. Contact support to recover deleted projects.
335
-
336
- #### `langctl projects add-language <slug> <language>`
337
-
338
- Add a new language to an existing project.
339
-
340
- ```bash
341
- langctl projects add-language my-app de
342
- ```
343
-
344
- **Examples:**
345
-
346
- ```bash
347
- # Add German
348
- langctl projects add-language my-app de
349
-
350
- # Add Japanese
351
- langctl projects add-language my-app ja
352
-
353
- # Add Chinese
354
- langctl projects add-language my-app zh
355
- ```
356
-
357
- #### `langctl projects remove-language <slug> <language>`
358
-
359
- Remove a language from a project.
360
-
361
- ```bash
362
- langctl projects remove-language my-app de
363
- ```
364
-
365
- **Note:** Cannot remove the default language. Change default language first if needed.
366
-
367
- #### `langctl projects stats <slug>`
368
-
369
- View project statistics.
370
-
371
- ```bash
372
- langctl projects stats my-app
373
- ```
374
-
375
- **Output:**
376
- - Total translation keys
377
- - Published vs unpublished counts
378
- - Number of modules
379
- - List of module names
380
-
381
- ---
382
-
383
- ### Translation Key Commands
384
-
385
- #### `langctl keys list <project>`
386
-
387
- List translation keys for a project.
388
-
389
- ```bash
390
- langctl keys list my-app
391
- ```
392
-
393
- **Options:**
394
- - `-m, --module <name>` - Filter by module
395
- - `-p, --published` - Show only published keys
396
- - `-s, --search <term>` - Search in key names
397
- - `--limit <number>` - Limit results (default: 100)
398
- - `--offset <number>` - Offset for pagination (default: 0)
399
-
400
- **Examples:**
401
-
402
- ```bash
403
- # List all keys
404
- langctl keys list my-app
405
-
406
- # Filter by module
407
- langctl keys list my-app --module auth
408
-
409
- # Show only published keys
410
- langctl keys list my-app --published
411
-
412
- # Search for specific keys
413
- langctl keys list my-app --search "welcome"
414
-
415
- # Pagination
416
- langctl keys list my-app --limit 50 --offset 0
417
- langctl keys list my-app --limit 50 --offset 50
418
- ```
419
-
420
- #### `langctl keys get <project> <key>`
421
-
422
- Get detailed information about a specific translation key.
423
-
424
- ```bash
425
- langctl keys get my-app home.welcome
426
- ```
427
-
428
- **Output:**
429
- - Key name and ID
430
- - Description
431
- - Module
432
- - Published status
433
- - All translations for all languages
434
-
435
- #### `langctl keys create <project> <key>`
436
-
437
- Create a new translation key with values for multiple languages.
438
-
439
- ```bash
440
- langctl keys create my-app home.welcome \
441
- --description "Welcome message on homepage" \
442
- --module home \
443
- --value-en "Welcome to our app!" \
444
- --value-es "¡Bienvenido a nuestra aplicación!" \
445
- --value-fr "Bienvenue dans notre application!" \
446
- --value-de "Willkommen in unserer App!"
447
- ```
448
-
449
- **Options:**
450
- - `-d, --description <text>` - Key description
451
- - `-m, --module <name>` - Module/namespace for organization
452
- - `--value-en <value>` - English translation
453
- - `--value-es <value>` - Spanish translation
454
- - `--value-fr <value>` - French translation
455
- - `--value-de <value>` - German translation
456
- - `--tags <tags>` - Comma-separated tags
457
-
458
- **Supported language options:**
459
- You can use `--value-{language}` for any language code in your project.
460
-
461
- **Examples:**
462
-
463
- ```bash
464
- # Simple key with one language
465
- langctl keys create my-app button.submit --value-en "Submit"
466
-
467
- # Multi-language key
468
- langctl keys create my-app home.title \
469
- --module home \
470
- --value-en "Home" \
471
- --value-es "Inicio" \
472
- --value-fr "Accueil"
473
-
474
- # With description and tags
475
- langctl keys create my-app error.network \
476
- --description "Network connection error" \
477
- --module errors \
478
- --value-en "Network error occurred" \
479
- --tags error,network
480
- ```
481
-
482
- #### `langctl keys delete <project> <key>`
483
-
484
- Delete a translation key.
485
-
486
- ```bash
487
- langctl keys delete my-app home.welcome
488
- ```
489
-
490
- #### `langctl keys translate <project> <key>`
491
-
492
- Update the translation value for a specific language.
493
-
494
- ```bash
495
- langctl keys translate my-app home.welcome \
496
- --language es \
497
- --value "¡Bienvenido!"
498
- ```
499
-
500
- **Options:**
501
- - `-l, --language <code>` - Language code (required)
502
- - `-v, --value <text>` - Translation value (required)
503
-
504
- **Examples:**
505
-
506
- ```bash
507
- # Update Spanish translation
508
- langctl keys translate my-app home.title -l es -v "Inicio"
509
-
510
- # Update French translation
511
- langctl keys translate my-app button.submit -l fr -v "Soumettre"
512
-
513
- # Add translation for new language
514
- langctl keys translate my-app home.welcome -l de -v "Willkommen"
515
- ```
516
-
517
- #### `langctl keys publish <project> <keys...>`
518
-
519
- Publish or unpublish translation keys.
520
-
521
- ```bash
522
- # Publish keys
523
- langctl keys publish my-app home.welcome home.title button.submit
524
-
525
- # Unpublish keys
526
- langctl keys publish my-app home.welcome --unpublish
527
- ```
528
-
529
- **Options:**
530
- - `--unpublish` - Unpublish instead of publish
531
-
532
- **Examples:**
533
-
534
- ```bash
535
- # Publish single key
536
- langctl keys publish my-app home.welcome
537
-
538
- # Publish multiple keys
539
- langctl keys publish my-app home.welcome home.title home.subtitle
540
-
541
- # Unpublish keys
542
- langctl keys publish my-app test.key --unpublish
543
- ```
544
-
545
- ---
546
-
547
- ### Team Management Commands
548
-
549
- #### `langctl team list`
550
-
551
- List all team members in your organization.
552
-
553
- ```bash
554
- langctl team list
555
- ```
556
-
557
- **Output:**
558
- - Member email
559
- - Role (viewer, member, admin, owner)
560
- - Join date
561
-
562
- #### `langctl team get <email>`
563
-
564
- Get details about a specific team member.
565
-
566
- ```bash
567
- langctl team get user@example.com
568
- ```
569
-
570
- #### `langctl team invite <email>`
571
-
572
- Invite a new team member.
573
-
574
- ```bash
575
- langctl team invite user@example.com --role member
576
- ```
577
-
578
- **Options:**
579
- - `-r, --role <role>` - Member role (default: `member`)
580
- - `viewer` - Read-only access
581
- - `member` - Can manage translations
582
- - `admin` - Full project and team management
583
-
584
- **Examples:**
585
-
586
- ```bash
587
- # Invite as member (default)
588
- langctl team invite user@example.com
589
-
590
- # Invite as admin
591
- langctl team invite admin@example.com --role admin
592
-
593
- # Invite as viewer
594
- langctl team invite viewer@example.com --role viewer
595
- ```
38
+ 1. Create an API key at **[app.langctl.com → API Keys](https://app.langctl.com/organization/api-keys)** (it is shown only once).
39
+ 2. In your repository:
596
40
 
597
- #### `langctl team remove <email>`
598
-
599
- Remove a team member from your organization.
600
-
601
- ```bash
602
- langctl team remove user@example.com
603
- ```
604
-
605
- **Note:** Cannot remove the organization owner. Cannot remove yourself (use appropriate UI for that).
606
-
607
- #### `langctl team update-role <email> <role>`
608
-
609
- Update a team member's role.
610
-
611
- ```bash
612
- langctl team update-role user@example.com admin
613
- ```
614
-
615
- **Valid roles:** `viewer`, `member`, `admin`
616
-
617
- **Examples:**
618
-
619
- ```bash
620
- # Promote to admin
621
- langctl team update-role user@example.com admin
622
-
623
- # Demote to viewer
624
- langctl team update-role user@example.com viewer
625
- ```
626
-
627
- #### `langctl team invitations`
628
-
629
- List all invitations (pending, accepted, or cancelled).
630
-
631
- ```bash
632
- # List all invitations
633
- langctl team invitations
634
-
635
- # List only pending invitations
636
- langctl team invitations --pending
637
- ```
638
-
639
- **Options:**
640
- - `-p, --pending` - Show only pending invitations
641
-
642
- #### `langctl team revoke-invitation <email>`
643
-
644
- Revoke a pending invitation.
645
-
646
- ```bash
647
- langctl team revoke-invitation user@example.com
648
- ```
649
-
650
- ---
651
-
652
- ### Export/Import Commands
653
-
654
- #### `langctl export <project>`
655
-
656
- Export translations in various formats.
657
-
658
- ```bash
659
- langctl export my-app --language en --format flat-json
660
- ```
661
-
662
- **Options:**
663
- - `-l, --language <code>` - Language to export (default: exports all languages)
664
- - `-f, --format <type>` - Export format (default: `flat-json`)
665
- - `-o, --output <path>` - Output file path (optional)
666
- - `-m, --module <name>` - Export only specific module
667
- - `--include-unpublished` - Include unpublished keys (default: published only)
668
-
669
- **Supported formats:**
670
- - `flat-json` - Flat key-value JSON (default)
671
- - `nested-json` - Nested JSON structure
672
- - `i18n-json` - i18next compatible format
673
- - `android-xml` - Android strings.xml format
674
- - `ios-strings` - iOS Localizable.strings format
675
- - `flutter-arb` - Flutter ARB format
676
-
677
- **Examples:**
678
-
679
- ```bash
680
- # Export single language as JSON
681
- langctl export my-app -l en -f flat-json
682
-
683
- # Export all languages
684
- langctl export my-app
685
-
686
- # Export for iOS
687
- langctl export my-app -l en -f ios-strings -o ./ios/en.lproj/Localizable.strings
688
-
689
- # Export for Android
690
- langctl export my-app -l es -f android-xml -o ./android/res/values-es/strings.xml
691
-
692
- # Export for Flutter
693
- langctl export my-app -l fr -f flutter-arb -o ./lib/l10n/app_fr.arb
694
-
695
- # Export specific module
696
- langctl export my-app -l en --module auth
697
-
698
- # Include unpublished translations
699
- langctl export my-app -l en --include-unpublished
700
- ```
701
-
702
- #### `langctl import <project> <file>`
703
-
704
- Import translations from a JSON file.
705
-
706
- ```bash
707
- langctl import my-app translations.json --language en
708
- ```
709
-
710
- **Options:**
711
- - `-l, --language <code>` - Target language (required)
712
- - `--overwrite` - Overwrite existing translations
713
- - `--publish` - Auto-publish imported keys
714
-
715
- **Examples:**
716
-
717
- ```bash
718
- # Import English translations
719
- langctl import my-app en.json -l en
720
-
721
- # Import and overwrite existing
722
- langctl import my-app en.json -l en --overwrite
723
-
724
- # Import and auto-publish
725
- langctl import my-app en.json -l en --publish
726
-
727
- # Import multiple languages
728
- langctl import my-app en.json -l en --publish
729
- langctl import my-app es.json -l es --publish
730
- langctl import my-app fr.json -l fr --publish
731
- ```
732
-
733
- **Supported JSON formats:**
734
-
735
- ```json
736
- // Flat format (recommended)
737
- {
738
- "home.welcome": "Welcome!",
739
- "home.subtitle": "Get started",
740
- "button.submit": "Submit"
741
- }
742
-
743
- // Nested format (auto-flattened)
744
- {
745
- "home": {
746
- "welcome": "Welcome!",
747
- "subtitle": "Get started"
748
- },
749
- "button": {
750
- "submit": "Submit"
751
- }
752
- }
753
- ```
754
-
755
- ---
756
-
757
- ## Export Format Examples
758
-
759
- ### Flat JSON (Default)
760
-
761
- ```json
762
- {
763
- "home.welcome": "Welcome!",
764
- "home.subtitle": "Get started with {{appName}}",
765
- "button.submit": "Submit"
766
- }
767
- ```
768
-
769
- ### Nested JSON
770
-
771
- ```json
772
- {
773
- "home": {
774
- "welcome": "Welcome!",
775
- "subtitle": "Get started with {{appName}}"
776
- },
777
- "button": {
778
- "submit": "Submit"
779
- }
780
- }
781
- ```
782
-
783
- ### i18next JSON
784
-
785
- ```json
786
- {
787
- "home": {
788
- "welcome": "Welcome!",
789
- "subtitle": "Get started with {{appName}}"
790
- }
791
- }
792
- ```
793
-
794
- ### iOS Strings
795
-
796
- ```
797
- /* Welcome message */
798
- "home.welcome" = "Welcome!";
799
-
800
- /* Homepage subtitle with app name placeholder */
801
- "home.subtitle" = "Get started with %@";
802
- ```
803
-
804
- ### Android XML
805
-
806
- ```xml
807
- <?xml version="1.0" encoding="utf-8"?>
808
- <resources>
809
- <!-- Welcome message -->
810
- <string name="home.welcome">Welcome!</string>
811
-
812
- <!-- Homepage subtitle with app name placeholder -->
813
- <string name="home.subtitle">Get started with %1$s</string>
814
- </resources>
815
- ```
816
-
817
- ### Flutter ARB
818
-
819
- ```json
820
- {
821
- "@@locale": "en",
822
- "home.welcome": "Welcome!",
823
- "@home.welcome": {
824
- "description": "Welcome message"
825
- },
826
- "home.subtitle": "Get started with {appName}",
827
- "@home.subtitle": {
828
- "description": "Homepage subtitle",
829
- "placeholders": {
830
- "appName": {
831
- "type": "String"
832
- }
833
- }
834
- }
835
- }
836
- ```
837
-
838
- ---
839
-
840
- ## Real-World Workflows
841
-
842
- ### Complete Project Setup
843
-
844
- ```bash
845
- # 1. Authenticate
846
- langctl auth lc_your_api_key_here
847
-
848
- # 2. Create project
849
- langctl projects create "Mobile App" \
850
- -l en,es,fr,de \
851
- --default-language en \
852
- -d "iOS and Android application"
853
-
854
- # 3. Add translation keys
855
- langctl keys create mobile-app home.welcome \
856
- --module home \
857
- --value-en "Welcome!" \
858
- --value-es "¡Bienvenido!" \
859
- --value-fr "Bienvenue!"
860
-
861
- langctl keys create mobile-app button.submit \
862
- --module common \
863
- --value-en "Submit" \
864
- --value-es "Enviar" \
865
- --value-fr "Soumettre"
866
-
867
- # 4. Publish keys
868
- langctl keys publish mobile-app home.welcome button.submit
869
-
870
- # 5. Export for platforms
871
- langctl export mobile-app -l en -f ios-strings -o ./ios/en.lproj/
872
- langctl export mobile-app -l en -f android-xml -o ./android/res/values/
873
- ```
874
-
875
- ### Bulk Import Workflow
876
-
877
- ```bash
878
- # 1. Prepare JSON files (en.json, es.json, fr.json)
879
- # 2. Import all languages
880
- langctl import my-app en.json -l en --publish
881
- langctl import my-app es.json -l es --publish
882
- langctl import my-app fr.json -l fr --publish
883
-
884
- # 3. Verify imports
885
- langctl keys list my-app --published
886
- langctl projects stats my-app
887
- ```
888
-
889
- ### Team Collaboration
890
-
891
- ```bash
892
- # 1. Invite team members
893
- langctl team invite developer@example.com --role member
894
- langctl team invite manager@example.com --role admin
895
-
896
- # 2. Check invitations
897
- langctl team invitations --pending
898
-
899
- # 3. Manage roles
900
- langctl team update-role developer@example.com admin
901
-
902
- # 4. View team
903
- langctl team list
904
- ```
905
-
906
- ### Multi-Platform Export
907
-
908
- ```bash
909
- # Export for all platforms
910
- PROJECT="my-app"
911
- LANG="en"
912
-
913
- # Web (i18next)
914
- langctl export $PROJECT -l $LANG -f i18n-json -o ./src/locales/$LANG.json
915
-
916
- # iOS
917
- langctl export $PROJECT -l $LANG -f ios-strings -o ./ios/$LANG.lproj/Localizable.strings
918
-
919
- # Android
920
- langctl export $PROJECT -l $LANG -f android-xml -o ./android/res/values/strings.xml
41
+ ```bash
42
+ langctl init
43
+ ```
921
44
 
922
- # Flutter
923
- langctl export $PROJECT -l $LANG -f flutter-arb -o ./lib/l10n/app_$LANG.arb
924
- ```
45
+ This asks for the key (input is hidden), lets you pick the project, file format and location,
46
+ and writes a `langctl.json` you can commit. Then:
925
47
 
926
- ---
48
+ ```bash
49
+ langctl pull # write translation files
50
+ langctl push # upload new keys from your source language file
51
+ ```
927
52
 
928
- ## CI/CD Integration
53
+ Keys are only included in `pull` once they are **published** — drafts stay out of your app until
54
+ someone reviews them (`--include-drafts` to override).
929
55
 
930
- ### GitHub Actions
56
+ ## Use it in CI
931
57
 
932
- Automated translation sync workflow:
58
+ No config file or login step is needed — set the key as a secret environment variable.
933
59
 
934
60
  ```yaml
935
- name: Sync Translations
936
-
937
- on:
938
- schedule:
939
- - cron: '0 0 * * *' # Daily at midnight
940
- workflow_dispatch: # Manual trigger
941
-
61
+ # .github/workflows/i18n.yml
62
+ name: translations
63
+ on: [pull_request]
942
64
  jobs:
943
- sync-translations:
65
+ i18n:
944
66
  runs-on: ubuntu-latest
945
67
  steps:
946
- - uses: actions/checkout@v3
947
-
948
- - name: Setup Node.js
949
- uses: actions/setup-node@v3
950
- with:
951
- node-version: '18'
952
-
953
- - name: Install Langctl
954
- run: npm install -g langctl
955
-
956
- - name: Authenticate
68
+ - uses: actions/checkout@v4
69
+ - uses: actions/setup-node@v4
70
+ with: { node-version: 22 }
71
+ - run: npx langctl@0 pull --check # fail if committed files are out of date
957
72
  env:
958
73
  LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }}
959
- run: langctl auth $LANGCTL_API_KEY
960
-
961
- - name: Export Translations
962
- run: |
963
- langctl export my-project -l en -f i18n-json -o ./locales/en.json
964
- langctl export my-project -l es -f i18n-json -o ./locales/es.json
965
- langctl export my-project -l fr -f i18n-json -o ./locales/fr.json
966
-
967
- - name: Commit Changes
968
- run: |
969
- git config user.name "Langctl Bot"
970
- git config user.email "bot@langctl.com"
971
- git add locales/
972
- git diff --staged --quiet || git commit -m "chore: update translations [skip ci]"
973
- git push
974
- ```
975
-
976
- ### GitLab CI
977
-
978
- ```yaml
979
- sync-translations:
980
- image: node:18
981
- script:
982
- - npm install -g langctl
983
- - langctl auth $LANGCTL_API_KEY
984
- - langctl export my-project -l en -f json -o ./locales/en.json
985
- - langctl export my-project -l es -f json -o ./locales/es.json
986
- - git config user.name "Langctl Bot"
987
- - git config user.email "bot@langctl.com"
988
- - git add locales/
989
- - git diff --staged --quiet || git commit -m "chore: update translations"
990
- - git push origin $CI_COMMIT_BRANCH
991
- only:
992
- - schedules
993
74
  ```
994
75
 
995
- ### Docker
996
-
997
- ```dockerfile
998
- FROM node:18-alpine
999
-
1000
- RUN npm install -g langctl
1001
-
1002
- WORKDIR /app
1003
-
1004
- COPY . .
1005
-
1006
- # Set API key via environment variable
1007
- ENV LANGCTL_API_KEY=""
1008
-
1009
- # Example: Export translations on build
1010
- RUN langctl auth $LANGCTL_API_KEY && \
1011
- langctl export my-project -l en -f json -o ./public/locales/en.json
1012
- ```
1013
-
1014
- ---
1015
-
1016
- ## Best Practices
1017
-
1018
- ### Project Organization
1019
-
1020
- 1. **Use modules** to organize keys by feature:
1021
- ```bash
1022
- langctl keys create app auth.login.title --module auth
1023
- langctl keys create app home.hero.title --module home
1024
- langctl keys create app settings.profile.name --module settings
1025
- ```
1026
-
1027
- 2. **Follow naming conventions**:
1028
- ```
1029
- module.screen.element
1030
- auth.login.title
1031
- home.hero.subtitle
1032
- ```
1033
-
1034
- 3. **Add descriptions** to keys:
1035
- ```bash
1036
- langctl keys create app button.submit \
1037
- --description "Primary action button across the app" \
1038
- --value-en "Submit"
1039
- ```
1040
-
1041
- ### Translation Workflow
1042
-
1043
- 1. **Create keys unpublished** (draft mode)
1044
- 2. **Add translations** for all languages
1045
- 3. **Review and test** translations
1046
- 4. **Publish** when ready:
1047
- ```bash
1048
- langctl keys publish my-app key1 key2 key3
1049
- ```
1050
-
1051
- 5. **Export** published translations only:
1052
- ```bash
1053
- langctl export my-app -l en
1054
- ```
1055
-
1056
- ### Team Management
1057
-
1058
- 1. **Use appropriate roles**:
1059
- - `viewer` - Stakeholders, reviewers (read-only)
1060
- - `member` - Translators, content writers
1061
- - `admin` - Project managers, team leads
1062
-
1063
- 2. **Regular access reviews**:
1064
- ```bash
1065
- langctl team list
1066
- langctl team invitations
1067
- ```
1068
-
1069
- ### Security
76
+ Other useful CI commands:
1070
77
 
1071
- 1. **Never commit API keys** to version control
1072
- 2. **Use environment variables**:
1073
- ```bash
1074
- export LANGCTL_API_KEY="lc_..."
1075
- langctl auth $LANGCTL_API_KEY
1076
- ```
1077
-
1078
- 3. **Rotate keys periodically** from [app.langctl.com](https://app.langctl.com)
1079
-
1080
- 4. **Use different keys** for different environments:
1081
- - Development key for local work
1082
- - CI/CD key for automated workflows
1083
- - Production key for releases
1084
-
1085
- ---
1086
-
1087
- ## Troubleshooting
1088
-
1089
- ### Authentication Issues
1090
-
1091
- **"Not authenticated" error:**
1092
78
  ```bash
1093
- # Solution: Authenticate with your API key
1094
- langctl auth lc_your_api_key_here
79
+ langctl push --dry-run --json # preview what a push would change
80
+ langctl push --publish # upload and publish new source strings (e.g. on main)
81
+ langctl pull --require-complete # fail if any language is missing translations
82
+ langctl whoami --json # verify the key, org and scopes
1095
83
  ```
1096
84
 
1097
- **"Invalid API key" error:**
1098
- - Verify key format (starts with `lc_`, 67 characters total)
1099
- - Check key hasn't been revoked at [app.langctl.com](https://app.langctl.com)
1100
- - Generate a new key if needed
85
+ In CI `langctl` never prompts and never animates: spinners are off when `CI` is set or output
86
+ isn't a terminal, destructive commands require `--yes`, and every failure has a distinct
87
+ [exit code](#exit-codes). Use a key with only the scopes the job needs — `translations:read`
88
+ is enough for `pull`.
1101
89
 
1102
- ### Project Issues
90
+ ## `langctl.json`
1103
91
 
1104
- **"Project not found" error:**
1105
- ```bash
1106
- # Check project slug (not name)
1107
- langctl projects list
1108
-
1109
- # Use the slug shown in the list
1110
- langctl keys list correct-slug-here
92
+ ```json
93
+ {
94
+ "project": "web-app",
95
+ "format": "json",
96
+ "output": "src/locales/{lang}.json"
97
+ }
1111
98
  ```
1112
99
 
1113
- **Cannot remove language:**
1114
- - Cannot remove the default language
1115
- - Change default language first:
1116
- ```bash
1117
- langctl projects update my-app --default-language en
1118
- langctl projects remove-language my-app fr
1119
- ```
1120
-
1121
- ### Import/Export Issues
1122
-
1123
- **Import fails with format error:**
1124
- - Ensure JSON is valid
1125
- - Use flat key-value format or nested objects
1126
- - Check language code is valid
1127
-
1128
- **Export shows no keys:**
1129
- - Verify keys are published:
1130
- ```bash
1131
- langctl keys list my-app --published
1132
- ```
1133
- - Or include unpublished:
1134
- ```bash
1135
- langctl export my-app -l en --include-unpublished
1136
- ```
1137
-
1138
- ### Connection Issues
1139
-
1140
- If experiencing connectivity problems:
1141
- 1. Check your internet connection
1142
- 2. Verify you're not behind a restrictive firewall
1143
- 3. Try again in a few moments
1144
- 4. Contact support if issue persists
1145
-
1146
- ---
1147
-
1148
- ## FAQ
1149
-
1150
- **Q: What's the difference between slug and name?**
1151
- A: The slug is the URL-friendly identifier (e.g., `my-app`), while the name is the display name (e.g., "My App"). Use slugs in CLI commands.
1152
-
1153
- **Q: Can I use the CLI without installing it?**
1154
- A: Yes! Use `npx langctl` instead of `langctl` for any command.
1155
-
1156
- **Q: How do I get my project slug?**
1157
- A: Run `langctl projects list` to see all project slugs.
1158
-
1159
- **Q: Can I export multiple languages at once?**
1160
- A: Yes, run `langctl export my-app` without `-l` flag to export all languages.
1161
-
1162
- **Q: What happens to unpublished keys?**
1163
- A: They're excluded from exports by default. Use `--include-unpublished` to include them.
1164
-
1165
- **Q: Can I undo a delete operation?**
1166
- A: Projects and keys use soft deletes. Contact support to recover deleted items.
1167
-
1168
- **Q: How do I change my default language?**
1169
- A: Run: `langctl projects update my-app --default-language <new-language>`
1170
-
1171
- **Q: Can I use the CLI in CI/CD?**
1172
- A: Yes! Store your API key as a secret and use it in your workflow. See [CI/CD Integration](#cicd-integration).
1173
-
1174
- ---
1175
-
1176
- ## Platform Support
1177
-
1178
- - ✅ macOS (Apple Silicon & Intel)
1179
- - ✅ Linux (x64, ARM64)
1180
- - ✅ Windows (x64, ARM64)
1181
- - ✅ Node.js 16.0.0 or higher
1182
-
1183
- ---
1184
-
1185
- ## 🌟 Why Langctl?
1186
-
1187
- Tired of expensive translation management tools? Langctl offers:
1188
- - ✅ **Free to start** - No credit card required
1189
- - ✅ **90% cheaper** than enterprise alternatives
1190
- - ✅ **Developer-first** - Built for your workflow
1191
- - ✅ **AI-powered** - Context-aware translations
1192
- - ✅ **Open source CLI** - Inspect, fork, contribute
1193
-
1194
- **[See pricing and compare features →](https://langctl.com/pricing)**
1195
-
1196
- ---
1197
-
1198
- ## 📚 Resources & Support
1199
-
1200
- - 🌐 **[Website](https://langctl.com)** - Features, pricing, and comparisons
1201
- - 📖 **[Full Documentation](https://langctl.com/docs)** - Guides, tutorials, and best practices
1202
- - 🎯 **[Quick Start Tutorial](https://langctl.com/docs/getting-started/quickstart)** - Step-by-step walkthrough
1203
- - 💡 **[Integration Guides](https://langctl.com/docs/integrations/overview)** - React, Angular, Vue, iOS, Android, Flutter
1204
- - 📊 **[Dashboard](https://app.langctl.com)** - Manage translations visually
1205
- - 💬 **[Get Support](https://langctl.com/contact)** - Email us at [hello@langctl.com](mailto:hello@langctl.com)
1206
- - 🐛 **[Report Issues](https://github.com/siddharthsaxena0/langctl/issues)** - Bug reports and feature requests
1207
-
1208
- ---
1209
-
1210
- ## Contributing
1211
-
1212
- We welcome contributions! This is an open-source project and we'd love your help making it better.
1213
-
1214
- ---
100
+ | Field | Description |
101
+ | --- | --- |
102
+ | `project` | Project slug (`langctl projects list`). |
103
+ | `format` | `json` (default), `nested-json`, `android`, `ios`, `arb` — see below. |
104
+ | `output` | Path template, relative to `langctl.json`. Variables: `{lang}` (`pt-BR`), `{lang_}` (`pt_BR`), `{android}` (`values`, `values-pt-rBR`). |
105
+ | `languages` | Languages to pull (default: all project languages). |
106
+ | `sourceLanguage` | Language `push` uploads by default (default: the project's default language). |
107
+ | `includeDrafts` | Pull unpublished keys too (default `false`). |
108
+ | `module` | Only pull/push keys in this module. |
109
+
110
+ Command-line flags override the file; `langctl` looks for `langctl.json` in the current directory and its parents.
111
+
112
+ ## Formats
113
+
114
+ | Format | Default path | Notes |
115
+ | --- | --- | --- |
116
+ | `json` | `locales/{lang}.json` | Flat `{"home.title": "…"}` (i18next, vue-i18n, …). Import also accepts nested JSON. |
117
+ | `nested-json` | `locales/{lang}.json` | `{"home": {"title": "…"}}`. Fails if a key is both a string and a parent. |
118
+ | `android` | `res/{android}/strings.xml` | Names become valid resources (`home.title` → `home_title`); `{{name}}` → `%1$s`. |
119
+ | `ios` | `{lang}.lproj/Localizable.strings` | `{{name}}` → `%1$@`. |
120
+ | `arb` | `lib/l10n/app_{lang_}.arb` | Flutter; ids become Dart identifiers (`home.title` → `homeTitle`); placeholders declared. |
121
+
122
+ Placeholders are stored as `{{name}}`. A placeholder used twice gets the same position on every
123
+ platform, and literal `%` is escaped where needed. Output is sorted and has no timestamps, so a
124
+ pull only changes files when translations change. If two keys map to the same Android/ARB name,
125
+ the pull fails and names both keys instead of silently dropping one.
126
+
127
+ ## Commands
128
+
129
+ | Command | Description |
130
+ | --- | --- |
131
+ | `langctl init` | Set up a repo (auth if needed, write `langctl.json`). Flags: `--project --format --output --force`. |
132
+ | `langctl pull [project]` | Download translations. `-l/--languages`, `-f/--format`, `-o/--output`, `-m/--module`, `--include-drafts`, `--include-unreviewed`, `--check`, `--dry-run`, `--require-complete`. AI translations nobody has reviewed are left out (with a warning) until approved. |
133
+ | `langctl push [project]` | Upload files. Default: source language only; `-l all` for every language. `--overwrite`, `--publish`, `--dry-run`, `-i/--input`. |
134
+ | `langctl export [project]` | One-off export: `-l es -f android -o strings.xml`. |
135
+ | `langctl import [project] <file>` | One-off import of a single file: `-l es`, `--overwrite`, `--publish`, `--dry-run`. |
136
+ | `langctl translate [project]` | Fill missing translations with AI (DeepL) from the default language. `-t/--to es,fr`, `-k/--keys`, `-m/--module`, `--overwrite`, `--dry-run` (no quota used). Placeholders like `{{name}}` are kept as-is; uses the plan's monthly AI translations, or the org's own DeepL key if one is saved. |
137
+ | `langctl review [project]` | List AI translations awaiting review; `--approve` approves them (narrow with `-k/--keys`, `-l/--languages`; `--yes` in CI). Editing a translation in the web app also counts as reviewing it. |
138
+ | `langctl auth [--stdin]` | Store an API key in `~/.langctl/config.json` (mode 600). `echo "$KEY" \| langctl auth --stdin`. |
139
+ | `langctl whoami` | Show org, key source, scopes and API latency. |
140
+ | `langctl logout` · `langctl config` · `langctl formats` | Remove the stored key · show effective config · list formats. |
141
+ | `langctl projects list\|get\|create\|update\|delete\|add-language\|remove-language\|stats` | Manage projects. `stats` shows translation coverage per language. |
142
+ | `langctl keys list\|get\|create\|update\|translate\|delete\|publish\|unpublish` | Manage keys, e.g. `keys create web home.title --value en="Welcome" --value es="Bienvenido" --publish`. |
143
+ | `langctl team list\|invite\|remove\|update-role\|invitations\|revoke-invitation` | Team management (key needs the `org:admin` scope). |
144
+ | `langctl org info\|stats\|plan` | Organization details, usage and plan limits. |
145
+
146
+ Global flags (any position): `--json` · `-q/--quiet` · `--verbose` (log HTTP requests) · `-y/--yes` ·
147
+ `--api-key` · `--api-url` · `--timeout <seconds>` · `--no-color`. Run `langctl <command> --help` for details.
148
+
149
+ ## Configuration & environment
150
+
151
+ | Variable | Purpose |
152
+ | --- | --- |
153
+ | `LANGCTL_API_KEY` | API key (takes precedence over the stored key). |
154
+ | `LANGCTL_API_URL` | API base URL (default `https://api.langctl.com/api/v1`). |
155
+ | `LANGCTL_TIMEOUT` | Request timeout in seconds (default 30). Idempotent requests are retried with backoff on network errors, 429 and 5xx. |
156
+ | `LANGCTL_CONFIG_DIR` | Where the user config lives (default `~/.langctl`). |
157
+ | `LANGCTL_NO_CACHE` | Set to `1` to disable the export cache (`~/.langctl/cache`). `pull` normally sends the last ETag and reuses the cached snapshot when the server answers 304 Not Modified. |
158
+ | `NO_COLOR` / `CI` | Disable colors / force non-interactive mode. |
159
+ | `NODE_EXTRA_CA_CERTS` | Trust a corporate proxy's CA. |
160
+
161
+ ## Exit codes
162
+
163
+ | Code | Meaning |
164
+ | --- | --- |
165
+ | 0 | Success |
166
+ | 1 | Error (including a cancelled confirmation, `--require-complete` failures) |
167
+ | 2 | Invalid usage — bad flag, unknown language/format, refused without `--yes` |
168
+ | 3 | Not authenticated, invalid/revoked key, or missing permission (scope) |
169
+ | 4 | Project, key, member or file not found |
170
+ | 5 | Network error, timeout, or the API is unavailable |
171
+ | 6 | Plan limit reached |
172
+ | 7 | `pull --check`: files are out of date |
173
+
174
+ With `--json`, errors are also printed to stdout as `{"error": {"message", "exitCode", "hint"}}`.
175
+
176
+ ## Upgrading from 0.2
177
+
178
+ - Requires **Node.js 20+** (0.2 already needed it in practice).
179
+ - `pull`/`export` default paths follow each platform's layout (`locales/{lang}.json`,
180
+ `res/values-xx/strings.xml`, `xx.lproj/…`, `app_xx.arb`). Use `-o` or `langctl.json` to keep your old paths.
181
+ - Exporting several languages to one file is now an error instead of silently overwriting it.
182
+ - Android and ARB output now uses valid identifiers (`home_title`, `homeTitle`) and correct escaping.
183
+ - Destructive commands (`projects delete`, `keys delete`, removing languages, `team remove`) ask for
184
+ confirmation, and require `--yes` when not run interactively.
185
+ - `keys translate` takes the text with `-t/--text` (`-v` is the version flag); `keys create`
186
+ accepts `--value LANG=TEXT` for any language. The `debug` command was replaced by `whoami`.
187
+ - `team` commands need an API key with the `org:admin` scope.
188
+
189
+ See [CHANGELOG.md](CHANGELOG.md) for everything else.
190
+
191
+ ## Development
192
+
193
+ ```bash
194
+ npm ci
195
+ npm test # unit tests
196
+ npm run build
197
+ LANGCTL_API_KEY=lc_… LANGCTL_E2E_PROJECT=<test-project> npm run test:e2e # against a real API
198
+ ```
1215
199
 
1216
200
  ## License
1217
201
 
1218
- MIT License - see [LICENSE](LICENSE) file for details.
1219
-
1220
- Copyright © 2026 Litcode Private Limited. All rights reserved.
1221
-
1222
- ---
1223
-
1224
- <div align="center">
1225
-
1226
- **Built with ❤️ by the Langctl team**
1227
-
1228
- *Making translation management simple, fast, and developer-friendly.*
1229
-
1230
- **[Get Started Free →](https://app.langctl.com/signup)**
1231
-
1232
- </div>
202
+ MIT