@contentstorage/i18next-plugin 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Contentstorage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,453 @@
1
+ # ContentStorage i18next Plugin
2
+
3
+ Official i18next plugin for [ContentStorage](https://contentstorage.app) live editor translation tracking.
4
+
5
+ ## Features
6
+
7
+ - **Live Editor Integration** - Automatically detects and enables tracking when running in ContentStorage live editor
8
+ - **Translation Tracking** - Maps translation values to their keys for click-to-edit functionality
9
+ - **Zero Production Overhead** - Tracking only activates in live editor mode
10
+ - **TypeScript Support** - Full type definitions included
11
+ - **Memory Management** - Automatic cleanup of old entries to prevent memory leaks
12
+ - **Flexible Loading** - Support for CDN, custom URLs, or custom fetch functions
13
+ - **Post-Processor Support** - Track translations at resolution time for dynamic content
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @contentstorage/i18next-plugin
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Basic Usage (Backend Plugin)
24
+
25
+ ```typescript
26
+ import i18next from 'i18next';
27
+ import ContentStorageBackend from '@contentstorage/i18next-plugin';
28
+
29
+ i18next
30
+ .use(ContentStorageBackend)
31
+ .init({
32
+ backend: {
33
+ contentKey: 'your-content-key-here', // Get this from ContentStorage dashboard
34
+ debug: false,
35
+ },
36
+ lng: 'en',
37
+ fallbackLng: 'en',
38
+ ns: ['common', 'homepage'],
39
+ defaultNS: 'common',
40
+ });
41
+
42
+ // Use translations as normal
43
+ i18next.t('common:welcome'); // "Welcome to our site"
44
+ ```
45
+
46
+ ### With Post-Processor (Recommended)
47
+
48
+ For better tracking of dynamic translations with interpolations:
49
+
50
+ ```typescript
51
+ import i18next from 'i18next';
52
+ import ContentStorageBackend, { ContentStoragePostProcessor } from '@contentstorage/i18next-plugin';
53
+
54
+ i18next
55
+ .use(ContentStorageBackend)
56
+ .use(new ContentStoragePostProcessor({ debug: false }))
57
+ .init({
58
+ backend: {
59
+ contentKey: 'your-content-key',
60
+ },
61
+ lng: 'en',
62
+ fallbackLng: 'en',
63
+ });
64
+
65
+ // Interpolated translations are tracked correctly
66
+ i18next.t('greeting', { name: 'John' }); // "Hello John"
67
+ ```
68
+
69
+ ## Configuration Options
70
+
71
+ ### Backend Options
72
+
73
+ ```typescript
74
+ interface ContentStoragePluginOptions {
75
+ /**
76
+ * Your ContentStorage content key (required for default CDN)
77
+ */
78
+ contentKey?: string;
79
+
80
+ /**
81
+ * Custom CDN base URL
82
+ * @default 'https://cdn.contentstorage.app'
83
+ */
84
+ cdnBaseUrl?: string;
85
+
86
+ /**
87
+ * Enable debug logging
88
+ * @default false
89
+ */
90
+ debug?: boolean;
91
+
92
+ /**
93
+ * Maximum number of entries in memoryMap
94
+ * @default 10000
95
+ */
96
+ maxMemoryMapSize?: number;
97
+
98
+ /**
99
+ * Custom load path (string template or function)
100
+ * @example '{{lng}}/{{ns}}.json'
101
+ * @example (lng, ns) => `https://my-cdn.com/${lng}/${ns}.json`
102
+ */
103
+ loadPath?: string | ((language: string, namespace: string) => string);
104
+
105
+ /**
106
+ * Custom fetch implementation
107
+ */
108
+ request?: (url: string, options: RequestInit) => Promise<any>;
109
+
110
+ /**
111
+ * Query parameter name for live editor detection
112
+ * @default 'contentstorage_live_editor'
113
+ */
114
+ liveEditorParam?: string;
115
+
116
+ /**
117
+ * Force live mode (useful for testing)
118
+ * @default false
119
+ */
120
+ forceLiveMode?: boolean;
121
+
122
+ /**
123
+ * Only track specific namespaces
124
+ */
125
+ trackNamespaces?: string[];
126
+ }
127
+ ```
128
+
129
+ ## Advanced Usage
130
+
131
+ ### Custom CDN URL
132
+
133
+ ```typescript
134
+ i18next.use(ContentStorageBackend).init({
135
+ backend: {
136
+ contentKey: 'your-key',
137
+ cdnBaseUrl: 'https://your-custom-cdn.com',
138
+ },
139
+ });
140
+ ```
141
+
142
+ ### Custom Load Path
143
+
144
+ ```typescript
145
+ i18next.use(ContentStorageBackend).init({
146
+ backend: {
147
+ loadPath: '/locales/{{lng}}/{{ns}}.json',
148
+ },
149
+ });
150
+
151
+ // Or with a function
152
+ i18next.use(ContentStorageBackend).init({
153
+ backend: {
154
+ loadPath: (lng, ns) => {
155
+ return `https://api.example.com/translations/${lng}/${ns}`;
156
+ },
157
+ },
158
+ });
159
+ ```
160
+
161
+ ### Custom Fetch with Authentication
162
+
163
+ ```typescript
164
+ i18next.use(ContentStorageBackend).init({
165
+ backend: {
166
+ contentKey: 'your-key',
167
+ request: async (url, options) => {
168
+ const response = await fetch(url, {
169
+ ...options,
170
+ headers: {
171
+ ...options.headers,
172
+ 'Authorization': 'Bearer YOUR_TOKEN',
173
+ },
174
+ });
175
+
176
+ if (!response.ok) {
177
+ throw new Error(`HTTP ${response.status}`);
178
+ }
179
+
180
+ return response.json();
181
+ },
182
+ },
183
+ });
184
+ ```
185
+
186
+ ### Track Only Specific Namespaces
187
+
188
+ ```typescript
189
+ i18next.use(ContentStorageBackend).init({
190
+ backend: {
191
+ contentKey: 'your-key',
192
+ trackNamespaces: ['common', 'marketing'], // Only track these
193
+ },
194
+ ns: ['common', 'marketing', 'admin'],
195
+ });
196
+ ```
197
+
198
+ ### Enable Debug Mode
199
+
200
+ ```typescript
201
+ i18next
202
+ .use(ContentStorageBackend)
203
+ .use(new ContentStoragePostProcessor({ debug: true }))
204
+ .init({
205
+ backend: {
206
+ contentKey: 'your-key',
207
+ debug: true,
208
+ },
209
+ });
210
+
211
+ // Console output:
212
+ // [ContentStorage] Live editor mode enabled
213
+ // [ContentStorage] Loading translations: en/common
214
+ // [ContentStorage] Tracked 42 translations for common
215
+ ```
216
+
217
+ ## How It Works
218
+
219
+ ### Live Editor Detection
220
+
221
+ The plugin automatically detects when your app is running in the ContentStorage live editor by checking:
222
+
223
+ 1. The app is running in an iframe (`window.self !== window.top`)
224
+ 2. The URL contains the query parameter `?contentstorage_live_editor=true`
225
+
226
+ Both conditions must be true for tracking to activate.
227
+
228
+ ### Translation Tracking
229
+
230
+ When in live editor mode, the plugin maintains a global `window.memoryMap` that maps translation values to their keys:
231
+
232
+ ```typescript
233
+ window.memoryMap = new Map([
234
+ ["Welcome to our site", {
235
+ ids: Set(["homepage.title", "banner.heading"]),
236
+ type: "text",
237
+ metadata: {
238
+ namespace: "common",
239
+ language: "en",
240
+ trackedAt: 1704067200000
241
+ }
242
+ }],
243
+ // ... more entries
244
+ ]);
245
+ ```
246
+
247
+ This allows the ContentStorage live editor to:
248
+ 1. Find which translation keys produced a given text
249
+ 2. Enable click-to-edit functionality
250
+ 3. Highlight translatable content on the page
251
+
252
+ ### Memory Management
253
+
254
+ The plugin automatically limits the size of `window.memoryMap` to prevent memory leaks:
255
+
256
+ - Default limit: 10,000 entries
257
+ - Oldest entries are removed first (based on `trackedAt` timestamp)
258
+ - Configurable via `maxMemoryMapSize` option
259
+
260
+ ## Usage with React
261
+
262
+ ### React 18+
263
+
264
+ ```typescript
265
+ import i18next from 'i18next';
266
+ import { initReactI18next } from 'react-i18next';
267
+ import ContentStorageBackend from '@contentstorage/i18next-plugin';
268
+
269
+ i18next
270
+ .use(ContentStorageBackend)
271
+ .use(initReactI18next)
272
+ .init({
273
+ backend: {
274
+ contentKey: 'your-key',
275
+ },
276
+ lng: 'en',
277
+ fallbackLng: 'en',
278
+ interpolation: {
279
+ escapeValue: false,
280
+ },
281
+ });
282
+
283
+ // In your component
284
+ import { useTranslation } from 'react-i18next';
285
+
286
+ function MyComponent() {
287
+ const { t } = useTranslation();
288
+
289
+ return <h1>{t('welcome')}</h1>;
290
+ }
291
+ ```
292
+
293
+ ## Usage with Next.js
294
+
295
+ ### App Router (Next.js 13+)
296
+
297
+ ```typescript
298
+ // app/i18n.ts
299
+ import i18next from 'i18next';
300
+ import ContentStorageBackend from '@contentstorage/i18next-plugin';
301
+
302
+ i18next.use(ContentStorageBackend).init({
303
+ backend: {
304
+ contentKey: process.env.NEXT_PUBLIC_CONTENTSTORAGE_KEY,
305
+ },
306
+ lng: 'en',
307
+ fallbackLng: 'en',
308
+ });
309
+
310
+ export default i18next;
311
+ ```
312
+
313
+ ```typescript
314
+ // app/[lang]/layout.tsx
315
+ 'use client';
316
+
317
+ import { useEffect } from 'react';
318
+ import i18next from '../i18n';
319
+
320
+ export default function RootLayout({
321
+ children,
322
+ params: { lang },
323
+ }: {
324
+ children: React.ReactNode;
325
+ params: { lang: string };
326
+ }) {
327
+ useEffect(() => {
328
+ i18next.changeLanguage(lang);
329
+ }, [lang]);
330
+
331
+ return (
332
+ <html lang={lang}>
333
+ <body>{children}</body>
334
+ </html>
335
+ );
336
+ }
337
+ ```
338
+
339
+ ## Testing
340
+
341
+ ### Force Live Mode
342
+
343
+ For testing purposes, you can force live mode:
344
+
345
+ ```typescript
346
+ i18next.use(ContentStorageBackend).init({
347
+ backend: {
348
+ contentKey: 'your-key',
349
+ forceLiveMode: true, // Always enable tracking
350
+ },
351
+ });
352
+ ```
353
+
354
+ ### Debug Memory Map
355
+
356
+ ```typescript
357
+ import { debugMemoryMap } from '@contentstorage/i18next-plugin';
358
+
359
+ // In browser console or your code
360
+ debugMemoryMap();
361
+
362
+ // Output:
363
+ // [ContentStorage] Memory map contents:
364
+ // Total entries: 156
365
+ // ┌─────────┬──────────────────────────────┬─────────────────────┐
366
+ // │ (index) │ value │ keys │
367
+ // │ namespace │
368
+ // ├─────────┼──────────────────────────────┼─────────────────────┤
369
+ // │ 0 │ 'Welcome to our site' │ 'homepage.title' │
370
+ // │ 'common' │
371
+ // └─────────┴──────────────────────────────┴─────────────────────┘
372
+ ```
373
+
374
+ ## Browser Support
375
+
376
+ - Modern browsers (Chrome, Firefox, Safari, Edge)
377
+ - ES2019+ features required
378
+ - `fetch` API required (polyfill if needed for older browsers)
379
+
380
+ ## TypeScript
381
+
382
+ Full TypeScript support included with type definitions:
383
+
384
+ ```typescript
385
+ import type {
386
+ ContentStoragePluginOptions,
387
+ MemoryMap,
388
+ MemoryMapEntry,
389
+ ContentStorageWindow,
390
+ } from '@contentstorage/i18next-plugin';
391
+ ```
392
+
393
+ ## Performance
394
+
395
+ - **Zero overhead in production** - Tracking only happens in live editor
396
+ - **Minimal overhead in editor** - Simple Map operations, ~1ms per translation
397
+ - **Automatic cleanup** - Old entries removed to prevent memory leaks
398
+ - **One-time tracking** - Translations tracked once on load, not on every render
399
+
400
+ ## Troubleshooting
401
+
402
+ ### memoryMap is empty
403
+
404
+ **Problem**: `window.memoryMap` exists but has no entries.
405
+
406
+ **Solutions**:
407
+ - Verify you're in an iframe: `window.self !== window.top`
408
+ - Check URL has `?contentstorage_live_editor=true`
409
+ - Enable debug mode to see what's being tracked
410
+ - Ensure translations are loading (check network tab)
411
+
412
+ ### Live editor can't find translations
413
+
414
+ **Problem**: Clicking on translated text doesn't work in live editor.
415
+
416
+ **Solutions**:
417
+ - Verify translation values exactly match rendered text
418
+ - Use post-processor for dynamic translations
419
+ - Check that tracking happens before DOM renders
420
+ - Enable debug mode and check console logs
421
+
422
+ ### TypeScript errors
423
+
424
+ **Problem**: TypeScript can't find type definitions.
425
+
426
+ **Solutions**:
427
+ - Ensure `@types/i18next` is installed
428
+ - Check `tsconfig.json` has `"esModuleInterop": true`
429
+ - Try importing types explicitly: `import type { ... }`
430
+
431
+ ### CORS errors
432
+
433
+ **Problem**: Cannot load translations from CDN.
434
+
435
+ **Solutions**:
436
+ - Verify your contentKey is correct
437
+ - Check CDN URL in network tab
438
+ - Ensure ContentStorage CDN allows your domain
439
+ - Use custom `request` function to debug
440
+
441
+ ## Contributing
442
+
443
+ Contributions are welcome! Please see our [Contributing Guide](CONTRIBUTING.md).
444
+
445
+ ## License
446
+
447
+ MIT License - see [LICENSE](LICENSE) file for details.
448
+
449
+ ## Support
450
+
451
+ - Documentation: https://docs.contentstorage.app
452
+ - Issues: https://github.com/contentstorage/i18next-plugin/issues
453
+ - Email: support@contentstorage.app
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @contentstorage/i18next-plugin
3
+ *
4
+ * i18next backend plugin for ContentStorage live editor translation tracking
5
+ */
6
+ export { ContentStorageBackend, createContentStorageBackend } from './plugin';
7
+ export { debugMemoryMap } from './utils';
8
+ export type { ContentStoragePluginOptions, MemoryMap, MemoryMapEntry, ContentStorageWindow, TranslationData, } from './types';
9
+ export { default } from './plugin';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,YAAY,EACV,2BAA2B,EAC3B,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,eAAe,GAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}