next-auto-i18n 0.1.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/DOCUMENTATION.md +749 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +291 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +11 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +58 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/generator/index.d.ts +24 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +35 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/key-builder.d.ts +28 -0
- package/dist/generator/key-builder.d.ts.map +1 -0
- package/dist/generator/key-builder.js +58 -0
- package/dist/generator/key-builder.js.map +1 -0
- package/dist/injector/config-injector.d.ts +11 -0
- package/dist/injector/config-injector.d.ts.map +1 -0
- package/dist/injector/config-injector.js +56 -0
- package/dist/injector/config-injector.js.map +1 -0
- package/dist/injector/index.d.ts +43 -0
- package/dist/injector/index.d.ts.map +1 -0
- package/dist/injector/index.js +63 -0
- package/dist/injector/index.js.map +1 -0
- package/dist/injector/layout-injector.d.ts +11 -0
- package/dist/injector/layout-injector.d.ts.map +1 -0
- package/dist/injector/layout-injector.js +111 -0
- package/dist/injector/layout-injector.js.map +1 -0
- package/dist/injector/middleware-injector.d.ts +10 -0
- package/dist/injector/middleware-injector.d.ts.map +1 -0
- package/dist/injector/middleware-injector.js +30 -0
- package/dist/injector/middleware-injector.js.map +1 -0
- package/dist/injector/routing-injector.d.ts +13 -0
- package/dist/injector/routing-injector.d.ts.map +1 -0
- package/dist/injector/routing-injector.js +33 -0
- package/dist/injector/routing-injector.js.map +1 -0
- package/dist/rewriter/attr-rewriter.d.ts +3 -0
- package/dist/rewriter/attr-rewriter.d.ts.map +1 -0
- package/dist/rewriter/attr-rewriter.js +36 -0
- package/dist/rewriter/attr-rewriter.js.map +1 -0
- package/dist/rewriter/index.d.ts +26 -0
- package/dist/rewriter/index.d.ts.map +1 -0
- package/dist/rewriter/index.js +133 -0
- package/dist/rewriter/index.js.map +1 -0
- package/dist/rewriter/jsx-rewriter.d.ts +20 -0
- package/dist/rewriter/jsx-rewriter.d.ts.map +1 -0
- package/dist/rewriter/jsx-rewriter.js +98 -0
- package/dist/rewriter/jsx-rewriter.js.map +1 -0
- package/dist/scanner/ast-parser.d.ts +19 -0
- package/dist/scanner/ast-parser.d.ts.map +1 -0
- package/dist/scanner/ast-parser.js +59 -0
- package/dist/scanner/ast-parser.js.map +1 -0
- package/dist/scanner/filters.d.ts +8 -0
- package/dist/scanner/filters.d.ts.map +1 -0
- package/dist/scanner/filters.js +146 -0
- package/dist/scanner/filters.js.map +1 -0
- package/dist/scanner/index.d.ts +16 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +82 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/string-extractor.d.ts +19 -0
- package/dist/scanner/string-extractor.d.ts.map +1 -0
- package/dist/scanner/string-extractor.js +108 -0
- package/dist/scanner/string-extractor.js.map +1 -0
- package/dist/translator/deepl.d.ts +19 -0
- package/dist/translator/deepl.d.ts.map +1 -0
- package/dist/translator/deepl.js +111 -0
- package/dist/translator/deepl.js.map +1 -0
- package/dist/translator/index.d.ts +14 -0
- package/dist/translator/index.d.ts.map +1 -0
- package/dist/translator/index.js +57 -0
- package/dist/translator/index.js.map +1 -0
- package/dist/utils/config.d.ts +19 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +50 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/env.d.ts +18 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +70 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +25 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +64 -0
package/DOCUMENTATION.md
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">next-auto-i18n</h1>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/next-auto-i18n"><img src="https://img.shields.io/npm/v/next-auto-i18n.svg" alt="npm version"></a>
|
|
7
|
+
<a href="https://github.com/stevenkoulo/next-auto-i18n/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/next-auto-i18n.svg" alt="license"></a>
|
|
8
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/next-auto-i18n.svg" alt="node version"></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center"><strong>Translate your Next.js project automatically in under 5 minutes.</strong></p>
|
|
12
|
+
|
|
13
|
+
next-auto-i18n is a CLI tool that fully automates internationalization (i18n) in existing Next.js projects. It scans your codebase via AST, extracts translatable strings, translates them through DeepL, rewrites your components to use `t("key")` calls, and configures [next-intl](https://next-intl-docs.vercel.app/) — all in a single command.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
- [Why next-auto-i18n?](#why-next-auto-i18n)
|
|
20
|
+
- [Prerequisites](#prerequisites)
|
|
21
|
+
- [Installation](#installation)
|
|
22
|
+
- [Quick Start](#quick-start)
|
|
23
|
+
- [Configuration](#configuration)
|
|
24
|
+
- [Commands](#commands)
|
|
25
|
+
- [How It Works](#how-it-works)
|
|
26
|
+
- [Supported String Types](#supported-string-types)
|
|
27
|
+
- [Safety & Backups](#safety--backups)
|
|
28
|
+
- [DeepL API](#deepl-api)
|
|
29
|
+
- [Troubleshooting](#troubleshooting)
|
|
30
|
+
- [Contributing](#contributing)
|
|
31
|
+
- [Roadmap](#roadmap)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Why next-auto-i18n?
|
|
37
|
+
|
|
38
|
+
### Manual setup vs next-auto-i18n
|
|
39
|
+
|
|
40
|
+
| | Manual i18n setup | next-auto-i18n |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| **Time** | 4-8 hours (medium project) | < 5 minutes |
|
|
43
|
+
| **Steps** | 6+ manual steps across dozens of files | 1 command |
|
|
44
|
+
| **Human error** | Missed strings, typos in keys, broken config | Zero — AST-powered, deterministic |
|
|
45
|
+
| **Translations** | Copy-paste into Google Translate | Automated via DeepL API |
|
|
46
|
+
| **Maintenance** | Re-scan manually after every change | `next-auto-i18n sync` |
|
|
47
|
+
|
|
48
|
+
- **Zero manual work** — from raw project to fully translated site in one command
|
|
49
|
+
- **AST-powered scanning** — finds every translatable string, including dynamic template literals
|
|
50
|
+
- **Incremental by design** — `sync` only translates what changed, preserving existing translations
|
|
51
|
+
- **Safe** — automatic backups, `--dry-run` mode, and `.gitignore` management
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Prerequisites
|
|
56
|
+
|
|
57
|
+
- **Node.js >= 18**
|
|
58
|
+
- **A Next.js project** using the App Router (`app/` directory)
|
|
59
|
+
- **A DeepL API key** — [sign up for free](https://www.deepl.com/pro-api) (500,000 characters/month at no cost)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Run directly without installing (recommended)
|
|
67
|
+
npx next-auto-i18n init
|
|
68
|
+
|
|
69
|
+
# Or install globally
|
|
70
|
+
npm install -g next-auto-i18n
|
|
71
|
+
next-auto-i18n init
|
|
72
|
+
|
|
73
|
+
# Or as a dev dependency
|
|
74
|
+
npm install -D next-auto-i18n
|
|
75
|
+
npx next-auto-i18n init
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### 1. Run the init command
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx next-auto-i18n init
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2. Answer the prompts
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
▸ Configuration
|
|
92
|
+
? Source locale (ISO code): fr
|
|
93
|
+
? Target locales (comma-separated): en, es
|
|
94
|
+
? DeepL API key: ********
|
|
95
|
+
✓ API key saved to .env.local
|
|
96
|
+
✓ .gitignore updated (.env.local, *.backup)
|
|
97
|
+
✓ auto-i18n.config.json created
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Watch it work
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
▸ Scanning project
|
|
104
|
+
✓ 47 strings found
|
|
105
|
+
|
|
106
|
+
▸ Generating keys
|
|
107
|
+
✓ 42 keys generated → ./messages/fr.json
|
|
108
|
+
|
|
109
|
+
▸ Translating via DeepL
|
|
110
|
+
✓ Translation EN (42 strings)
|
|
111
|
+
✓ Translation ES (42 strings)
|
|
112
|
+
✓ 84 strings translated
|
|
113
|
+
|
|
114
|
+
▸ Rewriting components
|
|
115
|
+
✓ 47 replacements in 12 files
|
|
116
|
+
|
|
117
|
+
▸ Configuring Next.js
|
|
118
|
+
✓ layout.tsx configured
|
|
119
|
+
✓ next.config configured
|
|
120
|
+
✓ middleware.ts created
|
|
121
|
+
✓ i18n/routing.ts created
|
|
122
|
+
|
|
123
|
+
✓ Internationalization configured successfully!
|
|
124
|
+
Languages: fr → en, es
|
|
125
|
+
Backups available in *.backup
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4. Start your app
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm run dev
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Your site now supports `fr`, `en`, and `es`. Visit `/en` or `/es` to see the translations.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
### auto-i18n.config.json
|
|
141
|
+
|
|
142
|
+
Generated automatically by `next-auto-i18n init`. This file is safe to commit.
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"sourceLocale": "fr",
|
|
147
|
+
"targetLocales": ["en", "es"],
|
|
148
|
+
"provider": "deepl",
|
|
149
|
+
"apiKeyEnv": "AUTO_I18N_DEEPL_KEY",
|
|
150
|
+
"messagesDir": "./messages",
|
|
151
|
+
"ignore": ["node_modules", ".next", "**/*.test.*", "**/*.spec.*"]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Field | Type | Default | Description |
|
|
156
|
+
|-------|------|---------|-------------|
|
|
157
|
+
| `sourceLocale` | `string` | — | ISO code of your source language (e.g. `"fr"`) |
|
|
158
|
+
| `targetLocales` | `string[]` | — | List of target language codes (e.g. `["en", "es"]`) |
|
|
159
|
+
| `provider` | `string` | `"deepl"` | Translation provider |
|
|
160
|
+
| `apiKeyEnv` | `string` | `"AUTO_I18N_DEEPL_KEY"` | Name of the environment variable holding the API key |
|
|
161
|
+
| `messagesDir` | `string` | `"./messages"` | Directory where JSON translation files are stored |
|
|
162
|
+
| `ignore` | `string[]` | `["node_modules", ".next", "**/*.test.*", "**/*.spec.*"]` | Glob patterns of files/directories to skip |
|
|
163
|
+
|
|
164
|
+
### .env.local
|
|
165
|
+
|
|
166
|
+
The DeepL API key is stored in `.env.local` and **never** in the config file.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# .env.local (auto-generated, never committed)
|
|
170
|
+
AUTO_I18N_DEEPL_KEY=your-deepl-api-key-here
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
next-auto-i18n automatically adds `.env.local` and `*.backup` to your `.gitignore`.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Commands
|
|
178
|
+
|
|
179
|
+
### `next-auto-i18n init`
|
|
180
|
+
|
|
181
|
+
Full project initialization: scan, translate, rewrite, and configure.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
next-auto-i18n init # Interactive mode
|
|
185
|
+
next-auto-i18n init --dry-run # Preview changes, ask before applying
|
|
186
|
+
next-auto-i18n init --locale en,es,de # Skip locale prompt
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| Flag | Description |
|
|
190
|
+
|------|-------------|
|
|
191
|
+
| `--dry-run` | Scans and generates keys, then shows a summary and asks for confirmation before translating, rewriting, or modifying config files |
|
|
192
|
+
| `--locale <locales>` | Comma-separated target locales, skips the interactive prompt |
|
|
193
|
+
|
|
194
|
+
**Example output with `--dry-run`:**
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
▸ Scanning project
|
|
198
|
+
✓ 47 strings found
|
|
199
|
+
|
|
200
|
+
▸ Generating keys
|
|
201
|
+
✓ 42 keys generated → ./messages/fr.json
|
|
202
|
+
|
|
203
|
+
Strings found: 47
|
|
204
|
+
Keys generated: 42
|
|
205
|
+
Files to rewrite: 12
|
|
206
|
+
Target locales: en, es
|
|
207
|
+
|
|
208
|
+
? Apply these changes? (Y/n)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `next-auto-i18n sync`
|
|
214
|
+
|
|
215
|
+
Re-scans the project and translates only new or modified strings. Existing translations are preserved.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
next-auto-i18n sync
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Example output:**
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
▸ Scanning project
|
|
225
|
+
✓ 53 strings found
|
|
226
|
+
|
|
227
|
+
▸ Updating keys
|
|
228
|
+
✓ 48 keys → ./messages/fr.json
|
|
229
|
+
|
|
230
|
+
▸ Incremental translation
|
|
231
|
+
✓ Translation EN (6 strings)
|
|
232
|
+
✓ Translation ES (6 strings)
|
|
233
|
+
✓ 12 new translations
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use `sync` after adding new components, changing text, or removing strings.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### `next-auto-i18n add-locale <locale>`
|
|
241
|
+
|
|
242
|
+
Adds a new target language and translates all existing keys into it.
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
next-auto-i18n add-locale ar
|
|
246
|
+
next-auto-i18n add-locale pt-BR
|
|
247
|
+
next-auto-i18n add-locale zh
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Example output:**
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
✓ ar added to auto-i18n.config.json
|
|
254
|
+
|
|
255
|
+
▸ Translating to AR
|
|
256
|
+
✓ Translation AR (42 strings)
|
|
257
|
+
✓ 42 strings translated
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### `next-auto-i18n missing`
|
|
263
|
+
|
|
264
|
+
Reports untranslated keys per target locale.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
next-auto-i18n missing
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Example output:**
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
✓ en — complete
|
|
274
|
+
⚠ es — 3 missing keys
|
|
275
|
+
new_feature_title
|
|
276
|
+
new_feature_description
|
|
277
|
+
cta_button
|
|
278
|
+
✓ de — complete
|
|
279
|
+
|
|
280
|
+
ℹ 3 missing keys total
|
|
281
|
+
Run "next-auto-i18n sync" to translate them.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## How It Works
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
npx next-auto-i18n init
|
|
290
|
+
│
|
|
291
|
+
▼
|
|
292
|
+
┌──────────────────────────────────────┐
|
|
293
|
+
│ 1. Interactive Configuration │
|
|
294
|
+
│ Source locale, targets, API key │
|
|
295
|
+
│ → auto-i18n.config.json │
|
|
296
|
+
│ → .env.local │
|
|
297
|
+
└──────────────────────────────────────┘
|
|
298
|
+
│
|
|
299
|
+
▼
|
|
300
|
+
┌──────────────────────────────────────┐
|
|
301
|
+
│ 2. AST Scan │
|
|
302
|
+
│ Parses .tsx/.jsx/.ts/.js files │
|
|
303
|
+
│ Extracts 3 types of strings: │
|
|
304
|
+
│ • JSX text: <p>Hello</p> │
|
|
305
|
+
│ • Attributes: placeholder="..." │
|
|
306
|
+
│ • Template literals: `Hello` │
|
|
307
|
+
└──────────────────────────────────────┘
|
|
308
|
+
│
|
|
309
|
+
▼
|
|
310
|
+
┌──────────────────────────────────────┐
|
|
311
|
+
│ 3. Key Generation │
|
|
312
|
+
│ messages/fr.json │
|
|
313
|
+
│ { "hello": "Bonjour", │
|
|
314
|
+
│ "welcome_name": "Bienvenue {name}" } │
|
|
315
|
+
└──────────────────────────────────────┘
|
|
316
|
+
│
|
|
317
|
+
▼
|
|
318
|
+
┌──────────────────────────────────────┐
|
|
319
|
+
│ 4. DeepL Translation │
|
|
320
|
+
│ messages/en.json │
|
|
321
|
+
│ messages/es.json │
|
|
322
|
+
│ Batched, placeholder-safe │
|
|
323
|
+
└──────────────────────────────────────┘
|
|
324
|
+
│
|
|
325
|
+
▼
|
|
326
|
+
┌──────────────────────────────────────┐
|
|
327
|
+
│ 5. Component Rewriting │
|
|
328
|
+
│ <p>Bonjour</p> │
|
|
329
|
+
│ → <p>{t("hello")}</p> │
|
|
330
|
+
│ │
|
|
331
|
+
│ + useTranslations / getTranslations │
|
|
332
|
+
│ + import statements │
|
|
333
|
+
└──────────────────────────────────────┘
|
|
334
|
+
│
|
|
335
|
+
▼
|
|
336
|
+
┌──────────────────────────────────────┐
|
|
337
|
+
│ 6. Next.js Config Injection │
|
|
338
|
+
│ • layout.tsx → NextIntlClientProvider │
|
|
339
|
+
│ • next.config → createNextIntlPlugin │
|
|
340
|
+
│ • middleware.ts → routing │
|
|
341
|
+
│ • i18n/routing.ts → locale definitions │
|
|
342
|
+
└──────────────────────────────────────┘
|
|
343
|
+
│
|
|
344
|
+
▼
|
|
345
|
+
✓ i18n-ready site
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### AST Scanning
|
|
349
|
+
|
|
350
|
+
The scanner uses [ts-morph](https://ts-morph.com/) to parse TypeScript/JSX via AST (Abstract Syntax Tree). This means it understands your code structure, not just raw text.
|
|
351
|
+
|
|
352
|
+
**What gets detected:**
|
|
353
|
+
|
|
354
|
+
- JSX text content: `<h1>Welcome</h1>`
|
|
355
|
+
- Translatable HTML attributes: `placeholder`, `alt`, `title`, `aria-label`, `aria-placeholder`, `aria-description`, `aria-details`, `label`, `content`
|
|
356
|
+
- Static template literals: `` `Hello world` ``
|
|
357
|
+
- Dynamic template literals: `` `Hello ${name}` `` (converted to `t("key", { name })`)
|
|
358
|
+
|
|
359
|
+
**What gets ignored automatically:**
|
|
360
|
+
|
|
361
|
+
- Technical strings: CSS classes, hex colors, URLs, routes, MIME types, env vars
|
|
362
|
+
- Technical attributes: `className`, `id`, `type`, `href`, `src`, `key`, `style`, etc.
|
|
363
|
+
- Technical keywords: CSS values (`flex`, `grid`, `bold`...), HTTP methods, HTML input types, boolean values, encoding names
|
|
364
|
+
- Short/numeric strings: empty strings, pure numbers, single characters
|
|
365
|
+
- Config files: `next.config.*`, `tailwind.config.*`, `vite.config.*`, etc.
|
|
366
|
+
- Test files: `*.test.*`, `*.spec.*`
|
|
367
|
+
- Directories: `node_modules`, `.next`, `.git`, `dist`, `build`, `out`, `coverage`, `public`
|
|
368
|
+
|
|
369
|
+
### API Key Security
|
|
370
|
+
|
|
371
|
+
- The DeepL API key is **never** stored in config files or source code
|
|
372
|
+
- It lives exclusively in `.env.local`, which is automatically added to `.gitignore`
|
|
373
|
+
- The config file only stores the **name** of the environment variable (`AUTO_I18N_DEEPL_KEY`), not the value
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Supported String Types
|
|
378
|
+
|
|
379
|
+
### JSX Text
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
// Before
|
|
383
|
+
<h1>Bienvenue sur notre site</h1>
|
|
384
|
+
<p>Découvrez nos produits</p>
|
|
385
|
+
|
|
386
|
+
// After
|
|
387
|
+
<h1>{t("bienvenue_sur_notre_site")}</h1>
|
|
388
|
+
<p>{t("decouvrez_nos_produits")}</p>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### HTML Attributes
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
// Before
|
|
395
|
+
<input placeholder="Rechercher un produit" />
|
|
396
|
+
<img alt="Photo de profil" />
|
|
397
|
+
<button title="Fermer la fenêtre">X</button>
|
|
398
|
+
<div aria-label="Menu principal">...</div>
|
|
399
|
+
|
|
400
|
+
// After
|
|
401
|
+
<input placeholder={t("rechercher_un_produit")} />
|
|
402
|
+
<img alt={t("photo_de_profil")} />
|
|
403
|
+
<button title={t("fermer_la_fenetre")}>X</button>
|
|
404
|
+
<div aria-label={t("menu_principal")}>...</div>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Supported attributes:** `placeholder`, `alt`, `title`, `aria-label`, `aria-placeholder`, `aria-description`, `aria-details`, `label`, `content`
|
|
408
|
+
|
|
409
|
+
### Static Template Literals
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
// Before
|
|
413
|
+
const greeting = `Bonjour le monde`;
|
|
414
|
+
|
|
415
|
+
// After
|
|
416
|
+
const greeting = t("bonjour_le_monde");
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Dynamic Template Literals
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
// Before
|
|
423
|
+
const message = `Bienvenue ${userName}, vous avez ${count} messages`;
|
|
424
|
+
|
|
425
|
+
// After
|
|
426
|
+
const message = t("bienvenue_username_vous_avez_count", { userName, count });
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
The corresponding JSON entry uses ICU-style placeholders:
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"bienvenue_username_vous_avez_count": "Bienvenue {userName}, vous avez {count} messages"
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Server Components vs Client Components
|
|
438
|
+
|
|
439
|
+
next-auto-i18n automatically detects whether a file is a Server Component or a Client Component:
|
|
440
|
+
|
|
441
|
+
**Server Components** (no `'use client'` directive):
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
import { getTranslations } from "next-intl/server";
|
|
445
|
+
|
|
446
|
+
export default async function Page() {
|
|
447
|
+
const t = await getTranslations();
|
|
448
|
+
return <h1>{t("hello")}</h1>;
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Client Components** (has `'use client'` directive):
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
"use client";
|
|
456
|
+
import { useTranslations } from "next-intl";
|
|
457
|
+
|
|
458
|
+
export default function Button() {
|
|
459
|
+
const t = useTranslations();
|
|
460
|
+
return <button>{t("submit")}</button>;
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Safety & Backups
|
|
467
|
+
|
|
468
|
+
### `--dry-run` mode
|
|
469
|
+
|
|
470
|
+
Preview everything next-auto-i18n will do before it makes any changes:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
next-auto-i18n init --dry-run
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
In dry-run mode, the tool:
|
|
477
|
+
1. Scans your project and extracts strings
|
|
478
|
+
2. Generates keys and shows a summary
|
|
479
|
+
3. Asks for your confirmation before proceeding
|
|
480
|
+
4. Only applies changes if you confirm
|
|
481
|
+
|
|
482
|
+
### Automatic backups
|
|
483
|
+
|
|
484
|
+
Before modifying any file, next-auto-i18n creates a `.backup` copy:
|
|
485
|
+
|
|
486
|
+
```
|
|
487
|
+
app/layout.tsx → app/layout.tsx.backup
|
|
488
|
+
app/page.tsx → app/page.tsx.backup
|
|
489
|
+
next.config.ts → next.config.ts.backup
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Restoring from backup
|
|
493
|
+
|
|
494
|
+
To revert a single file:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
cp app/page.tsx.backup app/page.tsx
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
To revert all changes:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# Restore all backup files
|
|
504
|
+
for f in $(find . -name "*.backup" -not -path "*/node_modules/*"); do
|
|
505
|
+
cp "$f" "${f%.backup}"
|
|
506
|
+
done
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
To clean up backup files after you're satisfied:
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
find . -name "*.backup" -not -path "*/node_modules/*" -delete
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Idempotency
|
|
516
|
+
|
|
517
|
+
All operations are idempotent. Running `next-auto-i18n init` twice will not duplicate imports, providers, or configuration. The tool detects what's already in place and skips it.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## DeepL API
|
|
522
|
+
|
|
523
|
+
### Getting a free API key
|
|
524
|
+
|
|
525
|
+
1. Go to [deepl.com/pro-api](https://www.deepl.com/pro-api)
|
|
526
|
+
2. Sign up for a **DeepL API Free** account
|
|
527
|
+
3. Copy your API key from the account dashboard
|
|
528
|
+
|
|
529
|
+
### Free plan limits
|
|
530
|
+
|
|
531
|
+
| | DeepL API Free | DeepL API Pro |
|
|
532
|
+
|---|---|---|
|
|
533
|
+
| Characters/month | 500,000 | Unlimited (pay-per-use) |
|
|
534
|
+
| Cost | Free | $5.49/million characters |
|
|
535
|
+
| API endpoint | `api-free.deepl.com` | `api.deepl.com` |
|
|
536
|
+
|
|
537
|
+
500,000 characters is enough for most projects. A medium-sized Next.js app typically has 5,000-20,000 characters of translatable text.
|
|
538
|
+
|
|
539
|
+
next-auto-i18n automatically detects whether your key is Free (ends with `:fx`) or Pro and uses the correct endpoint.
|
|
540
|
+
|
|
541
|
+
### Storing the API key
|
|
542
|
+
|
|
543
|
+
The API key is stored in `.env.local` and loaded via `dotenv`:
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
# .env.local
|
|
547
|
+
AUTO_I18N_DEEPL_KEY=your-api-key-here
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
You can also set it as a system environment variable:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
export AUTO_I18N_DEEPL_KEY=your-api-key-here
|
|
554
|
+
next-auto-i18n init
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Placeholder protection
|
|
558
|
+
|
|
559
|
+
Dynamic strings containing variables like `{name}` are protected during translation using XML tags. DeepL preserves XML tags in output, ensuring placeholders are never translated:
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
Input: "Bienvenue <x>name</x>"
|
|
563
|
+
Output: "Welcome <x>name</x>"
|
|
564
|
+
→ Restored: "Welcome {name}"
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Troubleshooting
|
|
570
|
+
|
|
571
|
+
### "Invalid or unauthorized API key" (403)
|
|
572
|
+
|
|
573
|
+
Your DeepL API key is invalid or expired.
|
|
574
|
+
|
|
575
|
+
```
|
|
576
|
+
✗ DeepL API error (403): clé API invalide ou non autorisée
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Fix:** Verify your key at [deepl.com/your-account/keys](https://www.deepl.com/your-account/keys) and update `.env.local`:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
# .env.local
|
|
583
|
+
AUTO_I18N_DEEPL_KEY=your-correct-key-here
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
### "Quota exceeded" (456)
|
|
589
|
+
|
|
590
|
+
You've used all 500,000 free characters for the current month.
|
|
591
|
+
|
|
592
|
+
```
|
|
593
|
+
✗ DeepL API error (456): quota de traduction dépassé
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Fix:** Wait for the monthly reset, upgrade to DeepL Pro, or reduce the scope by translating fewer locales at once.
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
### "No translatable strings found"
|
|
601
|
+
|
|
602
|
+
The scanner found no strings in your project.
|
|
603
|
+
|
|
604
|
+
```
|
|
605
|
+
⚠ No translatable string found — stopping
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Fix:** Make sure your components contain actual text (not just variables or technical strings). Check that your source files are in `app/` or `src/app/` and use `.tsx`, `.jsx`, `.ts`, or `.js` extensions.
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
### "layout.tsx not found"
|
|
613
|
+
|
|
614
|
+
The injector can't find your root layout file.
|
|
615
|
+
|
|
616
|
+
```
|
|
617
|
+
⚠ layout.tsx — layout.tsx introuvable
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Fix:** Ensure you have `app/layout.tsx` or `src/app/layout.tsx` in your project. next-auto-i18n checks both locations.
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
### "auto-i18n.config.json not found"
|
|
625
|
+
|
|
626
|
+
You're running `sync`, `add-locale`, or `missing` before `init`.
|
|
627
|
+
|
|
628
|
+
```
|
|
629
|
+
✗ ENOENT: no such file or directory, open 'auto-i18n.config.json'
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**Fix:** Run `next-auto-i18n init` first to generate the config file.
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
### "Too many requests" (429)
|
|
637
|
+
|
|
638
|
+
DeepL rate limit reached.
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
✗ DeepL API error (429): trop de requêtes
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Fix:** Wait a few seconds and retry. next-auto-i18n sends translations in batches of 50 strings to minimize this, but it can still occur with very large projects. Simply re-run the command.
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## Contributing
|
|
649
|
+
|
|
650
|
+
### Setup
|
|
651
|
+
|
|
652
|
+
```bash
|
|
653
|
+
git clone https://github.com/stevenkoulo/next-auto-i18n.git
|
|
654
|
+
cd next-auto-i18n
|
|
655
|
+
npm install
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Development
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
# Run the CLI in dev mode (no build needed)
|
|
662
|
+
npm run dev -- init --dry-run
|
|
663
|
+
|
|
664
|
+
# Build
|
|
665
|
+
npm run build
|
|
666
|
+
|
|
667
|
+
# Run tests
|
|
668
|
+
npm test
|
|
669
|
+
|
|
670
|
+
# Run tests in watch mode
|
|
671
|
+
npx vitest
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### Project structure
|
|
675
|
+
|
|
676
|
+
```
|
|
677
|
+
next-auto-i18n/
|
|
678
|
+
├── src/
|
|
679
|
+
│ ├── cli/ # CLI entry point + interactive prompts
|
|
680
|
+
│ ├── scanner/ # AST parsing + string extraction + filtering
|
|
681
|
+
│ ├── generator/ # Key generation + JSON file creation
|
|
682
|
+
│ ├── translator/ # DeepL API client + translation orchestration
|
|
683
|
+
│ ├── rewriter/ # JSX/attribute rewriting via AST
|
|
684
|
+
│ ├── injector/ # Next.js config injection (layout, config, middleware, routing)
|
|
685
|
+
│ └── utils/ # Config, env, logger utilities
|
|
686
|
+
├── tests/ # Vitest test suites (298 tests)
|
|
687
|
+
├── auto-i18n-specs.md # Project specifications
|
|
688
|
+
└── DOCUMENTATION.md # This file
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Running tests
|
|
692
|
+
|
|
693
|
+
```bash
|
|
694
|
+
npm test
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
The test suite covers all modules:
|
|
698
|
+
|
|
699
|
+
| Module | Tests |
|
|
700
|
+
|--------|-------|
|
|
701
|
+
| Scanner (filters) | 109 |
|
|
702
|
+
| Generator (key-builder) | 39 |
|
|
703
|
+
| Rewriter | 33 |
|
|
704
|
+
| Injector | 25 |
|
|
705
|
+
| Translator (DeepL) | 23 |
|
|
706
|
+
| Generator | 20 |
|
|
707
|
+
| Scanner (string-extractor) | 20 |
|
|
708
|
+
| CLI (config, env) | 18 |
|
|
709
|
+
| Translator (orchestration) | 11 |
|
|
710
|
+
| **Total** | **298** |
|
|
711
|
+
|
|
712
|
+
### Submitting changes
|
|
713
|
+
|
|
714
|
+
1. Fork the repository
|
|
715
|
+
2. Create a feature branch: `git checkout -b feature/my-feature`
|
|
716
|
+
3. Write tests for your changes
|
|
717
|
+
4. Ensure all tests pass: `npm test`
|
|
718
|
+
5. Ensure the build succeeds: `npm run build`
|
|
719
|
+
6. Submit a Pull Request
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Roadmap
|
|
724
|
+
|
|
725
|
+
### v1.x — Enhancements
|
|
726
|
+
|
|
727
|
+
- [ ] `next-auto-i18n sync` — rescan and incremental update *(done)*
|
|
728
|
+
- [ ] `next-auto-i18n missing` — report untranslated keys *(done)*
|
|
729
|
+
- [ ] `--watch` mode — auto-sync on file changes
|
|
730
|
+
- [ ] Support for Vite + React (without Next.js)
|
|
731
|
+
- [ ] Custom key naming strategies
|
|
732
|
+
|
|
733
|
+
### v2.0 — Multi-provider support
|
|
734
|
+
|
|
735
|
+
- [ ] OpenAI (GPT-4) as translation provider
|
|
736
|
+
- [ ] Google Cloud Translation API
|
|
737
|
+
- [ ] Configurable provider via `auto-i18n.config.json`
|
|
738
|
+
|
|
739
|
+
### v3.0 — Ecosystem
|
|
740
|
+
|
|
741
|
+
- [ ] Web dashboard for team translation management
|
|
742
|
+
- [ ] CI/CD integration (GitHub Actions)
|
|
743
|
+
- [ ] Public API for programmatic access
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## License
|
|
748
|
+
|
|
749
|
+
[MIT](./LICENSE) — Steven KOULO
|