@arc-js/intl 0.0.2

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 ADDED
@@ -0,0 +1,575 @@
1
+ # @arc-js/intl
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-007ACC)
5
+ ![React](https://img.shields.io/badge/React-19+-61DAFB)
6
+ ![Vite](https://img.shields.io/badge/Vite-6+-646CFF)
7
+
8
+ **@arc-js/intl** est un système de gestion d'internationalisation (i18n) modulaire et performant pour les applications React avec TypeScript/JavaScript. Il fournit une gestion avancée des traductions, un chargement dynamique des modules, et une intégration transparente avec l'écosystème Arc.
9
+
10
+ ## ✨ Fonctionnalités Principales
11
+
12
+ ### 🌍 Gestion Multi-Langue Avancée
13
+ - **Support de plusieurs locales** avec détection automatique
14
+ - **Chargement dynamique** des fichiers de traduction
15
+ - **Gestion des pluriels** basée sur les règles Intl
16
+ - **Interpolation de variables** dans les traductions
17
+
18
+ ### 📦 Architecture Modulaire
19
+ - **Traductions par module** avec isolation complète
20
+ - **Chargement à la demande** des traductions des modules
21
+ - **Fusion intelligente** des traductions hiérarchiques
22
+ - **Support des namespaces** pour une organisation claire
23
+
24
+ ### ⚡ Performance Optimisée
25
+ - **Chargement paresseux** des fichiers de traduction
26
+ - **Mémoire cache** des traductions chargées
27
+ - **Minimal bundle size** grâce au code splitting
28
+ - **Hot reload** pendant le développement
29
+
30
+ ### 🔧 Intégration Facile
31
+ - **Provider React** simple à configurer
32
+ - **Hooks personnalisés** pour une utilisation intuitive
33
+ - **Compatibilité totale** avec TypeScript
34
+ - **Intégration avec @arc-js/cooks** pour la persistance
35
+
36
+ ## 📦 Installation
37
+
38
+ ### Via npm/yarn/pnpm
39
+
40
+ ```bash
41
+ npm install @arc-js/intl @arc-js/cooks react
42
+ # ou
43
+ yarn add @arc-js/intl @arc-js/cooks react
44
+ # ou
45
+ pnpm add @arc-js/intl @arc-js/cooks react
46
+ ```
47
+
48
+ ### Dépendances requises
49
+ - React 19+
50
+ - @arc-js/cooks 0.0.3+
51
+ - TypeScript 5.0+ (recommandé)
52
+ - Vite (pour le chargement dynamique)
53
+
54
+ ## 🚀 Démarrage Rapide
55
+
56
+ ### Structure de projet recommandée
57
+
58
+ ```
59
+ src/
60
+ ├── locales/
61
+ │ ├── en.json # Traductions anglaises de base
62
+ │ └── fr.json # Traductions françaises de base
63
+ ├── modules/
64
+ │ ├── admin/
65
+ │ │ └── locales/
66
+ │ │ ├── en.json # Traductions admin anglaises
67
+ │ │ └── fr.json # Traductions admin françaises
68
+ │ └── dashboard/
69
+ │ └── locales/
70
+ │ ├── en.json # Traductions dashboard anglaises
71
+ │ └── fr.json # Traductions dashboard françaises
72
+ └── main.tsx
73
+ ```
74
+
75
+ ### Configuration de base
76
+
77
+ ```typescript
78
+ // main.tsx
79
+ import React from 'react';
80
+ import ReactDOM from 'react-dom/client';
81
+ import { ArcIntlProvider } from '@arc-js/intl';
82
+
83
+ const App = () => {
84
+ return (
85
+ <div>
86
+ <h1>Mon Application Internationalisée</h1>
87
+ {/* Votre contenu ici */}
88
+ </div>
89
+ );
90
+ };
91
+
92
+ ReactDOM.createRoot(document.getElementById('root')!).render(
93
+ <React.StrictMode>
94
+ <ArcIntlProvider supportedLocales={['en', 'fr', 'es']}>
95
+ <App />
96
+ </ArcIntlProvider>
97
+ </React.StrictMode>
98
+ );
99
+ ```
100
+
101
+ ### Fichiers de traduction
102
+
103
+ ```json
104
+ // locales/en.json
105
+ {
106
+ "common": {
107
+ "welcome": "Welcome to our application",
108
+ "login": "Sign in",
109
+ "logout": "Sign out",
110
+ "save": "Save changes"
111
+ },
112
+ "errors": {
113
+ "required": "This field is required",
114
+ "email": "Please enter a valid email address"
115
+ }
116
+ }
117
+
118
+ // modules/admin/locales/en.json
119
+ {
120
+ "admin": {
121
+ "dashboard": {
122
+ "title": "Admin Dashboard",
123
+ "users": "Manage Users",
124
+ "settings": "System Settings"
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## 📚 Documentation API
131
+
132
+ ### Hook useTranslation
133
+
134
+ ```typescript
135
+ import { useTranslation } from '@arc-js/intl';
136
+
137
+ const MyComponent = () => {
138
+ const {
139
+ t, // Fonction de traduction principale
140
+ changeLocale, // Changer la locale
141
+ currentLocale, // Locale actuelle
142
+ isLoading // État de chargement
143
+ } = useTranslation('admin'); // Optionnel: nom du module
144
+
145
+ // Exemple d'utilisation
146
+ const handleChangeLanguage = () => {
147
+ changeLocale(currentLocale === 'en' ? 'fr' : 'en');
148
+ };
149
+
150
+ return (
151
+ <div>
152
+ <h1>{t('admin.dashboard.title')}</h1>
153
+ <p>{t('common.welcome')}</p>
154
+ <button onClick={handleChangeLanguage}>
155
+ {t('common.change_language')}
156
+ </button>
157
+ </div>
158
+ );
159
+ };
160
+ ```
161
+
162
+ ### ArcIntlProvider
163
+
164
+ ```typescript
165
+ import { ArcIntlProvider } from '@arc-js/intl';
166
+
167
+ // Configuration avec locales personnalisées
168
+ <ArcIntlProvider
169
+ supportedLocales={['en', 'fr', 'es', 'de']}
170
+ >
171
+ {children}
172
+ </ArcIntlProvider>
173
+
174
+ // Configuration par défaut (en, fr)
175
+ <ArcIntlProvider>
176
+ {children}
177
+ </ArcIntlProvider>
178
+ ```
179
+
180
+ ## 🔧 Utilisation Avancée
181
+
182
+ ### Traductions avec paramètres
183
+
184
+ ```typescript
185
+ // locales/en.json
186
+ {
187
+ "greeting": "Hello, {name}!",
188
+ "items_count": "You have {count} item{count, plural, one {} other {s}}"
189
+ }
190
+
191
+ // Utilisation dans le composant
192
+ const Greeting = ({ userName, itemCount }) => {
193
+ const { t } = useTranslation();
194
+
195
+ return (
196
+ <div>
197
+ <p>{t('greeting', { name: userName })}</p>
198
+ <p>{t('items_count', { count: itemCount })}</p>
199
+ </div>
200
+ );
201
+ };
202
+ ```
203
+
204
+ ### Pluriels et contextes
205
+
206
+ ```typescript
207
+ // locales/en.json
208
+ {
209
+ "messages": {
210
+ "unread": {
211
+ "one": "You have one unread message",
212
+ "other": "You have {count} unread messages"
213
+ }
214
+ },
215
+ "action": {
216
+ "save": "Save",
217
+ "save_context": {
218
+ "draft": "Save as draft",
219
+ "final": "Save and publish"
220
+ }
221
+ }
222
+ }
223
+
224
+ // Utilisation
225
+ const Notification = ({ unreadCount }) => {
226
+ const { t } = useTranslation();
227
+
228
+ return (
229
+ <div>
230
+ {/* Pluriel automatique */}
231
+ <p>{t('messages.unread', { count: unreadCount })}</p>
232
+
233
+ {/* Contexte spécifique */}
234
+ <button>{t('action.save', {}, { context: 'draft' })}</button>
235
+ <button>{t('action.save', {}, { context: 'final' })}</button>
236
+ </div>
237
+ );
238
+ };
239
+ ```
240
+
241
+ ### Chargement dynamique de modules
242
+
243
+ ```typescript
244
+ import { useEffect } from 'react';
245
+ import { useArcIntl } from '@arc-js/intl';
246
+
247
+ const AdminModule = () => {
248
+ const { loadModuleTranslations, t } = useArcIntl();
249
+
250
+ useEffect(() => {
251
+ // Charger les traductions du module admin à la demande
252
+ loadModuleTranslations('admin');
253
+ }, []);
254
+
255
+ return (
256
+ <div>
257
+ <h1>{t('admin.dashboard.title', {}, { moduleName: 'admin' })}</h1>
258
+ </div>
259
+ );
260
+ };
261
+ ```
262
+
263
+ ## 🎯 Exemples Complets
264
+
265
+ ### Exemple 1 : Sélecteur de langue
266
+
267
+ ```typescript
268
+ import { useTranslation } from '@arc-js/intl';
269
+
270
+ const LanguageSwitcher = () => {
271
+ const { currentLocale, changeLocale, t } = useTranslation();
272
+
273
+ const languages = [
274
+ { code: 'en', name: 'English' },
275
+ { code: 'fr', name: 'Français' },
276
+ { code: 'es', name: 'Español' },
277
+ { code: 'de', name: 'Deutsch' }
278
+ ];
279
+
280
+ return (
281
+ <div className="language-switcher">
282
+ <span>{t('common.language')}:</span>
283
+ <select
284
+ value={currentLocale}
285
+ onChange={(e) => changeLocale(e.target.value)}
286
+ >
287
+ {languages.map(lang => (
288
+ <option key={lang.code} value={lang.code}>
289
+ {lang.name}
290
+ </option>
291
+ ))}
292
+ </select>
293
+ </div>
294
+ );
295
+ };
296
+ ```
297
+
298
+ ### Exemple 2 : Formulaire internationalisé
299
+
300
+ ```typescript
301
+ import { useTranslation } from '@arc-js/intl';
302
+ import { useState } from 'react';
303
+
304
+ const ContactForm = () => {
305
+ const { t } = useTranslation();
306
+ const [formData, setFormData] = useState({
307
+ name: '',
308
+ email: '',
309
+ message: ''
310
+ });
311
+
312
+ const handleSubmit = (e) => {
313
+ e.preventDefault();
314
+ // Soumettre le formulaire
315
+ };
316
+
317
+ return (
318
+ <form onSubmit={handleSubmit} className="contact-form">
319
+ <div className="form-group">
320
+ <label>{t('form.name')}</label>
321
+ <input
322
+ value={formData.name}
323
+ onChange={e => setFormData({...formData, name: e.target.value})}
324
+ placeholder={t('form.name_placeholder')}
325
+ />
326
+ {!formData.name && (
327
+ <span className="error">{t('errors.required')}</span>
328
+ )}
329
+ </div>
330
+
331
+ <div className="form-group">
332
+ <label>{t('form.email')}</label>
333
+ <input
334
+ type="email"
335
+ value={formData.email}
336
+ onChange={e => setFormData({...formData, email: e.target.value})}
337
+ placeholder={t('form.email_placeholder')}
338
+ />
339
+ </div>
340
+
341
+ <div className="form-group">
342
+ <label>{t('form.message')}</label>
343
+ <textarea
344
+ value={formData.message}
345
+ onChange={e => setFormData({...formData, message: e.target.value})}
346
+ placeholder={t('form.message_placeholder')}
347
+ rows={4}
348
+ />
349
+ </div>
350
+
351
+ <button type="submit">
352
+ {t('form.submit')}
353
+ </button>
354
+ </form>
355
+ );
356
+ };
357
+ ```
358
+
359
+ ### Exemple 3 : Dashboard avec modules multiples
360
+
361
+ ```typescript
362
+ import { useArcIntl } from '@arc-js/intl';
363
+ import { useEffect } from 'react';
364
+
365
+ const Dashboard = () => {
366
+ const { t, loadModuleTranslations, currentLocale } = useArcIntl();
367
+
368
+ // Charger les traductions de plusieurs modules
369
+ useEffect(() => {
370
+ const loadModules = async () => {
371
+ await loadModuleTranslations('admin');
372
+ await loadModuleTranslations('analytics');
373
+ await loadModuleTranslations('reports');
374
+ };
375
+ loadModules();
376
+ }, [currentLocale]);
377
+
378
+ return (
379
+ <div className="dashboard">
380
+ <header>
381
+ <h1>{t('dashboard.title', {}, { moduleName: 'admin' })}</h1>
382
+ <p>{t('dashboard.welcome_message', { user: 'John Doe' })}</p>
383
+ </header>
384
+
385
+ <section className="stats">
386
+ <div className="stat-card">
387
+ <h3>{t('analytics.visitors', {}, { moduleName: 'analytics' })}</h3>
388
+ <p>1,234</p>
389
+ </div>
390
+
391
+ <div className="stat-card">
392
+ <h3>{t('reports.monthly', {}, { moduleName: 'reports' })}</h3>
393
+ <p>{t('reports.growth', { percent: '15%' })}</p>
394
+ </div>
395
+ </section>
396
+
397
+ <footer>
398
+ <p>{t('common.last_updated', { date: new Date().toLocaleDateString(currentLocale) })}</p>
399
+ </footer>
400
+ </div>
401
+ );
402
+ };
403
+ ```
404
+
405
+ ## 🔧 Configuration Avancée
406
+
407
+ ### Configuration Vite
408
+
409
+ ```typescript
410
+ // vite.config.ts
411
+ import { defineConfig } from 'vite';
412
+ import react from '@vitejs/plugin-react';
413
+
414
+ export default defineConfig({
415
+ plugins: [react()],
416
+ resolve: {
417
+ alias: {
418
+ '@arc-js/intl': '@arc-js/intl/index.js'
419
+ }
420
+ }
421
+ });
422
+ ```
423
+
424
+ ### Configuration TypeScript
425
+
426
+ ```json
427
+ {
428
+ "compilerOptions": {
429
+ "target": "ES2020",
430
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
431
+ "module": "ESNext",
432
+ "moduleResolution": "bundler",
433
+ "allowImportingTsExtensions": true,
434
+ "resolveJsonModule": true,
435
+ "strict": true,
436
+ "types": ["vite/client"]
437
+ },
438
+ "include": ["src", "node_modules/@arc-js/intl/**/*"]
439
+ }
440
+ ```
441
+
442
+ ### Variables d'environnement
443
+
444
+ ```bash
445
+ # .env
446
+ VITE_DEFAULT_LOCALE=en
447
+ VITE_SUPPORTED_LOCALES=en,fr,es
448
+ VITE_TRANSLATION_DEBUG=true
449
+ ```
450
+
451
+ ## 🛡️ Gestion des Erreurs
452
+
453
+ ### Fallback et logs
454
+
455
+ ```typescript
456
+ // En mode développement, les clés manquantes sont loggées
457
+ const MissingKeyHandler = () => {
458
+ const { t } = useTranslation();
459
+
460
+ // Si la clé n'existe pas, elle est retournée telle quelle
461
+ const title = t('nonexistent.key'); // Retourne: "nonexistent.key"
462
+
463
+ // Avec une valeur par défaut
464
+ const subtitle = t('another.missing.key', {}, {
465
+ defaultValue: 'Default text'
466
+ }); // Retourne: "Default text"
467
+
468
+ return (
469
+ <div>
470
+ <h1>{title}</h1>
471
+ <p>{subtitle}</p>
472
+ </div>
473
+ );
474
+ };
475
+ ```
476
+
477
+ ### Validation des traductions
478
+
479
+ ```typescript
480
+ // Script de validation des traductions
481
+ import fs from 'fs';
482
+ import path from 'path';
483
+
484
+ const validateTranslations = (locale: string) => {
485
+ const basePath = path.join(__dirname, 'locales', `\${locale}.json`);
486
+ const baseTranslations = JSON.parse(fs.readFileSync(basePath, 'utf-8'));
487
+
488
+ // Vérifier les clés de base
489
+ const requiredKeys = ['common', 'errors', 'form'];
490
+ requiredKeys.forEach(key => {
491
+ if (!baseTranslations[key]) {
492
+ console.warn(`Missing required namespace "\${key}" in \${locale}`);
493
+ }
494
+ });
495
+
496
+ // Vérifier la structure des pluriels
497
+ Object.entries(baseTranslations).forEach(([namespace, translations]) => {
498
+ // Logique de validation spécifique
499
+ });
500
+ };
501
+ ```
502
+
503
+ ## 📋 Table des Conventions
504
+
505
+ ### Structure des fichiers JSON
506
+
507
+ | Chemin | Description | Exemple |
508
+ |--------|-------------|---------|
509
+ | `locales/{locale}.json` | Traductions de base | `locales/en.json` |
510
+ | `modules/{module}/locales/{locale}.json` | Traductions du module | `modules/admin/locales/fr.json` |
511
+ | `public/locales/{locale}.json` | Traductions statiques (optionnel) | `public/locales/es.json` |
512
+
513
+ ### Clés de traduction
514
+
515
+ | Format | Description | Exemple |
516
+ |--------|-------------|---------|
517
+ | `namespace.key` | Clé simple | `common.save` |
518
+ | `namespace.nested.key` | Clé imbriquée | `form.contact.email` |
519
+ | `key_{context}` | Avec contexte | `save_draft`, `save_final` |
520
+ | `key_{pluralForm}` | Forme plurielle | `item_one`, `item_other` |
521
+
522
+ ### Paramètres supportés
523
+
524
+ | Paramètre | Type | Description |
525
+ |-----------|------|-------------|
526
+ | `count` | number | Pour la gestion des pluriels |
527
+ | `context` | string | Contexte spécifique (draft, final, etc.) |
528
+ | `moduleName` | string | Module cible pour la traduction |
529
+ | `defaultValue` | string | Valeur par défaut si la clé n'existe pas |
530
+
531
+ ## 🔧 Build et Développement
532
+
533
+ ### Scripts recommandés
534
+
535
+ ```json
536
+ {
537
+ "scripts": {
538
+ "dev": "vite",
539
+ "build": "tsc && vite build",
540
+ "preview": "vite preview",
541
+ "type-check": "tsc --noEmit",
542
+ "extract-translations": "node scripts/extract-keys.js",
543
+ "validate-translations": "node scripts/validate-translations.js"
544
+ }
545
+ }
546
+ ```
547
+
548
+ ### Extraction des clés de traduction
549
+
550
+ ```javascript
551
+ // scripts/extract-keys.js
552
+ import { extractKeysFromFiles } from './translation-utils';
553
+
554
+ // Extraire toutes les clés utilisées dans les composants
555
+ const keys = extractKeysFromFiles('src/**/*.{tsx,ts}');
556
+ fs.writeFileSync('translation-keys.json', JSON.stringify(keys, null, 2));
557
+ ```
558
+
559
+ ## 📄 Licence
560
+
561
+ MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
562
+
563
+ ## 🐛 Signaler un Bug
564
+
565
+ Envoyez-nous un mail à l'adresse `contact.inicode@gmail.com` pour :
566
+ - Signaler un bug
567
+ - Proposer une amélioration
568
+ - Poser une question sur l'utilisation
569
+ - Demander une nouvelle fonctionnalité
570
+
571
+ ---
572
+
573
+ **@arc-js/intl** - La solution d'internationalisation modulaire pour React et TypeScript.
574
+
575
+ *Développé par l'équipe INICODE*
package/config.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ declare const DEFAULT_LOCALE = "en";
2
+ declare const SUPPORTED_LOCALES: string[];
3
+ declare const COOKIE_NAME = "app_locale";
4
+ type Locale = typeof SUPPORTED_LOCALES[number];
5
+ declare const getSavedLocale: (supportedLocales?: string[]) => string;
6
+ declare const saveLocale: (locale: string) => void;
7
+ declare const PATH_CONFIG: {
8
+ SRC_DIR: string;
9
+ };
10
+ declare const SRC_DIR: string;
11
+
12
+ export { COOKIE_NAME, DEFAULT_LOCALE, PATH_CONFIG, SRC_DIR, SUPPORTED_LOCALES, getSavedLocale, saveLocale };
13
+ export type { Locale };