@translation-cms/sync 1.2.4 → 1.2.6

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,175 @@ 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
+ **Bij eerste sync:** je krijgt mogelijk een error dat geen vertalingen kunnen
112
+ worden gedownload dit is normaal! De keys zijn net geupload maar hebben nog
113
+ geen vertalingen. Ga naar je CMS, vul vertalingen in, dan draai je `pull`
114
+ opnieuw.
83
115
 
84
- Add to your `package.json` so translations are always fresh:
116
+ ## Stap 5b: Vertalingen Toevoegen in CMS
85
117
 
86
- ```json
87
- "scripts": {
88
- "dev": "sync-translations pull && next dev",
89
- "build": "sync-translations pull && next build"
90
- }
91
- ```
118
+ 1. Open je Translations CMS project
119
+ 2. Je keys staan nu gelistd (zonder vertalingen)
120
+ 3. Vul vertalingen in per locale (English, Nederlands, etc.)
121
+ 4. Klik Save
92
122
 
93
- ---
123
+ ## Stap 6: Vertalingen Ophalen
94
124
 
95
- ## CLI
125
+ Als je de eerste sync hebt gedaan EN vertalingen in de CMS hebt ingevuld:
96
126
 
97
127
  ```bash
98
- # Scan keys + push to CMS
99
- sync-translations sync
128
+ pnpm sync-translations pull
129
+ ```
100
130
 
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
131
+ Dit haalt vertalingen op van de CMS en schrijft ze naar lokale JSON:
107
132
 
108
- # Preview changes without touching the CMS
109
- sync-translations sync --dry-run
133
+ - `dictionaries/en.json` English vertalingen
134
+ - `dictionaries/nl.json` → Nederlandse vertalingen
135
+ - etc. (één per locale)
110
136
 
111
- # Show diff vs last sync (no network)
112
- sync-translations status
137
+ ## Stap 7: Integratie in je Project
113
138
 
114
- # Watch + auto-sync on file save
115
- sync-translations watch
139
+ ### A. Root Layout Setup
116
140
 
117
- # Interactive setup
118
- sync-translations init
119
- ```
141
+ Voeg de provider toe aan je `src/app/layout.tsx`:
120
142
 
121
- **Output:** `dictionaries/{locale}.json` — one file per locale, namespaces as
122
- top-level keys:
143
+ ```tsx
144
+ import { TranslationProvider } from '@/lib/i18n/provider';
123
145
 
124
- ```json
125
- {
126
- "common": { "nav.home": "Home" },
127
- "auth": { "signIn": "Sign in" }
146
+ export default function RootLayout({
147
+ children,
148
+ }: {
149
+ children: React.ReactNode;
150
+ }) {
151
+ return (
152
+ <html>
153
+ <body>
154
+ <TranslationProvider>{children}</TranslationProvider>
155
+ </body>
156
+ </html>
157
+ );
128
158
  }
129
159
  ```
130
160
 
131
- ### Flags
161
+ ### B. Server Components (Aanbevolen)
132
162
 
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 |
163
+ ```tsx
164
+ import { getTranslation } from '@/lib/i18n/server';
144
165
 
145
- ---
166
+ export default async function Page() {
167
+ const { t } = await getTranslation('common');
146
168
 
147
- ## i18next integration
169
+ return (
170
+ <div>
171
+ <h1>{t('common:app.title')}</h1>
172
+ <p>{t('common:app.description')}</p>
173
+ </div>
174
+ );
175
+ }
176
+ ```
148
177
 
149
- The scaffolded `client.ts` uses static imports so webpack bundles the
150
- translations at build time — no flash, no loading state:
178
+ ### C. Client Components
151
179
 
152
- ```ts
153
- // src/lib/i18n/client.ts ('use client')
154
- import en from './dictionaries/en.json';
155
- import nl from './dictionaries/nl.json';
180
+ ```tsx
181
+ 'use client';
182
+ import { useTranslation } from '@/lib/i18n/client';
156
183
 
157
- const resources = { en, nl } as const;
184
+ export function MyButton() {
185
+ const { t } = useTranslation('common');
158
186
 
159
- await i18nInstance.use(initReactI18next).init({
160
- ...getOptions(locale),
161
- resources,
162
- });
187
+ return <button>{t('common:button.click')}</button>;
188
+ }
163
189
  ```
164
190
 
165
- The scaffolded `server.ts` uses `i18next-resources-to-backend` with dynamic
166
- `import()` — no HTTP, reads directly from the filesystem:
191
+ ### D. Multiple Namespaces (Met Type-Checking)
167
192
 
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
- ))
193
+ ```tsx
194
+ const { t } = useTranslation(['common', 'auth']);
195
+
196
+ // Dit werkt
197
+ t('common:nav.home');
198
+ t('auth:login.email');
199
+
200
+ // ❌ Dit geeft een type error
201
+ t('payments:amount'); // namespace niet toegevoegd
173
202
  ```
174
203
 
175
- ### Usage
204
+ ## Stap 8: Automatische Sync in je Workflow
176
205
 
177
- ```tsx
178
- // Client component
179
- import { useTranslation } from '@/lib/i18n/client';
180
- const { t } = useTranslation('common');
181
- t('common:nav.home');
206
+ Voeg dit toe aan `package.json`:
182
207
 
183
- // Server component
184
- import { getTranslation } from '@/lib/i18n/server';
185
- const { t } = await getTranslation('common');
186
- t('common:nav.home');
208
+ ```json
209
+ {
210
+ "scripts": {
211
+ "dev": "sync-translations pull && next dev",
212
+ "build": "sync-translations pull && next build"
213
+ }
214
+ }
187
215
  ```
188
216
 
189
- ---
217
+ Nu pull je automatisch de latest vertalingen bij startup ⚡
218
+
219
+ ## Stap 9 (Optioneel): Preview Mode Instellen
190
220
 
191
- ## Preview Listener
221
+ Enable live in-context preview — editors kunnen live zien hoe hun tekst in je
222
+ app eruit ziet.
192
223
 
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.
224
+ Voeg dit toe aan je root layout:
196
225
 
197
226
  ```tsx
198
227
  'use client';
@@ -202,198 +231,293 @@ import { initPreviewListener } from '@translation-cms/sync';
202
231
  export function CMSPreview() {
203
232
  useEffect(() => {
204
233
  if (process.env.NODE_ENV === 'development') {
205
- initPreviewListener();
234
+ initPreviewListener({
235
+ onLocaleSwitch: locale => {
236
+ // wijzig taal als CMS een ander locale selecteert
237
+ window.location.href = `/${locale}`;
238
+ },
239
+ });
206
240
  }
207
241
  }, []);
208
242
  return null;
209
243
  }
210
244
  ```
211
245
 
212
- Add `<CMSPreview />` to your root layout.
246
+ En voeg toe in layout:
213
247
 
214
- ### `data-cms-key`
248
+ ```tsx
249
+ <TranslationProvider>
250
+ <CMSPreview />
251
+ {children}
252
+ </TranslationProvider>
253
+ ```
215
254
 
216
- For precise element targeting useful with interpolated values:
255
+ Voeg `data-cms-key` toe op elementen voor nauwkeurige highlighting:
217
256
 
218
257
  ```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>
258
+ <h1 data-cms-key="common:page.title">{t('common:page.title')}</h1>
221
259
  ```
222
260
 
223
- ### Locale switching
261
+ ---
224
262
 
225
- ```ts
226
- initPreviewListener({
227
- onLocaleSwitch: locale => i18n.changeLanguage(locale),
228
- });
263
+ # CLI Commands Reference
264
+
265
+ ## Basiscommands
266
+
267
+ ```bash
268
+ # Scan code + upload naar CMS
269
+ pnpm sync-translations sync
270
+
271
+ # Download vertalingen naar lokale JSON
272
+ pnpm sync-translations pull
273
+
274
+ # Bekijk wat er zou veranderen (zonder upload)
275
+ pnpm sync-translations sync --dry-run
276
+
277
+ # Bekijk verschil met vorige sync
278
+ pnpm sync-translations status
279
+
280
+ # Watch mode — auto-sync bij bestand wijzigingen
281
+ pnpm sync-translations watch
282
+
283
+ # Interactieve setup
284
+ pnpm sync-translations init
229
285
  ```
230
286
 
231
- ### Custom highlight styles
287
+ ## Advanced Flags
232
288
 
233
- ```ts
234
- initPreviewListener({
235
- highlightStyles: {
236
- outline: '3px solid #3b82f6',
237
- outlineOffset: '4px',
238
- backgroundColor: 'rgba(59, 130, 246, 0.1)',
239
- },
240
- });
289
+ | Flag | Beschrijving |
290
+ | ----------------- | ------------------------------------ |
291
+ | `--dry-run` | Toon wijzigingen, voer niet uit |
292
+ | `--force` | Negeer cache, force hernieuwen |
293
+ | `--output <dir>` | Custom output map voor JSON |
294
+ | `--env <name>` | Pull uit staging/production omgeving |
295
+ | `--ttl <ms>` | Overschrijf cache TTL (ms) |
296
+ | `--project-id` | Overschrijf project ID |
297
+ | `--api-key` | Overschrijf API key |
298
+ | `--cms-url` | Overschrijf CMS URL |
299
+ | `--report <file>` | Schrijf sync rapport naar JSON file |
300
+
301
+ ## Voorbeelden
302
+
303
+ ```bash
304
+ # Force refresh, negeer cache
305
+ pnpm sync-translations pull --force
306
+
307
+ # Custom output directory
308
+ pnpm sync-translations pull --output ./locales
309
+
310
+ # Pull uit staging environment
311
+ pnpm sync-translations pull --env staging
312
+
313
+ # Schrijf rapport van wijzigingen
314
+ pnpm sync-translations sync --report ./sync-report.json
241
315
  ```
242
316
 
243
- ### Cleanup
317
+ ---
318
+
319
+ # Key Scanner — Hoe Werkt Het
320
+
321
+ De tool herkent deze patronen in je code:
244
322
 
245
323
  ```ts
246
- import { cleanupPreviewListener } from '@translation-cms/sync';
247
- cleanupPreviewListener();
248
- ```
324
+ // React-i18next hook
325
+ const { t } = useTranslation('blog');
326
+ t('blog:post.title');
249
327
 
250
- ### Triggering actions (dialogs, menus, etc.)
328
+ // Trans component
329
+ <Trans i18nKey="blog:post.title" />
251
330
 
252
- For translations inside dialogs or collapsed menus, register an action callback
253
- that opens the UI when the CMS sends a trigger:
331
+ // String literal
332
+ const key = 'blog:post.title';
333
+ ```
254
334
 
255
- ```tsx
256
- 'use client';
257
- import { useState, useEffect } from 'react';
258
- import {
259
- initPreviewListener,
260
- registerPreviewAction,
261
- } from '@translation-cms/sync';
335
+ ### Format: `namespace:key`
262
336
 
263
- export function NavMenu() {
264
- const [open, setOpen] = useState(false);
337
+ Alle keys moeten dit format volgen. Anders krijg je een warning:
265
338
 
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
- }, []);
339
+ ```ts
340
+ // Goed
341
+ t('common:button.save');
274
342
 
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
- }
343
+ // ❌ Fout — namespace mist
344
+ t('save');
291
345
  ```
292
346
 
293
- #### Setting up action_id in the CMS
347
+ ### Dynamische Routes Auto-Generated routeParams
294
348
 
295
- In the Translations CMS, each translation key can have an `action_id`:
349
+ **Hoe het werkt:**
296
350
 
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**
351
+ Bij elke `pnpm sync-translations sync`:
301
352
 
302
- When an editor clicks "Show in app" for that key, the CMS will:
353
+ 1. Scanner detecteert automatisch alle routes met dynamic parameters (bijv.
354
+ `/[locale]/blog/[slug]`)
355
+ 2. Voor elke `[param]` wordt automatisch een sensible default gegenereerd:
356
+ - `locale` / `lang` → `"en"`
357
+ - `id`, `postId`, `productId` → `"123"`
358
+ - `slug` → `"demo"`
359
+ - `username` → `"demo-user"`
360
+ - `email` → `"demo@example.com"`
361
+ 3. Dit wordt opgeslagen in `.cms-sync-cache-meta.json` (mag je negeren)
362
+ 4. De CMS gebruikt deze om werkende preview URLs te genereren
303
363
 
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
364
+ **Je hoeft niks te doen!** alles werkt out-of-the-box.
307
365
 
308
- **Action IDs** are namespaced identifiers you define — they should match action
309
- names registered during initialization. Common patterns:
366
+ **Handmatig overschrijven (optioneel):**
310
367
 
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
368
+ Als je andere test-waarden wil gebruiken, vul je `.translationsrc.json` in:
369
+
370
+ ```json
371
+ {
372
+ "routeParams": {
373
+ "/[locale]/products/[id]": { "id": "prod-999" },
374
+ "/[locale]/blog/[slug]": { "slug": "my-custom-post" }
375
+ }
376
+ }
320
377
  ```
321
378
 
322
- **Complete workflow example:**
379
+ Dit overschrijft de auto-generated waarden. Alles wat je hier toevoegt krijgt
380
+ prioriteit.
323
381
 
324
- 1. **Client app registers action:**
382
+ ### Cache bestanden
325
383
 
326
- ```ts
327
- registerPreviewAction('nav.mobile-menu', () => setMobileMenuOpen(true));
328
- ```
384
+ Bij elke sync worden twee cache bestanden aangemaakt:
329
385
 
330
- 2. **CMS Editor:**
331
- - Selects key `common:nav.home`
332
- - Clicks Edit
333
- - Sets **Preview action** to `nav.mobile-menu`
334
- - Clicks Update
386
+ - `.cms-sync-cache.json` Geüploade keys en hun routes (voor diff bij volgende
387
+ sync)
388
+ - `.cms-sync-cache-meta.json` — Auto-generated route params (mag je .gitignore
389
+ toevoegen)
335
390
 
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
391
+ ### Scanner Opties
341
392
 
342
- ---
393
+ Customize scan gedrag in `.translationsrc.json`:
343
394
 
344
- ### How it works
395
+ ```json
396
+ {
397
+ "excludedDirs": ["e2e", "node_modules", "fixtures"],
398
+ "sourceExtensions": [".ts", ".tsx", ".js", ".jsx"],
399
+ "reservedCssNamespaces": ["after", "before"]
400
+ }
401
+ ```
345
402
 
346
403
  ---
347
404
 
348
- ## Scanner
405
+ # Advanced Features
349
406
 
350
- The CLI scans your source files for translation keys. Recognised patterns:
407
+ ## Preview Mode Live Highlighting
351
408
 
352
- ```ts
353
- // react-i18next
354
- const { t } = useTranslation('blog');
355
- t('blog:post.title');
409
+ Editors kunnen in de CMS klikken op "Show in app" en je app opent in een iframe
410
+ met live highlighting van het element.
356
411
 
357
- // <Trans> component
358
- <Trans i18nKey="blog:post.title" />
412
+ ### Setup
359
413
 
360
- // Standalone string literals
361
- const key = 'blog:post.title';
414
+ ```tsx
415
+ 'use client';
416
+ import { initPreviewListener } from '@translation-cms/sync';
417
+
418
+ useEffect(() => {
419
+ initPreviewListener({
420
+ highlightStyles: {
421
+ outline: '3px solid #3b82f6',
422
+ outlineOffset: '2px',
423
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
424
+ },
425
+ });
426
+ }, []);
362
427
  ```
363
428
 
364
- All keys must use `namespace:key` format. Bare keys (`t('save')`) produce a
365
- warning.
429
+ ### Element Targeting
430
+
431
+ Gebruik `data-cms-key` voor nauwkeurige targeting:
432
+
433
+ ```tsx
434
+ <h1 data-cms-key="blog:title">{t('blog:title')}</h1>
435
+ <p data-cms-key="blog:excerpt">{t('blog:excerpt')}</p>
436
+ ```
437
+
438
+ ---
366
439
 
367
- ### `routeParams` mock params for dynamic routes
440
+ # JSON Output Format
368
441
 
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:
442
+ Na `sync-translations pull` worden dit soort bestanden gegenereerd:
372
443
 
373
444
  ```json
374
445
  {
375
- "routeParams": {
376
- "/[locale]/blog/[slug]": { "slug": "first-post" },
377
- "/[locale]/products/[slug]": { "slug": "product-a" }
446
+ "common": {
447
+ "app.title": "Mijn App",
448
+ "button.save": "Opslaan",
449
+ "nav.home": "Home"
450
+ },
451
+ "auth": {
452
+ "login.email": "Email",
453
+ "login.password": "Wachtwoord"
378
454
  }
379
455
  }
380
456
  ```
381
457
 
382
- The scanner applies these automatically to every key it detects on that route —
383
- no need to repeat params per key.
458
+ **Structuur:**
384
459
 
385
- ### Scan options (`.translationsrc.json`)
460
+ - Top-level keys = namespaces
461
+ - Nested keys = translation strings
462
+ - 1 bestand per taal (`en.json`, `nl.json`, etc.)
386
463
 
387
- ```json
388
- {
389
- "excludedDirs": ["e2e", "fixtures"],
390
- "sourceExtensions": [".ts", ".tsx", ".js", ".jsx", ".vue"],
391
- "reservedCssNamespaces": ["after", "before"]
392
- }
464
+ ---
465
+
466
+ # Troubleshooting
467
+
468
+ ### Geen keys gevonden
469
+
470
+ ```bash
471
+ # Check scanner configuratie
472
+ pnpm sync-translations sync --dry-run
393
473
  ```
394
474
 
475
+ Zorg dat je keys `namespace:key` format gebruiken.
476
+
477
+ ### Environment variables niet gevonden
478
+
479
+ Controleer `.env.local`:
480
+
481
+ ```bash
482
+ cat .env.local | grep NEXT_PUBLIC_CMS
483
+ ```
484
+
485
+ Moet `NEXT_PUBLIC_CMS_URL`, `NEXT_PUBLIC_CMS_PROJECT_ID`,
486
+ `NEXT_PUBLIC_CMS_ANON_KEY` bevatten.
487
+
488
+ ### Pull werkt niet
489
+
490
+ ```bash
491
+ # Force refresh, negeer cache
492
+ pnpm sync-translations pull --force
493
+
494
+ # Check configuratie
495
+ cat .translationsrc.json
496
+ ```
497
+
498
+ ### Type errors in TypeScript
499
+
500
+ Zorg dat `src/lib/i18n/types.ts` correct gegenereerd is en dat je namespaces in
501
+ `.translationsrc.json` staan.
502
+
503
+ ---
504
+
505
+ # Best Practices
506
+
507
+ 1. **Gebruik server components** waar mogelijk — beter voor performance
508
+ 2. **Voeg `data-cms-key` toe** op visueel belangrijke elementen
509
+ 3. **Commit `.translationsrc.json`** naar git — niet `.env.local`
510
+ 4. **Negeer cache bestanden:** voeg toe aan `.gitignore`:
511
+ ```
512
+ .cms-sync-cache.json
513
+ .cms-sync-cache-meta.json
514
+ .last-pulled
515
+ ```
516
+ 5. **Run `sync` in CI/CD** op main branch — catch missing translations
517
+ 6. **Voeg `pull` toe aan build script** — altijd fresh translations
518
+
395
519
  ---
396
520
 
397
- ## License
521
+ # License
398
522
 
399
523
  MIT