@translation-cms/sync 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,29 +1,51 @@
1
1
  # @translation-cms/sync
2
2
 
3
- Scan translation keys in your codebase, sync them to the Translations CMS, and
4
- pull translations back as local JSON files. Built for Next.js + i18next.
3
+ Automatisch vertaalsleutels uit je codebase scannen, syncen met de Translations
4
+ CMS, en vertalingen ophalen als lokale JSON-bestanden. Gebouwd voor Next.js +
5
+ i18next.
5
6
 
6
- ## How it works
7
+ ## Wat is dit?
8
+
9
+ Een CLI-tool die je workflow vereenvoudigt:
10
+
11
+ - 🔍 **Scant** je code voor translation keys (React-i18next patroon)
12
+ - 📤 **Synct** nieuwe keys naar de Translations CMS
13
+ - 📥 **Haalt** vertalingen op als JSON-bestanden
14
+ - 🎯 **Preview** functie waarin editors live kunnen zien hoe hun tekst in je app
15
+ eruit ziet
7
16
 
8
17
  ```
9
- 1. sync-translations sync scans your code, pushes new keys to CMS
10
- 2. sync-translations pull → fetches translations, writes src/lib/i18n/dictionaries/{locale}.json
11
- 3. i18next loads the files → static imports in client.ts, dynamic imports in server.ts
18
+ Your Codescan CMS pull JSON files → i18next → Your App
12
19
  ```
13
20
 
14
21
  ---
15
22
 
16
- ## Installation
23
+ # Setup Handleiding — Van Nul Tot Functie
24
+
25
+ ## Stap 1: Vereisten
26
+
27
+ Controleer wat je nodig hebt:
28
+
29
+ - ✅ Next.js project (v14+)
30
+ - ✅ pnpm (aanbevolen) of npm
31
+ - ✅ Translations CMS account + project aangemaakt
32
+ - ✅ CMS project credentials (URL, project ID, API key)
33
+
34
+ ## Stap 2: Installatie
17
35
 
18
36
  ```bash
19
37
  pnpm add @translation-cms/sync
20
38
  ```
21
39
 
22
- ---
40
+ Plus de peer dependencies:
41
+
42
+ ```bash
43
+ pnpm add i18next react-i18next i18next-resources-to-backend
44
+ ```
23
45
 
24
- ## Configuration
46
+ ## Stap 3: CMS Credentials Instellen
25
47
 
26
- ### `.env.local`
48
+ Maak `.env.local` aan in je project root:
27
49
 
28
50
  ```bash
29
51
  NEXT_PUBLIC_CMS_URL=https://cms.example.com
@@ -31,168 +53,159 @@ NEXT_PUBLIC_CMS_PROJECT_ID=your-project-id
31
53
  NEXT_PUBLIC_CMS_ANON_KEY=your-api-key
32
54
  ```
33
55
 
34
- ### `.translationsrc.json`
56
+ Deze krijg je uit je CMS project settings.
35
57
 
36
- Run `sync-translations init` to generate it interactively, or create it
37
- manually:
58
+ ## Stap 4: Automatische Setup (Aanbevolen)
59
+
60
+ Run de interactive setup wizard:
61
+
62
+ ```bash
63
+ pnpm sync-translations init
64
+ ```
65
+
66
+ Dit maakt automatisch aan:
67
+
68
+ ```
69
+ src/lib/i18n/
70
+ ├── settings.ts # i18next configuratie
71
+ ├── types.ts # TypeScript types voor translations
72
+ ├── client.ts # 'use client' hook + static imports
73
+ ├── server.ts # Server component support
74
+ ├── provider.tsx # Provider wrapper
75
+ └── dictionaries/
76
+ ├── en.json # Engels (leeg, wordt gevuld door pull)
77
+ └── nl.json # Nederlands (leeg, wordt gevuld door pull)
78
+ ```
79
+
80
+ Ook `.translationsrc.json` wordt gegenereerd:
38
81
 
39
82
  ```json
40
83
  {
41
84
  "outputDir": "./src/lib/i18n/dictionaries",
42
85
  "pullTtlMs": 300000,
43
- "excludedDirs": ["e2e", "fixtures"],
44
- "routeParams": {
45
- "/[locale]/blog/[slug]": { "slug": "first-post" },
46
- "/[locale]/products/[slug]": { "slug": "product-a" }
47
- }
86
+ "excludedDirs": ["e2e", "node_modules", "fixtures"],
87
+ "routeParams": {}
48
88
  }
49
89
  ```
50
90
 
51
- **Priority:** CLI args `.env.local` `.translationsrc.json`
52
-
53
- ---
91
+ **Let op:** `routeParams` kan leeg blijven! De scanner genereert automatisch
92
+ sensible defaults voor alle dynamic routes.
54
93
 
55
- ## Quickstart
94
+ ## Stap 5: Eerste Sync — Keys Uploaden
56
95
 
57
- Run the interactive setup scaffolds all i18n files automatically:
96
+ Scan je code en upload gedetecteerde keys naar de CMS:
58
97
 
59
98
  ```bash
60
- sync-translations init
99
+ pnpm sync-translations sync
61
100
  ```
62
101
 
63
- It creates `.translationsrc.json` and writes:
102
+ Dit zal:
64
103
 
65
- ```
66
- src/lib/i18n/
67
- settings.ts
68
- types.ts
69
- client.ts ← 'use client', static imports
70
- server.ts ← RSC, dynamic import()
71
- provider.tsx ← TranslationProvider
72
- dictionaries/
73
- en.json ← populated by: sync-translations pull
74
- nl.json
75
- ```
104
+ 1. Je code scannen op translation keys (`t('namespace:key')` patroon)
105
+ 2. Route detecteren waar elke key gebruikt wordt
106
+ 3. Nieuwe keys naar CMS uploaden
107
+ 4. Rapport tonen (welke keys toegevoegd/gewijzigd)
76
108
 
77
- Then install peer deps and pull translations:
109
+ Controleer in de CMS of je keys verschenen zijn ✅
78
110
 
79
- ```bash
80
- pnpm add i18next react-i18next i18next-resources-to-backend
81
- sync-translations pull
82
- ```
111
+ ## Stap 6: Vertalingen Ophalen
83
112
 
84
- Add to your `package.json` so translations are always fresh:
113
+ Download vertalingen van de CMS en schrijf ze naar lokale JSON:
85
114
 
86
- ```json
87
- "scripts": {
88
- "dev": "sync-translations pull && next dev",
89
- "build": "sync-translations pull && next build"
90
- }
115
+ ```bash
116
+ pnpm sync-translations pull
91
117
  ```
92
118
 
93
- ---
119
+ Dit vult `dictionaries/en.json` en `dictionaries/nl.json` met vertalingen.
94
120
 
95
- ## CLI
121
+ ## Stap 7: Integratie in je Project
96
122
 
97
- ```bash
98
- # Scan keys + push to CMS
99
- sync-translations sync
123
+ ### A. Root Layout Setup
100
124
 
101
- # Pull translations to local JSON files (respects TTL cache)
102
- sync-translations pull
103
- sync-translations pull --force # ignore TTL
104
- sync-translations pull --output ./locales # custom output dir
105
- sync-translations pull --env staging # pull staging environment
106
- sync-translations pull --ttl 600000 # custom TTL in ms
125
+ Voeg de provider toe aan je `src/app/layout.tsx`:
107
126
 
108
- # Preview changes without touching the CMS
109
- sync-translations sync --dry-run
127
+ ```tsx
128
+ import { TranslationProvider } from '@/lib/i18n/provider';
110
129
 
111
- # Show diff vs last sync (no network)
112
- sync-translations status
130
+ export default function RootLayout({
131
+ children,
132
+ }: {
133
+ children: React.ReactNode;
134
+ }) {
135
+ return (
136
+ <html>
137
+ <body>
138
+ <TranslationProvider>{children}</TranslationProvider>
139
+ </body>
140
+ </html>
141
+ );
142
+ }
143
+ ```
113
144
 
114
- # Watch + auto-sync on file save
115
- sync-translations watch
145
+ ### B. Server Components (Aanbevolen)
116
146
 
117
- # Interactive setup
118
- sync-translations init
119
- ```
147
+ ```tsx
148
+ import { getTranslation } from '@/lib/i18n/server';
120
149
 
121
- **Output:** `dictionaries/{locale}.json` one file per locale, namespaces as
122
- top-level keys:
150
+ export default async function Page() {
151
+ const { t } = await getTranslation('common');
123
152
 
124
- ```json
125
- {
126
- "common": { "nav.home": "Home" },
127
- "auth": { "signIn": "Sign in" }
153
+ return (
154
+ <div>
155
+ <h1>{t('common:app.title')}</h1>
156
+ <p>{t('common:app.description')}</p>
157
+ </div>
158
+ );
128
159
  }
129
160
  ```
130
161
 
131
- ### Flags
132
-
133
- | Flag | Applies to | Description |
134
- | ----------------- | -------------- | ------------------------------------- |
135
- | `--dry-run` | `sync` | Print what would change, skip posting |
136
- | `--force` | `sync`, `pull` | Skip cache / force re-sync |
137
- | `--report <file>` | `sync` | Write JSON report to file |
138
- | `--output <dir>` | `pull` | Output directory for JSON files |
139
- | `--env <name>` | `pull` | Pull from a specific environment |
140
- | `--ttl <ms>` | `pull` | Override cache TTL |
141
- | `--project-id` | all | Override project ID |
142
- | `--api-key` | all | Override API key |
143
- | `--cms-url` | all | Override CMS URL |
162
+ ### C. Client Components
144
163
 
145
- ---
146
-
147
- ## i18next integration
164
+ ```tsx
165
+ 'use client';
166
+ import { useTranslation } from '@/lib/i18n/client';
148
167
 
149
- The scaffolded `client.ts` uses static imports so webpack bundles the
150
- translations at build time no flash, no loading state:
168
+ export function MyButton() {
169
+ const { t } = useTranslation('common');
151
170
 
152
- ```ts
153
- // src/lib/i18n/client.ts ('use client')
154
- import en from './dictionaries/en.json';
155
- import nl from './dictionaries/nl.json';
171
+ return <button>{t('common:button.click')}</button>;
172
+ }
173
+ ```
156
174
 
157
- const resources = { en, nl } as const;
175
+ ### D. Multiple Namespaces (Met Type-Checking)
158
176
 
159
- await i18nInstance.use(initReactI18next).init({
160
- ...getOptions(locale),
161
- resources,
162
- });
163
- ```
177
+ ```tsx
178
+ const { t } = useTranslation(['common', 'auth']);
164
179
 
165
- The scaffolded `server.ts` uses `i18next-resources-to-backend` with dynamic
166
- `import()` — no HTTP, reads directly from the filesystem:
180
+ // Dit werkt
181
+ t('common:nav.home');
182
+ t('auth:login.email');
167
183
 
168
- ```ts
169
- // src/lib/i18n/server.ts (RSC)
170
- .use(resourcesToBackend((lng, ns) =>
171
- import(`./dictionaries/${lng}.json`).then(m => m.default[ns] ?? {})
172
- ))
184
+ // ❌ Dit geeft een type error
185
+ t('payments:amount'); // namespace niet toegevoegd
173
186
  ```
174
187
 
175
- ### Usage
188
+ ## Stap 8: Automatische Sync in je Workflow
176
189
 
177
- ```tsx
178
- // Client component
179
- import { useTranslation } from '@/lib/i18n/client';
180
- const { t } = useTranslation('common');
181
- t('common:nav.home');
190
+ Voeg dit toe aan `package.json`:
182
191
 
183
- // Server component
184
- import { getTranslation } from '@/lib/i18n/server';
185
- const { t } = await getTranslation('common');
186
- t('common:nav.home');
192
+ ```json
193
+ {
194
+ "scripts": {
195
+ "dev": "sync-translations pull && next dev",
196
+ "build": "sync-translations pull && next build"
197
+ }
198
+ }
187
199
  ```
188
200
 
189
- ---
201
+ Nu pull je automatisch de latest vertalingen bij startup ⚡
202
+
203
+ ## Stap 9 (Optioneel): Preview Mode Instellen
190
204
 
191
- ## Preview Listener
205
+ Enable live in-context preview — editors kunnen live zien hoe hun tekst in je
206
+ app eruit ziet.
192
207
 
193
- Enable live in-context preview. When an editor selects a key in the CMS, the
194
- matching element in your app (loaded in an iframe) is highlighted. Navigation
195
- and interactions are blocked during preview.
208
+ Voeg dit toe aan je root layout:
196
209
 
197
210
  ```tsx
198
211
  'use client';
@@ -202,198 +215,293 @@ import { initPreviewListener } from '@translation-cms/sync';
202
215
  export function CMSPreview() {
203
216
  useEffect(() => {
204
217
  if (process.env.NODE_ENV === 'development') {
205
- initPreviewListener();
218
+ initPreviewListener({
219
+ onLocaleSwitch: locale => {
220
+ // wijzig taal als CMS een ander locale selecteert
221
+ window.location.href = `/${locale}`;
222
+ },
223
+ });
206
224
  }
207
225
  }, []);
208
226
  return null;
209
227
  }
210
228
  ```
211
229
 
212
- Add `<CMSPreview />` to your root layout.
230
+ En voeg toe in layout:
213
231
 
214
- ### `data-cms-key`
232
+ ```tsx
233
+ <TranslationProvider>
234
+ <CMSPreview />
235
+ {children}
236
+ </TranslationProvider>
237
+ ```
215
238
 
216
- For precise element targeting useful with interpolated values:
239
+ Voeg `data-cms-key` toe op elementen voor nauwkeurige highlighting:
217
240
 
218
241
  ```tsx
219
- <h1 data-cms-key="blog:post.title">{t('blog:post.title')}</h1>
220
- <p data-cms-key="common:welcome">{t('common:welcome', { name })}</p>
242
+ <h1 data-cms-key="common:page.title">{t('common:page.title')}</h1>
221
243
  ```
222
244
 
223
- ### Locale switching
245
+ ---
224
246
 
225
- ```ts
226
- initPreviewListener({
227
- onLocaleSwitch: locale => i18n.changeLanguage(locale),
228
- });
247
+ # CLI Commands Reference
248
+
249
+ ## Basiscommands
250
+
251
+ ```bash
252
+ # Scan code + upload naar CMS
253
+ pnpm sync-translations sync
254
+
255
+ # Download vertalingen naar lokale JSON
256
+ pnpm sync-translations pull
257
+
258
+ # Bekijk wat er zou veranderen (zonder upload)
259
+ pnpm sync-translations sync --dry-run
260
+
261
+ # Bekijk verschil met vorige sync
262
+ pnpm sync-translations status
263
+
264
+ # Watch mode — auto-sync bij bestand wijzigingen
265
+ pnpm sync-translations watch
266
+
267
+ # Interactieve setup
268
+ pnpm sync-translations init
229
269
  ```
230
270
 
231
- ### Custom highlight styles
271
+ ## Advanced Flags
232
272
 
233
- ```ts
234
- initPreviewListener({
235
- highlightStyles: {
236
- outline: '3px solid #3b82f6',
237
- outlineOffset: '4px',
238
- backgroundColor: 'rgba(59, 130, 246, 0.1)',
239
- },
240
- });
273
+ | Flag | Beschrijving |
274
+ | ----------------- | ------------------------------------ |
275
+ | `--dry-run` | Toon wijzigingen, voer niet uit |
276
+ | `--force` | Negeer cache, force hernieuwen |
277
+ | `--output <dir>` | Custom output map voor JSON |
278
+ | `--env <name>` | Pull uit staging/production omgeving |
279
+ | `--ttl <ms>` | Overschrijf cache TTL (ms) |
280
+ | `--project-id` | Overschrijf project ID |
281
+ | `--api-key` | Overschrijf API key |
282
+ | `--cms-url` | Overschrijf CMS URL |
283
+ | `--report <file>` | Schrijf sync rapport naar JSON file |
284
+
285
+ ## Voorbeelden
286
+
287
+ ```bash
288
+ # Force refresh, negeer cache
289
+ pnpm sync-translations pull --force
290
+
291
+ # Custom output directory
292
+ pnpm sync-translations pull --output ./locales
293
+
294
+ # Pull uit staging environment
295
+ pnpm sync-translations pull --env staging
296
+
297
+ # Schrijf rapport van wijzigingen
298
+ pnpm sync-translations sync --report ./sync-report.json
241
299
  ```
242
300
 
243
- ### Cleanup
301
+ ---
302
+
303
+ # Key Scanner — Hoe Werkt Het
304
+
305
+ De tool herkent deze patronen in je code:
244
306
 
245
307
  ```ts
246
- import { cleanupPreviewListener } from '@translation-cms/sync';
247
- cleanupPreviewListener();
248
- ```
308
+ // React-i18next hook
309
+ const { t } = useTranslation('blog');
310
+ t('blog:post.title');
249
311
 
250
- ### Triggering actions (dialogs, menus, etc.)
312
+ // Trans component
313
+ <Trans i18nKey="blog:post.title" />
251
314
 
252
- For translations inside dialogs or collapsed menus, register an action callback
253
- that opens the UI when the CMS sends a trigger:
315
+ // String literal
316
+ const key = 'blog:post.title';
317
+ ```
254
318
 
255
- ```tsx
256
- 'use client';
257
- import { useState, useEffect } from 'react';
258
- import {
259
- initPreviewListener,
260
- registerPreviewAction,
261
- } from '@translation-cms/sync';
319
+ ### Format: `namespace:key`
262
320
 
263
- export function NavMenu() {
264
- const [open, setOpen] = useState(false);
321
+ Alle keys moeten dit format volgen. Anders krijg je een warning:
265
322
 
266
- useEffect(() => {
267
- if (process.env.NODE_ENV === 'development') {
268
- // Register so the CMS can open the menu during preview
269
- registerPreviewAction('nav.menu', () => {
270
- setOpen(true);
271
- });
272
- }
273
- }, []);
323
+ ```ts
324
+ // Goed
325
+ t('common:button.save');
274
326
 
275
- return (
276
- <nav>
277
- <button onClick={() => setOpen(!open)}>Menu</button>
278
- {open && (
279
- <div>
280
- <a href="/">
281
- <span data-cms-key="nav:home">{t('nav:home')}</span>
282
- </a>
283
- <a href="/about">
284
- <span data-cms-key="nav:about">{t('nav:about')}</span>
285
- </a>
286
- </div>
287
- )}
288
- </nav>
289
- );
290
- }
327
+ // ❌ Fout — namespace mist
328
+ t('save');
291
329
  ```
292
330
 
293
- #### Setting up action_id in the CMS
331
+ ### Dynamische Routes Auto-Generated routeParams
294
332
 
295
- In the Translations CMS, each translation key can have an `action_id`:
333
+ **Hoe het werkt:**
296
334
 
297
- 1. Click the **Edit** button (pencil icon) on a translation key
298
- 2. Fill in **Preview action** field with your action ID (e.g., `nav.menu`,
299
- `modal.signup`)
300
- 3. Click **Update**
335
+ Bij elke `pnpm sync-translations sync`:
301
336
 
302
- When an editor clicks "Show in app" for that key, the CMS will:
337
+ 1. Scanner detecteert automatisch alle routes met dynamic parameters (bijv.
338
+ `/[locale]/blog/[slug]`)
339
+ 2. Voor elke `[param]` wordt automatisch een sensible default gegenereerd:
340
+ - `locale` / `lang` → `"en"`
341
+ - `id`, `postId`, `productId` → `"123"`
342
+ - `slug` → `"demo"`
343
+ - `username` → `"demo-user"`
344
+ - `email` → `"demo@example.com"`
345
+ 3. Dit wordt opgeslagen in `.cms-sync-cache-meta.json` (mag je negeren)
346
+ 4. De CMS gebruikt deze om werkende preview URLs te genereren
303
347
 
304
- 1. Load your app in an iframe
305
- 2. Auto-open the menu/dialog/modal by sending a `CMS_TRIGGER_ACTION` message
306
- 3. Highlight the matching `data-cms-key` element
348
+ **Je hoeft niks te doen!** alles werkt out-of-the-box.
307
349
 
308
- **Action IDs** are namespaced identifiers you define — they should match action
309
- names registered during initialization. Common patterns:
350
+ **Handmatig overschrijven (optioneel):**
310
351
 
311
- ```
312
- nav.menu → Opens main navigation menu
313
- nav.mobile-menu → Opens mobile hamburger menu
314
- modal.signup → Opens signup modal
315
- modal.login → Opens login modal
316
- dropdown.language → Opens language selector
317
- sidebar.filter Opens filter sidebar
318
- accordion.faq → Opens FAQ accordion
319
- tabs.pricing → Switches to pricing tab
352
+ Als je andere test-waarden wil gebruiken, vul je `.translationsrc.json` in:
353
+
354
+ ```json
355
+ {
356
+ "routeParams": {
357
+ "/[locale]/products/[id]": { "id": "prod-999" },
358
+ "/[locale]/blog/[slug]": { "slug": "my-custom-post" }
359
+ }
360
+ }
320
361
  ```
321
362
 
322
- **Complete workflow example:**
363
+ Dit overschrijft de auto-generated waarden. Alles wat je hier toevoegt krijgt
364
+ prioriteit.
323
365
 
324
- 1. **Client app registers action:**
366
+ ### Cache bestanden
325
367
 
326
- ```ts
327
- registerPreviewAction('nav.mobile-menu', () => setMobileMenuOpen(true));
328
- ```
368
+ Bij elke sync worden twee cache bestanden aangemaakt:
329
369
 
330
- 2. **CMS Editor:**
331
- - Selects key `common:nav.home`
332
- - Clicks Edit
333
- - Sets **Preview action** to `nav.mobile-menu`
334
- - Clicks Update
370
+ - `.cms-sync-cache.json` Geüploade keys en hun routes (voor diff bij volgende
371
+ sync)
372
+ - `.cms-sync-cache-meta.json` — Auto-generated route params (mag je .gitignore
373
+ toevoegen)
335
374
 
336
- 3. **CMS Preview:**
337
- - Editor clicks "Show in app"
338
- - Mobile menu auto-opens
339
- - `<span data-cms-key="common:nav.home">` element is highlighted in yellow
340
- - Editor sees the translation in context
375
+ ### Scanner Opties
341
376
 
342
- ---
377
+ Customize scan gedrag in `.translationsrc.json`:
343
378
 
344
- ### How it works
379
+ ```json
380
+ {
381
+ "excludedDirs": ["e2e", "node_modules", "fixtures"],
382
+ "sourceExtensions": [".ts", ".tsx", ".js", ".jsx"],
383
+ "reservedCssNamespaces": ["after", "before"]
384
+ }
385
+ ```
345
386
 
346
387
  ---
347
388
 
348
- ## Scanner
389
+ # Advanced Features
349
390
 
350
- The CLI scans your source files for translation keys. Recognised patterns:
391
+ ## Preview Mode Live Highlighting
351
392
 
352
- ```ts
353
- // react-i18next
354
- const { t } = useTranslation('blog');
355
- t('blog:post.title');
393
+ Editors kunnen in de CMS klikken op "Show in app" en je app opent in een iframe
394
+ met live highlighting van het element.
356
395
 
357
- // <Trans> component
358
- <Trans i18nKey="blog:post.title" />
396
+ ### Setup
359
397
 
360
- // Standalone string literals
361
- const key = 'blog:post.title';
398
+ ```tsx
399
+ 'use client';
400
+ import { initPreviewListener } from '@translation-cms/sync';
401
+
402
+ useEffect(() => {
403
+ initPreviewListener({
404
+ highlightStyles: {
405
+ outline: '3px solid #3b82f6',
406
+ outlineOffset: '2px',
407
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
408
+ },
409
+ });
410
+ }, []);
411
+ ```
412
+
413
+ ### Element Targeting
414
+
415
+ Gebruik `data-cms-key` voor nauwkeurige targeting:
416
+
417
+ ```tsx
418
+ <h1 data-cms-key="blog:title">{t('blog:title')}</h1>
419
+ <p data-cms-key="blog:excerpt">{t('blog:excerpt')}</p>
362
420
  ```
363
421
 
364
- All keys must use `namespace:key` format. Bare keys (`t('save')`) produce a
365
- warning.
422
+ ---
366
423
 
367
- ### `routeParams` mock params for dynamic routes
424
+ # JSON Output Format
368
425
 
369
- The scanner detects which route each key is used on automatically. For dynamic
370
- routes (e.g. `/[locale]/blog/[slug]`), add mock params in `.translationsrc.json`
371
- so the CMS can generate a working preview URL:
426
+ Na `sync-translations pull` worden dit soort bestanden gegenereerd:
372
427
 
373
428
  ```json
374
429
  {
375
- "routeParams": {
376
- "/[locale]/blog/[slug]": { "slug": "first-post" },
377
- "/[locale]/products/[slug]": { "slug": "product-a" }
430
+ "common": {
431
+ "app.title": "Mijn App",
432
+ "button.save": "Opslaan",
433
+ "nav.home": "Home"
434
+ },
435
+ "auth": {
436
+ "login.email": "Email",
437
+ "login.password": "Wachtwoord"
378
438
  }
379
439
  }
380
440
  ```
381
441
 
382
- The scanner applies these automatically to every key it detects on that route —
383
- no need to repeat params per key.
442
+ **Structuur:**
384
443
 
385
- ### Scan options (`.translationsrc.json`)
444
+ - Top-level keys = namespaces
445
+ - Nested keys = translation strings
446
+ - 1 bestand per taal (`en.json`, `nl.json`, etc.)
386
447
 
387
- ```json
388
- {
389
- "excludedDirs": ["e2e", "fixtures"],
390
- "sourceExtensions": [".ts", ".tsx", ".js", ".jsx", ".vue"],
391
- "reservedCssNamespaces": ["after", "before"]
392
- }
448
+ ---
449
+
450
+ # Troubleshooting
451
+
452
+ ### Geen keys gevonden
453
+
454
+ ```bash
455
+ # Check scanner configuratie
456
+ pnpm sync-translations sync --dry-run
393
457
  ```
394
458
 
459
+ Zorg dat je keys `namespace:key` format gebruiken.
460
+
461
+ ### Environment variables niet gevonden
462
+
463
+ Controleer `.env.local`:
464
+
465
+ ```bash
466
+ cat .env.local | grep NEXT_PUBLIC_CMS
467
+ ```
468
+
469
+ Moet `NEXT_PUBLIC_CMS_URL`, `NEXT_PUBLIC_CMS_PROJECT_ID`,
470
+ `NEXT_PUBLIC_CMS_ANON_KEY` bevatten.
471
+
472
+ ### Pull werkt niet
473
+
474
+ ```bash
475
+ # Force refresh, negeer cache
476
+ pnpm sync-translations pull --force
477
+
478
+ # Check configuratie
479
+ cat .translationsrc.json
480
+ ```
481
+
482
+ ### Type errors in TypeScript
483
+
484
+ Zorg dat `src/lib/i18n/types.ts` correct gegenereerd is en dat je namespaces in
485
+ `.translationsrc.json` staan.
486
+
487
+ ---
488
+
489
+ # Best Practices
490
+
491
+ 1. **Gebruik server components** waar mogelijk — beter voor performance
492
+ 2. **Voeg `data-cms-key` toe** op visueel belangrijke elementen
493
+ 3. **Commit `.translationsrc.json`** naar git — niet `.env.local`
494
+ 4. **Negeer cache bestanden:** voeg toe aan `.gitignore`:
495
+ ```
496
+ .cms-sync-cache.json
497
+ .cms-sync-cache-meta.json
498
+ .last-pulled
499
+ ```
500
+ 5. **Run `sync` in CI/CD** op main branch — catch missing translations
501
+ 6. **Voeg `pull` toe aan build script** — altijd fresh translations
502
+
395
503
  ---
396
504
 
397
- ## License
505
+ # License
398
506
 
399
507
  MIT