@arc-js/intl
v0.0.97
Published
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 transparen
Readme
@arc-js/intl
@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.
✨ Fonctionnalités Principales
🌍 Gestion Multi-Langue Avancée
- Support de plusieurs locales avec détection automatique
- Chargement dynamique des fichiers de traduction
- Gestion des pluriels basée sur les règles Intl
- Interpolation de variables dans les traductions
📦 Architecture Modulaire
- Traductions par module avec isolation complète
- Chargement à la demande des traductions des modules
- Fusion intelligente des traductions hiérarchiques
- Support des namespaces pour une organisation claire
⚡ Performance Optimisée
- Chargement paresseux des fichiers de traduction
- Mémoire cache des traductions chargées
- Minimal bundle size grâce au code splitting
- Hot reload pendant le développement
🔧 Intégration Facile
- Provider React simple à configurer
- Hooks personnalisés pour une utilisation intuitive
- Compatibilité totale avec TypeScript
- Intégration avec @arc-js/cooks pour la persistance
📦 Installation
Via npm/yarn/pnpm
npm install @arc-js/intl @arc-js/cooks react
# ou
yarn add @arc-js/intl @arc-js/cooks react
# ou
pnpm add @arc-js/intl @arc-js/cooks reactDépendances requises
- React 19+
- @arc-js/cooks 1.0.0+
- TypeScript 5.0+ (recommandé)
- Vite (pour le chargement dynamique)
🚀 Démarrage Rapide
Structure de projet recommandée
src/
├── locales/
│ ├── en.json # Traductions anglaises de base
│ └── fr.json # Traductions françaises de base
├── modules/
│ ├── admin/
│ │ └── locales/
│ │ ├── en.json # Traductions admin anglaises
│ │ └── fr.json # Traductions admin françaises
│ └── dashboard/
│ └── locales/
│ ├── en.json # Traductions dashboard anglaises
│ └── fr.json # Traductions dashboard françaises
└── main.tsxConfiguration de base
// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ArcIntlProvider } from '@arc-js/intl';
const App = () => {
return (
<div>
<h1>Mon Application Internationalisée</h1>
{/* Votre contenu ici */}
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ArcIntlProvider
translations={{
base: {
en: async () => (await import('./locales/en.json')).default,
fr: async () => (await import('./locales/fr.json')).default
}
}}
supportedLocales={['en', 'fr', 'es']}
>
<App />
</ArcIntlProvider>
</React.StrictMode>
);Fichiers de traduction
// locales/en.json
{
"common": {
"welcome": "Welcome to our application",
"login": "Sign in",
"logout": "Sign out",
"save": "Save changes"
},
"errors": {
"required": "This field is required",
"email": "Please enter a valid email address"
}
}
// modules/admin/locales/en.json
{
"admin": {
"dashboard": {
"title": "Admin Dashboard",
"users": "Manage Users",
"settings": "System Settings"
}
}
}📚 Documentation API
Hook useTranslation
import { useTranslation } from '@arc-js/intl';
const MyComponent = () => {
const {
t, // Fonction de traduction principale
changeLocale, // Changer la locale
currentLocale, // Locale actuelle
isLoading // État de chargement
} = useTranslation('admin'); // Optionnel: nom du module
// Exemple d'utilisation
const handleChangeLanguage = () => {
changeLocale(currentLocale === 'en' ? 'fr' : 'en');
};
return (
<div>
<h1>{t('admin.dashboard.title')}</h1>
<p>{t('common.welcome')}</p>
<button onClick={handleChangeLanguage}>
Switch to {currentLocale === 'en' ? 'French' : 'English'}
</button>
</div>
);
};ArcIntlProvider
import { ArcIntlProvider } from '@arc-js/intl';
// Configuration complète avec modules
<ArcIntlProvider
translations={{
base: {
en: async () => (await import('./locales/en.json')).default,
fr: async () => (await import('./locales/fr.json')).default
},
modules: {
admin: {
en: async () => (await import('./modules/admin/locales/en.json')).default,
fr: async () => (await import('./modules/admin/locales/fr.json')).default
},
dashboard: {
en: async () => (await import('./modules/dashboard/locales/en.json')).default,
fr: async () => (await import('./modules/dashboard/locales/fr.json')).default
}
}
}}
supportedLocales={['en', 'fr', 'es']}
>
{children}
</ArcIntlProvider>🔧 Utilisation Avancée
Traductions avec paramètres
// locales/en.json
{
"greeting": "Hello, {name}!",
"items_count": "You have {count} item{count, plural, one {} other {s}}"
}
// Utilisation dans le composant
const Greeting = ({ userName, itemCount }) => {
const { t } = useTranslation();
return (
<div>
<p>{t('greeting', { name: userName })}</p>
<p>{t('items_count', { count: itemCount })}</p>
</div>
);
};Pluriels et contextes
// locales/en.json
{
"messages": {
"unread": {
"one": "You have one unread message",
"other": "You have {count} unread messages"
}
}
}
// Utilisation
const Notification = ({ unreadCount }) => {
const { t } = useTranslation();
return (
<div>
{/* Pluriel automatique */}
<p>{t('messages.unread', { count: unreadCount })}</p>
</div>
);
};Chargement dynamique de modules
import { useEffect } from 'react';
import { useTranslation } from '@arc-js/intl';
const AdminModule = () => {
const { loadModule, t, isModuleLoaded } = useTranslation('admin');
useEffect(() => {
// Charger les traductions du module admin à la demande
if (!isModuleLoaded) {
loadModule('admin');
}
}, []);
if (!isModuleLoaded) return <div>Loading translations...</div>;
return (
<div>
<h1>{t('admin.dashboard.title')}</h1>
</div>
);
};🎯 Exemples Complets
Exemple 1 : Sélecteur de langue
import { useTranslation } from '@arc-js/intl';
const LanguageSwitcher = () => {
const { currentLocale, changeLocale, t } = useTranslation();
const languages = [
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'Français' },
{ code: 'es', name: 'Español' },
{ code: 'de', name: 'Deutsch' }
];
return (
<div className="language-switcher">
<span>{t('common.language')}:</span>
<select
value={currentLocale}
onChange={(e) => changeLocale(e.target.value)}
>
{languages.map(lang => (
<option key={lang.code} value={lang.code}>
{lang.name}
</option>
))}
</select>
</div>
);
};Exemple 2 : Formulaire internationalisé
import { useTranslation } from '@arc-js/intl';
import { useState } from 'react';
const ContactForm = () => {
const { t } = useTranslation();
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const handleSubmit = (e) => {
e.preventDefault();
// Soumettre le formulaire
};
return (
<form onSubmit={handleSubmit} className="contact-form">
<div className="form-group">
<label>{t('form.name')}</label>
<input
value={formData.name}
onChange={e => setFormData({...formData, name: e.target.value})}
placeholder={t('form.name_placeholder')}
/>
{!formData.name && (
<span className="error">{t('errors.required')}</span>
)}
</div>
<div className="form-group">
<label>{t('form.email')}</label>
<input
type="email"
value={formData.email}
onChange={e => setFormData({...formData, email: e.target.value})}
placeholder={t('form.email_placeholder')}
/>
</div>
<div className="form-group">
<label>{t('form.message')}</label>
<textarea
value={formData.message}
onChange={e => setFormData({...formData, message: e.target.value})}
placeholder={t('form.message_placeholder')}
rows={4}
/>
</div>
<button type="submit">
{t('form.submit')}
</button>
</form>
);
};Exemple 3 : Dashboard avec modules multiples
import { useTranslation } from '@arc-js/intl';
import { useEffect } from 'react';
const Dashboard = () => {
const { t, loadModule, currentLocale, isLoading } = useTranslation('admin');
// Charger les traductions de plusieurs modules
useEffect(() => {
const loadModules = async () => {
await loadModule('admin');
await loadModule('analytics');
await loadModule('reports');
};
loadModules();
}, [currentLocale]);
if (isLoading) return <div>Loading translations...</div>;
return (
<div className="dashboard">
<header>
<h1>{t('admin.dashboard.title')}</h1>
<p>{t('common.welcome', { user: 'John Doe' })}</p>
</header>
<section className="stats">
<div className="stat-card">
<h3>{t('analytics.visitors', {}, { moduleName: 'analytics' })}</h3>
<p>1,234</p>
</div>
<div className="stat-card">
<h3>{t('reports.monthly', {}, { moduleName: 'reports' })}</h3>
<p>{t('reports.growth', { percent: '15%' })}</p>
</div>
</section>
<footer>
<p>{t('common.last_updated', { date: new Date().toLocaleDateString(currentLocale) })}</p>
</footer>
</div>
);
};📋 API Reference
ArcIntlProvider
| Prop | Type | Description | Required |
|------|------|-------------|----------|
| translations | TranslationsConfig | Configuration des chargeurs de traductions | Oui |
| supportedLocales | string[] | Locales supportées (défaut: ['en', 'fr']) | Non |
| children | React.ReactNode | Composants enfants | Oui |
Hook useTranslation
Retourne un objet avec:
t(key: string, params?: Record<string, any>, options?: TranslationOptions): Fonction de traductionchangeLocale(locale: string): Changer la locale actuellecurrentLocale: Locale actuelleisLoading: État de chargement globalisModuleLoaded: Indique si le module demandé est chargéloadModule(moduleName: string): Charge un module spécifique
Options de Traduction
interface TranslationOptions {
moduleName?: string; // Nom du module (défaut: 'core')
defaultValue?: string; // Valeur par défaut si clé non trouvée
count?: number; // Pour la gestion des pluriels
context?: string; // Contexte spécifique
}Structure de configuration
interface TranslationsConfig {
base: {
[locale: string]: () => Promise<Record<string, any>>;
};
modules?: {
[moduleName: string]: {
[locale: string]: () => Promise<Record<string, any>>;
};
};
}🛡️ Gestion des Erreurs
Fallback sécurisé
const SafeComponent = () => {
const { t } = useTranslation();
// Utilisation sécurisée avec valeur par défaut
const title = t('nonexistent.key', {}, {
defaultValue: 'Default Title'
});
// Si pas de valeur par défaut, retourne la clé
const subtitle = t('another.missing.key'); // Retourne: "another.missing.key"
return (
<div>
<h1>{title}</h1>
<p>{subtitle}</p>
</div>
);
};Logs en développement
// En mode développement, les clés manquantes sont automatiquement loggées
const MissingTranslations = () => {
const { t } = useTranslation();
// Ceci loggue un avertissement en développement
const missingKey = t('non.existent.key');
return <div>{missingKey}</div>;
};🔧 Configuration TypeScript
{
"compilerOptions": {
"target": "ES2020",
"lib": ["DOM", "DOM.Iterable", "ES2020"],
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"types": ["vite/client"]
},
"include": ["src", "node_modules/@arc-js/intl/**/*"]
}📋 Table des Conventions
Structure des fichiers JSON
| Chemin | Description | Exemple |
|--------|-------------|---------|
| locales/{locale}.json | Traductions de base | locales/en.json |
| modules/{module}/locales/{locale}.json | Traductions du module | modules/admin/locales/fr.json |
| public/locales/{locale}.json | Traductions statiques (optionnel) | public/locales/es.json |
Clés de traduction
| Format | Description | Exemple |
|--------|-------------|---------|
| namespace.key | Clé simple | common.save |
| namespace.nested.key | Clé imbriquée | form.contact.email |
| key_{context} | Avec contexte | save_draft, save_final |
Paramètres supportés
| Paramètre | Type | Description |
|-----------|------|-------------|
| count | number | Pour la gestion des pluriels |
| context | string | Contexte spécifique (draft, final, etc.) |
| moduleName | string | Module cible pour la traduction |
| defaultValue | string | Valeur par défaut si la clé n'existe pas |
🔧 Build et Développement
Scripts recommandés
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"type-check": "tsc --noEmit",
"extract-translations": "node scripts/extract-keys.js",
"validate-translations": "node scripts/validate-translations.js"
}
}Configuration Vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@arc-js/intl': '@arc-js/intl/index.js'
}
}
});📄 Licence
MIT License - Voir le fichier LICENSE pour plus de détails.
🐛 Signaler un Bug
Envoyez-nous un mail à l'adresse [email protected] pour :
- Signaler un bug
- Proposer une amélioration
- Poser une question sur l'utilisation
- Demander une nouvelle fonctionnalité
@arc-js/intl - La solution d'internationalisation modulaire pour React et TypeScript.
Développé par l'équipe INICODE
