@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 +358 -250
- package/dist/bin.js +7 -4
- package/dist/bin.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +19 -1
- package/dist/commands/sync.js.map +1 -1
- package/dist/config/resolve-config.d.ts +6 -0
- package/dist/config/resolve-config.d.ts.map +1 -1
- package/dist/config/resolve-config.js +16 -0
- package/dist/config/resolve-config.js.map +1 -1
- package/dist/core/api-internals/pull.d.ts +1 -1
- package/dist/core/api-internals/pull.d.ts.map +1 -1
- package/dist/core/api-internals/pull.js +165 -43
- package/dist/core/api-internals/pull.js.map +1 -1
- package/dist/core/api-internals/sync.d.ts.map +1 -1
- package/dist/core/api-internals/sync.js.map +1 -1
- package/dist/core/cache.d.ts +23 -0
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +90 -0
- package/dist/core/cache.js.map +1 -1
- package/dist/core/scanner-internals/key-extractor.d.ts.map +1 -1
- package/dist/core/scanner-internals/key-extractor.js +5 -1
- package/dist/core/scanner-internals/key-extractor.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,29 +1,51 @@
|
|
|
1
1
|
# @translation-cms/sync
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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 Code → scan → CMS → pull → JSON files → i18next → Your App
|
|
12
19
|
```
|
|
13
20
|
|
|
14
21
|
---
|
|
15
22
|
|
|
16
|
-
|
|
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
|
-
##
|
|
46
|
+
## Stap 3: CMS Credentials Instellen
|
|
25
47
|
|
|
26
|
-
|
|
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
|
-
|
|
56
|
+
Deze krijg je uit je CMS project settings.
|
|
35
57
|
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
**
|
|
52
|
-
|
|
53
|
-
---
|
|
91
|
+
**Let op:** `routeParams` kan leeg blijven! De scanner genereert automatisch
|
|
92
|
+
sensible defaults voor alle dynamic routes.
|
|
54
93
|
|
|
55
|
-
##
|
|
94
|
+
## Stap 5: Eerste Sync — Keys Uploaden
|
|
56
95
|
|
|
57
|
-
|
|
96
|
+
Scan je code en upload gedetecteerde keys naar de CMS:
|
|
58
97
|
|
|
59
98
|
```bash
|
|
60
|
-
sync-translations
|
|
99
|
+
pnpm sync-translations sync
|
|
61
100
|
```
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
Dit zal:
|
|
64
103
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
109
|
+
Controleer in de CMS of je keys verschenen zijn ✅
|
|
78
110
|
|
|
79
|
-
|
|
80
|
-
pnpm add i18next react-i18next i18next-resources-to-backend
|
|
81
|
-
sync-translations pull
|
|
82
|
-
```
|
|
111
|
+
## Stap 6: Vertalingen Ophalen
|
|
83
112
|
|
|
84
|
-
|
|
113
|
+
Download vertalingen van de CMS en schrijf ze naar lokale JSON:
|
|
85
114
|
|
|
86
|
-
```
|
|
87
|
-
|
|
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
|
-
##
|
|
121
|
+
## Stap 7: Integratie in je Project
|
|
96
122
|
|
|
97
|
-
|
|
98
|
-
# Scan keys + push to CMS
|
|
99
|
-
sync-translations sync
|
|
123
|
+
### A. Root Layout Setup
|
|
100
124
|
|
|
101
|
-
|
|
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
|
-
|
|
109
|
-
|
|
127
|
+
```tsx
|
|
128
|
+
import { TranslationProvider } from '@/lib/i18n/provider';
|
|
110
129
|
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
115
|
-
sync-translations watch
|
|
145
|
+
### B. Server Components (Aanbevolen)
|
|
116
146
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
147
|
+
```tsx
|
|
148
|
+
import { getTranslation } from '@/lib/i18n/server';
|
|
120
149
|
|
|
121
|
-
|
|
122
|
-
|
|
150
|
+
export default async function Page() {
|
|
151
|
+
const { t } = await getTranslation('common');
|
|
123
152
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
164
|
+
```tsx
|
|
165
|
+
'use client';
|
|
166
|
+
import { useTranslation } from '@/lib/i18n/client';
|
|
148
167
|
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
export function MyButton() {
|
|
169
|
+
const { t } = useTranslation('common');
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
import nl from './dictionaries/nl.json';
|
|
171
|
+
return <button>{t('common:button.click')}</button>;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
156
174
|
|
|
157
|
-
|
|
175
|
+
### D. Multiple Namespaces (Met Type-Checking)
|
|
158
176
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
resources,
|
|
162
|
-
});
|
|
163
|
-
```
|
|
177
|
+
```tsx
|
|
178
|
+
const { t } = useTranslation(['common', 'auth']);
|
|
164
179
|
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
// ✅ Dit werkt
|
|
181
|
+
t('common:nav.home');
|
|
182
|
+
t('auth:login.email');
|
|
167
183
|
|
|
168
|
-
|
|
169
|
-
//
|
|
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
|
-
|
|
188
|
+
## Stap 8: Automatische Sync in je Workflow
|
|
176
189
|
|
|
177
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
205
|
+
Enable live in-context preview — editors kunnen live zien hoe hun tekst in je
|
|
206
|
+
app eruit ziet.
|
|
192
207
|
|
|
193
|
-
|
|
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
|
-
|
|
230
|
+
En voeg toe in layout:
|
|
213
231
|
|
|
214
|
-
|
|
232
|
+
```tsx
|
|
233
|
+
<TranslationProvider>
|
|
234
|
+
<CMSPreview />
|
|
235
|
+
{children}
|
|
236
|
+
</TranslationProvider>
|
|
237
|
+
```
|
|
215
238
|
|
|
216
|
-
|
|
239
|
+
Voeg `data-cms-key` toe op elementen voor nauwkeurige highlighting:
|
|
217
240
|
|
|
218
241
|
```tsx
|
|
219
|
-
<h1 data-cms-key="
|
|
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
|
-
|
|
245
|
+
---
|
|
224
246
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
271
|
+
## Advanced Flags
|
|
232
272
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
# Key Scanner — Hoe Werkt Het
|
|
304
|
+
|
|
305
|
+
De tool herkent deze patronen in je code:
|
|
244
306
|
|
|
245
307
|
```ts
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
308
|
+
// ✅ React-i18next hook
|
|
309
|
+
const { t } = useTranslation('blog');
|
|
310
|
+
t('blog:post.title');
|
|
249
311
|
|
|
250
|
-
|
|
312
|
+
// ✅ Trans component
|
|
313
|
+
<Trans i18nKey="blog:post.title" />
|
|
251
314
|
|
|
252
|
-
|
|
253
|
-
|
|
315
|
+
// ✅ String literal
|
|
316
|
+
const key = 'blog:post.title';
|
|
317
|
+
```
|
|
254
318
|
|
|
255
|
-
|
|
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
|
-
|
|
264
|
-
const [open, setOpen] = useState(false);
|
|
321
|
+
Alle keys moeten dit format volgen. Anders krijg je een warning:
|
|
265
322
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
registerPreviewAction('nav.menu', () => {
|
|
270
|
-
setOpen(true);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}, []);
|
|
323
|
+
```ts
|
|
324
|
+
// ✅ Goed
|
|
325
|
+
t('common:button.save');
|
|
274
326
|
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
331
|
+
### Dynamische Routes — Auto-Generated routeParams
|
|
294
332
|
|
|
295
|
-
|
|
333
|
+
**Hoe het werkt:**
|
|
296
334
|
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
309
|
-
names registered during initialization. Common patterns:
|
|
350
|
+
**Handmatig overschrijven (optioneel):**
|
|
310
351
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
363
|
+
Dit overschrijft de auto-generated waarden. Alles wat je hier toevoegt krijgt
|
|
364
|
+
prioriteit.
|
|
323
365
|
|
|
324
|
-
|
|
366
|
+
### Cache bestanden
|
|
325
367
|
|
|
326
|
-
|
|
327
|
-
registerPreviewAction('nav.mobile-menu', () => setMobileMenuOpen(true));
|
|
328
|
-
```
|
|
368
|
+
Bij elke sync worden twee cache bestanden aangemaakt:
|
|
329
369
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
389
|
+
# Advanced Features
|
|
349
390
|
|
|
350
|
-
|
|
391
|
+
## Preview Mode — Live Highlighting
|
|
351
392
|
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
358
|
-
<Trans i18nKey="blog:post.title" />
|
|
396
|
+
### Setup
|
|
359
397
|
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
365
|
-
warning.
|
|
422
|
+
---
|
|
366
423
|
|
|
367
|
-
|
|
424
|
+
# JSON Output Format
|
|
368
425
|
|
|
369
|
-
|
|
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
|
-
"
|
|
376
|
-
"
|
|
377
|
-
"
|
|
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
|
-
|
|
383
|
-
no need to repeat params per key.
|
|
442
|
+
**Structuur:**
|
|
384
443
|
|
|
385
|
-
|
|
444
|
+
- Top-level keys = namespaces
|
|
445
|
+
- Nested keys = translation strings
|
|
446
|
+
- 1 bestand per taal (`en.json`, `nl.json`, etc.)
|
|
386
447
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
505
|
+
# License
|
|
398
506
|
|
|
399
507
|
MIT
|