@abdess76/i18nkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +309 -0
  4. package/bin/cli.js +48 -0
  5. package/bin/commands/apply.js +48 -0
  6. package/bin/commands/check-sync.js +35 -0
  7. package/bin/commands/extract-utils.js +216 -0
  8. package/bin/commands/extract.js +198 -0
  9. package/bin/commands/find-orphans.js +36 -0
  10. package/bin/commands/help.js +34 -0
  11. package/bin/commands/index.js +79 -0
  12. package/bin/commands/translate.js +51 -0
  13. package/bin/commands/version.js +17 -0
  14. package/bin/commands/watch.js +34 -0
  15. package/bin/core/applier-utils.js +144 -0
  16. package/bin/core/applier.js +165 -0
  17. package/bin/core/args.js +147 -0
  18. package/bin/core/backup.js +74 -0
  19. package/bin/core/command-interface.js +69 -0
  20. package/bin/core/config.js +108 -0
  21. package/bin/core/context.js +86 -0
  22. package/bin/core/detector.js +152 -0
  23. package/bin/core/file-walker.js +159 -0
  24. package/bin/core/fs-adapter.js +56 -0
  25. package/bin/core/help-generator.js +208 -0
  26. package/bin/core/index.js +63 -0
  27. package/bin/core/json-utils.js +213 -0
  28. package/bin/core/key-generator.js +75 -0
  29. package/bin/core/log-utils.js +26 -0
  30. package/bin/core/orphan-finder.js +208 -0
  31. package/bin/core/parser-utils.js +187 -0
  32. package/bin/core/paths.js +60 -0
  33. package/bin/core/plugin-interface.js +83 -0
  34. package/bin/core/plugin-resolver-utils.js +166 -0
  35. package/bin/core/plugin-resolver.js +211 -0
  36. package/bin/core/sync-checker-utils.js +99 -0
  37. package/bin/core/sync-checker.js +199 -0
  38. package/bin/core/translator.js +197 -0
  39. package/bin/core/types.js +297 -0
  40. package/bin/core/watcher.js +119 -0
  41. package/bin/plugins/adapter-transloco.js +156 -0
  42. package/bin/plugins/parser-angular.js +56 -0
  43. package/bin/plugins/parser-primeng.js +79 -0
  44. package/bin/plugins/parser-typescript.js +66 -0
  45. package/bin/plugins/provider-deepl.js +65 -0
  46. package/bin/plugins/provider-mymemory.js +192 -0
  47. package/package.json +123 -0
  48. package/types/index.d.ts +85 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to
7
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
+
9
+ ## [1.0.0] - 01-12-2025
10
+
11
+ ### Added
12
+
13
+ - Initial release
14
+ - Zero-dependency CLI for i18n extraction
15
+ - Angular + Transloco support with PrimeNG patterns
16
+ - Auto-apply feature to replace hardcoded strings
17
+ - Sync check to compare language files
18
+ - Orphan key detection
19
+ - Translation API integration (MyMemory + DeepL)
20
+ - Watch mode for development
21
+ - CI mode with strict validation
22
+ - Configuration file support (i18nkit.config.js, i18nkit.config.json)
23
+ - Custom key mapping (.i18n-keys.json)
24
+ - Backup system before modifications
25
+ - Interactive mode for confirmations
26
+ - Dry-run mode for previews
27
+ - Flat and nested output formats
28
+ - ICU message format support
29
+ - ES2024 features (Array.fromAsync, Promise.withResolvers, Set.difference)
30
+
31
+ ### Technical
32
+
33
+ - Pure Node.js implementation (no external dependencies)
34
+ - Requires Node.js >= 22.0.0
35
+ - Uses modern JavaScript patterns and optimizations
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Abdessamad DERRAZ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,309 @@
1
+ # i18nkit
2
+
3
+ ![i18nkit](assets/logo-with-title.png)
4
+
5
+ [![npm](https://img.shields.io/npm/v/i18nkit.svg)](https://npmjs.com/package/i18nkit)
6
+ [![node](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](https://nodejs.org)
7
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+ [![zero-deps](https://img.shields.io/badge/dependencies-0-blue.svg)](package.json)
9
+ [![docs](https://img.shields.io/badge/API-docs-blue.svg)](https://abdess.github.io/i18nkit/)
10
+
11
+ ---
12
+
13
+ Universal i18n toolkit: extract translation keys, sync language files, detect
14
+ missing translations. Zero dependencies, extensible via plugins.
15
+
16
+ ## Why i18nkit?
17
+
18
+ Managing translations is tedious: find hardcoded strings, create translation
19
+ keys, keep language files in sync. **i18nkit automates it.**
20
+
21
+ | Problem | Solution |
22
+ | -------------------------------- | ------------------------------ |
23
+ | Hardcoded strings in templates | Auto-extract translatable text |
24
+ | Manual pipe/function replacement | Auto-apply in one command |
25
+ | Missing translations across lang | Sync check with CI integration |
26
+ | Unused keys bloating bundles | Orphan detection to clean up |
27
+ | Manual translation copy-paste | API translation (free + pro) |
28
+
29
+ ## Current Support
30
+
31
+ - **Angular + Transloco** - Full support out of the box
32
+ - **PrimeNG components** - Auto-extraction of translatable attributes
33
+
34
+ Extensible via plugins for other frameworks and libraries.
35
+
36
+ ## Quick Start
37
+
38
+ ### 1. Install
39
+
40
+ ```bash
41
+ npm install --save-dev i18nkit
42
+ ```
43
+
44
+ ### 2. Add scripts to package.json
45
+
46
+ ```json
47
+ {
48
+ "scripts": {
49
+ "i18n": "i18nkit --lang fr --merge",
50
+ "i18n:apply": "i18nkit --auto-apply --init-langs en,fr",
51
+ "i18n:check": "i18nkit --check-sync --strict",
52
+ "i18n:orphans": "i18nkit --find-orphans --strict",
53
+ "i18n:translate": "i18nkit --translate fr:en",
54
+ "i18n:watch": "i18nkit --watch",
55
+ "i18n:ci": "npm run i18n:check && npm run i18n:orphans"
56
+ }
57
+ }
58
+ ```
59
+
60
+ ### 3. Run
61
+
62
+ ```bash
63
+ # First time: extract + apply + create language files
64
+ npm run i18n:apply
65
+
66
+ # Development: watch mode
67
+ npm run i18n:watch
68
+
69
+ # Before commit / CI
70
+ npm run i18n:ci
71
+ ```
72
+
73
+ ## How It Works
74
+
75
+ **Before:**
76
+
77
+ ```html
78
+ <h1>Welcome to our app</h1>
79
+ <button label="Submit" />
80
+ <input placeholder="Enter your name" />
81
+ ```
82
+
83
+ **After:**
84
+
85
+ ```html
86
+ <h1>{{ 'home.titles.welcome_to_our_app' | transloco }}</h1>
87
+ <button [label]="'home.buttons.submit' | transloco" />
88
+ <input [placeholder]="'home.forms.enter_your_name' | transloco" />
89
+ ```
90
+
91
+ **Generated `fr.json`:**
92
+
93
+ ```json
94
+ {
95
+ "home": {
96
+ "titles": { "welcome_to_our_app": "Welcome to our app" },
97
+ "buttons": { "submit": "Submit" },
98
+ "forms": { "enter_your_name": "Enter your name" }
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## Commands
104
+
105
+ | Script | Command | Description |
106
+ | ------------------------ | --------------------------- | ---------------------------- |
107
+ | `npm run i18n` | `--lang fr --merge` | Extract, merge with existing |
108
+ | `npm run i18n:apply` | `--auto-apply --init-langs` | Extract + replace + create |
109
+ | `npm run i18n:check` | `--check-sync --strict` | Validate files are in sync |
110
+ | `npm run i18n:orphans` | `--find-orphans --strict` | Find unused keys |
111
+ | `npm run i18n:translate` | `--translate fr:en` | Translate via API |
112
+ | `npm run i18n:watch` | `--watch` | Re-run on file changes |
113
+
114
+ ## Configuration
115
+
116
+ Create `i18nkit.config.js` in your project root:
117
+
118
+ ```javascript
119
+ module.exports = {
120
+ src: 'src/app',
121
+ i18nDir: 'src/assets/i18n',
122
+ lang: 'fr',
123
+ format: 'nested',
124
+ backup: true,
125
+ excludedFolders: ['node_modules', 'dist', '.git'],
126
+ };
127
+ ```
128
+
129
+ Or `i18nkit.config.json`:
130
+
131
+ ```json
132
+ {
133
+ "src": "src/app",
134
+ "i18nDir": "src/assets/i18n",
135
+ "lang": "fr",
136
+ "format": "nested"
137
+ }
138
+ ```
139
+
140
+ ## Supported Patterns
141
+
142
+ ### HTML Templates
143
+
144
+ | Pattern | Example |
145
+ | ------------ | ---------------------------------- |
146
+ | Text content | `<h1>Welcome</h1>` |
147
+ | Attributes | `alt`, `title`, `placeholder` |
148
+ | Angular 17+ | `@if`, `@for`, `@switch`, `@defer` |
149
+
150
+ ### Component Libraries
151
+
152
+ | Library | Extracted Attributes |
153
+ | ---------------- | ---------------------------- |
154
+ | PrimeNG | `label`, `tooltip`, `header` |
155
+ | Angular Material | `placeholder`, `label` |
156
+ | Bootstrap | `title`, `alt` |
157
+
158
+ ## Translation APIs
159
+
160
+ ```bash
161
+ # Free: MyMemory API
162
+ npm run i18n:translate
163
+
164
+ # Free with higher rate limit
165
+ npx i18nkit --translate fr:en --email you@example.com
166
+
167
+ # Pro: DeepL API (best quality)
168
+ DEEPL_API_KEY=xxx npx i18nkit --translate fr:en --deepl
169
+ ```
170
+
171
+ ## CI/CD Integration
172
+
173
+ ### GitHub Actions
174
+
175
+ ```yaml
176
+ name: i18n
177
+ on: [push, pull_request]
178
+
179
+ jobs:
180
+ validate:
181
+ runs-on: ubuntu-latest
182
+ steps:
183
+ - uses: actions/checkout@v4
184
+ - uses: actions/setup-node@v4
185
+ with:
186
+ node-version: 22
187
+ - run: npm ci
188
+ - run: npm run i18n:ci
189
+ ```
190
+
191
+ ## Key Mapping
192
+
193
+ Override auto-generated keys with `.i18n-keys.json`:
194
+
195
+ ```json
196
+ {
197
+ "Welcome to our app": "common.welcome",
198
+ "Submit": "buttons.submit",
199
+ "Cancel": "buttons.cancel"
200
+ }
201
+ ```
202
+
203
+ ## Plugin System
204
+
205
+ i18nkit is built to be extended. Create plugins for any framework, library, or
206
+ translation service.
207
+
208
+ | Plugin Type | Purpose | Example Use Case |
209
+ | ------------ | -------------------------- | ------------------ |
210
+ | **parser** | Extract strings from files | Vue, Svelte, React |
211
+ | **adapter** | Transform to i18n syntax | i18next, vue-i18n |
212
+ | **provider** | Translate via API | Google, AWS, Azure |
213
+
214
+ ### Quick Example
215
+
216
+ ```javascript
217
+ // plugins/my-parser.js
218
+ module.exports = {
219
+ name: 'parser-vue',
220
+ type: 'parser',
221
+ meta: { description: 'Extract from Vue SFC' },
222
+ extensions: ['.vue'],
223
+ detect: ctx => ctx.pkg.dependencies?.['vue'],
224
+ extract(content) {
225
+ return [{ text: 'Hello', context: 'greeting' }];
226
+ },
227
+ };
228
+ ```
229
+
230
+ ```javascript
231
+ // i18nkit.config.js
232
+ module.exports = {
233
+ plugins: ['./plugins/my-parser.js'],
234
+ };
235
+ ```
236
+
237
+ **Full guide:** [PLUGINS.md](PLUGINS.md)
238
+
239
+ ### Community Plugins
240
+
241
+ Publish your plugin to npm:
242
+
243
+ ```bash
244
+ npm publish i18nkit-parser-svelte
245
+ ```
246
+
247
+ i18nkit auto-discovers packages named `i18nkit-*` or `@yourorg/i18nkit-*`.
248
+
249
+ **Want to contribute?** Check [builtin plugins](bin/plugins/) for reference
250
+ implementations.
251
+
252
+ ## Options
253
+
254
+ ```text
255
+ --src <path> Source directory (default: src/app)
256
+ --i18n-dir <path> i18n directory (default: src/assets/i18n)
257
+ --lang <code> Language code
258
+ --format <type> nested | flat (default: nested)
259
+ --merge Merge with existing translations
260
+ --auto-apply Extract and apply pipes
261
+ --init-langs <codes> Create language files (e.g., en,fr,es)
262
+ --check-sync Compare language files
263
+ --find-orphans Find unused keys
264
+ --translate <src:tgt> Translate via API (e.g., fr:en)
265
+ --deepl Use DeepL API
266
+ --watch Watch mode
267
+ --dry-run Preview only
268
+ --strict Exit 1 on issues
269
+ --ci CI mode (strict + json output)
270
+ --verbose Detailed output
271
+ ```
272
+
273
+ ## Exit Codes
274
+
275
+ | Code | Meaning |
276
+ | ---- | -------------------------------------------------- |
277
+ | 0 | Success |
278
+ | 1 | Missing translations or sync problems (`--strict`) |
279
+ | 2 | Error (file not found, parse error) |
280
+
281
+ ## API Documentation
282
+
283
+ Full API reference:
284
+ [abdess.github.io/i18nkit](https://abdess.github.io/i18nkit/)
285
+
286
+ ## Requirements
287
+
288
+ - Node.js >= 22.0.0
289
+
290
+ ## Contributing
291
+
292
+ i18nkit welcomes community contributions:
293
+
294
+ - **Plugins** - Create parsers for new frameworks, adapters for i18n libraries,
295
+ or providers for translation APIs
296
+ - **Bug reports** - Open an issue with reproduction steps
297
+ - **Feature requests** - Suggest improvements via GitHub issues
298
+
299
+ See [PLUGINS.md](PLUGINS.md) for the plugin development guide.
300
+
301
+ ## License
302
+
303
+ MIT
304
+
305
+ ---
306
+
307
+ [GitHub](https://github.com/Abdess/i18nkit) ·
308
+ [npm](https://npmjs.com/package/i18nkit) ·
309
+ [Issues](https://github.com/Abdess/i18nkit/issues) · [Plugin Guide](PLUGINS.md)
package/bin/cli.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { createContext, detectCommand, EXIT_CODES } = require('./core/context');
6
+ const { getCommand } = require('./commands');
7
+ const core = require('./core');
8
+
9
+ const args = process.argv.slice(2);
10
+ const commandName = detectCommand(args);
11
+ const command = getCommand(commandName);
12
+
13
+ if (!command) {
14
+ console.error(`Unknown command: ${commandName}`);
15
+ process.exit(EXIT_CODES.ERROR);
16
+ }
17
+
18
+ function handleError(err, verbose) {
19
+ console.error('Error:', err.message);
20
+ if (verbose) {
21
+ console.error(err.stack);
22
+ }
23
+
24
+ const backups = core.getBackupFiles();
25
+ if (backups.size > 0) {
26
+ console.log('\nRestoring backups...');
27
+ console.log(`Restored ${core.restoreBackups()} file(s)`);
28
+ }
29
+
30
+ process.exit(EXIT_CODES.ERROR);
31
+ }
32
+
33
+ async function main() {
34
+ const ctx = createContext(args);
35
+
36
+ try {
37
+ const result = await command.run(ctx);
38
+ const exitCode = result?.exitCode ?? EXIT_CODES.SUCCESS;
39
+
40
+ if (exitCode !== null) {
41
+ process.exit(exitCode);
42
+ }
43
+ } catch (err) {
44
+ handleError(err, ctx.verbose);
45
+ }
46
+ }
47
+
48
+ main();
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Apply command - replaces source text with translation keys.
5
+ * Applies findings from report.json to source files with backup support.
6
+ * @module commands/apply
7
+ */
8
+
9
+ const { getArgValue, applyTranslations } = require('../core');
10
+
11
+ function getApplyOptions(ctx) {
12
+ const { srcDir, backupDir, adapter, backup, dryRun, verbose, interactive, log } = ctx;
13
+ return { srcDir, backupDir, adapter, backup, dryRun, verbose, interactive, log };
14
+ }
15
+
16
+ function validateApplyFile(applyFile, exitCodes) {
17
+ if (!applyFile) {
18
+ console.error('Missing --apply <report.json> argument');
19
+ return { exitCode: exitCodes.ERROR };
20
+ }
21
+ return null;
22
+ }
23
+
24
+ module.exports = {
25
+ name: 'apply',
26
+ category: 'apply',
27
+ description: 'Apply translations from a report file to source files',
28
+
29
+ options: [
30
+ { flag: '--apply <report.json>', description: 'Path to the report file' },
31
+ { flag: '--backup', description: 'Create backups before modifying (default: on)' },
32
+ { flag: '--no-backup', description: 'Disable backup creation' },
33
+ { flag: '--interactive', description: 'Ask for confirmation before changes' },
34
+ ],
35
+
36
+ examples: ['i18nkit --apply .i18n/report.json', 'i18nkit --apply report.json --no-backup'],
37
+
38
+ async run(ctx) {
39
+ const { args, config, exitCodes } = ctx;
40
+ const applyFile = getArgValue(args, config, '--apply', null, null);
41
+ const error = validateApplyFile(applyFile, exitCodes);
42
+ if (error) {
43
+ return error;
44
+ }
45
+ await applyTranslations(applyFile, getApplyOptions(ctx));
46
+ return { exitCode: exitCodes.SUCCESS };
47
+ },
48
+ };
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Check-sync command - validates i18n file consistency.
5
+ * Detects missing keys, identical values, and ICU format mismatches.
6
+ * @module commands/check-sync
7
+ */
8
+
9
+ const { checkSync } = require('../core');
10
+
11
+ module.exports = {
12
+ name: 'check-sync',
13
+ category: 'validation',
14
+ description: 'Compare language files for missing or identical keys',
15
+
16
+ options: [
17
+ { flag: '--strict', description: 'Exit with error if missing or untranslated keys found' },
18
+ ],
19
+
20
+ examples: ['i18nkit --check-sync', 'i18nkit --check-sync --strict'],
21
+
22
+ async run(ctx) {
23
+ const { i18nDir, format, strict, log, exitCodes } = ctx;
24
+
25
+ const result = await checkSync({
26
+ i18nDir,
27
+ format,
28
+ strict,
29
+ log,
30
+ exitCodes: { success: exitCodes.SUCCESS, untranslated: exitCodes.UNTRANSLATED },
31
+ });
32
+
33
+ return { exitCode: result.exitCode ?? exitCodes.SUCCESS };
34
+ },
35
+ };