@umituz/react-native-localization 1.10.0 → 1.12.0
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 +247 -2
- package/package.json +1 -1
- package/src/infrastructure/config/i18n.ts +10 -41
package/README.md
CHANGED
|
@@ -12,6 +12,36 @@ English-only localization system for React Native apps with i18n support. Built
|
|
|
12
12
|
- **Zero Configuration**: Works out of the box with sensible defaults
|
|
13
13
|
- **Production Ready**: Battle-tested in production apps
|
|
14
14
|
- **Lightweight**: Minimal dependencies with tree-shakeable exports
|
|
15
|
+
- **Domain-Driven Design**: Follows DDD principles with clear separation of concerns
|
|
16
|
+
|
|
17
|
+
## Architecture Overview
|
|
18
|
+
|
|
19
|
+
This package follows Domain-Driven Design principles:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
📁 src/
|
|
23
|
+
├── domain/ # Business entities and interfaces
|
|
24
|
+
├── infrastructure/ # Storage, config, and external services
|
|
25
|
+
└── presentation/ # Hooks and components for UI
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Package vs Project Translations
|
|
29
|
+
|
|
30
|
+
**Package Translations (This Package):**
|
|
31
|
+
- Core UI translations (buttons, alerts, navigation)
|
|
32
|
+
- Device-specific translations (camera, location, etc.)
|
|
33
|
+
- Generic business logic translations
|
|
34
|
+
|
|
35
|
+
**Project Translations (Your App):**
|
|
36
|
+
- App-specific translations
|
|
37
|
+
- Business domain translations
|
|
38
|
+
- Feature-specific content
|
|
39
|
+
|
|
40
|
+
**Why This Separation?**
|
|
41
|
+
- Package stays lightweight and reusable
|
|
42
|
+
- Projects maintain full control over their translations
|
|
43
|
+
- Easy updates without affecting project-specific content
|
|
44
|
+
- Clear separation of concerns
|
|
15
45
|
|
|
16
46
|
## Installation
|
|
17
47
|
|
|
@@ -33,10 +63,143 @@ npm install zustand i18next react-i18next expo-localization @umituz/react-native
|
|
|
33
63
|
|
|
34
64
|
## Quick Start
|
|
35
65
|
|
|
36
|
-
### 1
|
|
66
|
+
### Step 1: Install Package Translations (Core UI)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install @umituz/react-native-localization
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 2: Create Your Project's Localization Domain
|
|
73
|
+
|
|
74
|
+
Create a localization domain in your project following DDD principles:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
📁 src/domains/localization/
|
|
78
|
+
├── index.ts # Domain exports
|
|
79
|
+
├── infrastructure/
|
|
80
|
+
│ ├── locales/
|
|
81
|
+
│ │ ├── en-US/
|
|
82
|
+
│ │ │ ├── index.ts # Auto-loader (uses filesystem package)
|
|
83
|
+
│ │ │ ├── auth.json # Authentication translations
|
|
84
|
+
│ │ │ ├── home.json # Home screen translations
|
|
85
|
+
│ │ │ ├── settings.json # Settings translations
|
|
86
|
+
│ │ │ └── [feature].json # Feature-specific translations
|
|
87
|
+
│ │ └── [other-languages]/ # Future language support
|
|
88
|
+
│ └── config/
|
|
89
|
+
│ └── i18n.ts # Project i18n configuration
|
|
90
|
+
└── presentation/
|
|
91
|
+
└── hooks/
|
|
92
|
+
└── useLocalization.ts # Project-specific hooks (optional)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Example from Vivoim App:**
|
|
96
|
+
```
|
|
97
|
+
📁 src/domains/localization/
|
|
98
|
+
├── index.ts
|
|
99
|
+
└── infrastructure/
|
|
100
|
+
└── locales/
|
|
101
|
+
└── en-US/
|
|
102
|
+
├── auth.json # Login, signup, password
|
|
103
|
+
├── chat.json # Chat messages, typing
|
|
104
|
+
├── common.json # Shared UI elements
|
|
105
|
+
├── community.json # Social features
|
|
106
|
+
├── creations.json # Content creation
|
|
107
|
+
├── editor.json # Image/video editing
|
|
108
|
+
├── home.json # Dashboard, navigation
|
|
109
|
+
├── index.ts # Auto-loader
|
|
110
|
+
├── navigation.json # App navigation
|
|
111
|
+
├── paywall.json # Subscription features
|
|
112
|
+
├── premium.json # Premium content
|
|
113
|
+
├── profile.json # User profiles
|
|
114
|
+
├── projects.json # Project management
|
|
115
|
+
├── settings.json # App settings
|
|
116
|
+
├── support.json # Help & support
|
|
117
|
+
├── templates.json # Content templates
|
|
118
|
+
├── text2image.json # AI image generation
|
|
119
|
+
└── wallet.json # Payments, credits
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Why This Structure?**
|
|
123
|
+
- **Separation of Concerns**: Package handles core UI, project handles business logic
|
|
124
|
+
- **Scalability**: Easy to add new features without touching core package
|
|
125
|
+
- **Maintainability**: Clear ownership of translations
|
|
126
|
+
- **Reusability**: Same package works across hundreds of apps
|
|
127
|
+
|
|
128
|
+
### Step 3: Set Up Project Translations
|
|
129
|
+
|
|
130
|
+
**src/domains/localization/infrastructure/locales/en-US/index.ts:**
|
|
131
|
+
```typescript
|
|
132
|
+
import { loadJsonModules } from "@umituz/react-native-filesystem";
|
|
133
|
+
|
|
134
|
+
// Metro bundler require.context - auto-discover all .json files
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
136
|
+
const translationContext = (require as any).context("./", false, /\.json$/);
|
|
137
|
+
|
|
138
|
+
// Load all JSON modules using filesystem package utilities
|
|
139
|
+
const translations = loadJsonModules(translationContext);
|
|
140
|
+
|
|
141
|
+
export default translations;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**src/domains/localization/infrastructure/locales/en-US/your-feature.json:**
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"title": "Your Feature Title",
|
|
148
|
+
"description": "Feature description",
|
|
149
|
+
"button": {
|
|
150
|
+
"save": "Save Changes",
|
|
151
|
+
"cancel": "Cancel"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Step 4: Configure Project i18n
|
|
157
|
+
|
|
158
|
+
**src/domains/localization/infrastructure/config/i18n.ts:**
|
|
159
|
+
```typescript
|
|
160
|
+
import i18n from 'i18next';
|
|
161
|
+
import { initReactI18next } from 'react-i18next';
|
|
162
|
+
import { DEFAULT_LANGUAGE } from '@umituz/react-native-localization';
|
|
163
|
+
|
|
164
|
+
// Load project translations
|
|
165
|
+
const loadProjectTranslations = () => {
|
|
166
|
+
try {
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
168
|
+
return require('../locales/en-US');
|
|
169
|
+
} catch {
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const projectTranslations = loadProjectTranslations();
|
|
175
|
+
|
|
176
|
+
// Configure i18n with project translations
|
|
177
|
+
i18n
|
|
178
|
+
.use(initReactI18next)
|
|
179
|
+
.init({
|
|
180
|
+
resources: {
|
|
181
|
+
'en-US': {
|
|
182
|
+
translation: projectTranslations.default || projectTranslations
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
lng: DEFAULT_LANGUAGE,
|
|
186
|
+
fallbackLng: DEFAULT_LANGUAGE,
|
|
187
|
+
interpolation: {
|
|
188
|
+
escapeValue: false,
|
|
189
|
+
},
|
|
190
|
+
react: {
|
|
191
|
+
useSuspense: false,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export default i18n;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Step 5: Wrap Your App
|
|
37
199
|
|
|
38
200
|
```tsx
|
|
39
201
|
import { LocalizationProvider } from '@umituz/react-native-localization';
|
|
202
|
+
import './domains/localization/infrastructure/config/i18n'; // Initialize project i18n
|
|
40
203
|
|
|
41
204
|
export default function App() {
|
|
42
205
|
return (
|
|
@@ -47,7 +210,89 @@ export default function App() {
|
|
|
47
210
|
}
|
|
48
211
|
```
|
|
49
212
|
|
|
50
|
-
###
|
|
213
|
+
### Step 6: Use in Components
|
|
214
|
+
|
|
215
|
+
**Option A: Use Package Localization (Recommended for UI components)**
|
|
216
|
+
```tsx
|
|
217
|
+
import { useLocalization } from '@umituz/react-native-localization';
|
|
218
|
+
|
|
219
|
+
function MyComponent() {
|
|
220
|
+
const { t } = useLocalization();
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<View>
|
|
224
|
+
<Text>{t('general.save')}</Text> {/* From package */}
|
|
225
|
+
<Text>{t('yourFeature.title')}</Text> {/* From your project */}
|
|
226
|
+
</View>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Option B: Use Project Localization (For business logic)**
|
|
232
|
+
```tsx
|
|
233
|
+
import { useTranslation } from 'react-i18next';
|
|
234
|
+
|
|
235
|
+
function MyComponent() {
|
|
236
|
+
const { t } = useTranslation();
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<View>
|
|
240
|
+
<Text>{t('yourFeature.title')}</Text> {/* From your project */}
|
|
241
|
+
</View>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Step 7: Translation Management
|
|
247
|
+
|
|
248
|
+
#### Adding New Translations
|
|
249
|
+
|
|
250
|
+
1. **Create JSON file** in `src/domains/localization/infrastructure/locales/en-US/`
|
|
251
|
+
2. **Add translations** following flat or nested structure
|
|
252
|
+
3. **Auto-loading** happens automatically via `index.ts`
|
|
253
|
+
|
|
254
|
+
#### Translation File Structure
|
|
255
|
+
|
|
256
|
+
**Flat Structure (Recommended for simple features):**
|
|
257
|
+
```json
|
|
258
|
+
// settings.json
|
|
259
|
+
{
|
|
260
|
+
"title": "Settings",
|
|
261
|
+
"language": "Language",
|
|
262
|
+
"theme": "Theme",
|
|
263
|
+
"notifications": "Notifications"
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Nested Structure (For complex features):**
|
|
268
|
+
```json
|
|
269
|
+
// auth.json
|
|
270
|
+
{
|
|
271
|
+
"login": {
|
|
272
|
+
"title": "Welcome Back",
|
|
273
|
+
"email": "Email Address",
|
|
274
|
+
"password": "Password",
|
|
275
|
+
"forgotPassword": "Forgot Password?",
|
|
276
|
+
"signIn": "Sign In"
|
|
277
|
+
},
|
|
278
|
+
"register": {
|
|
279
|
+
"title": "Create Account",
|
|
280
|
+
"confirmPassword": "Confirm Password",
|
|
281
|
+
"signUp": "Sign Up"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Usage in Components
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
// Flat structure
|
|
290
|
+
t('settings.title') // "Settings"
|
|
291
|
+
|
|
292
|
+
// Nested structure
|
|
293
|
+
t('auth.login.title') // "Welcome Back"
|
|
294
|
+
t('auth.register.signUp') // "Sign Up"
|
|
295
|
+
```
|
|
51
296
|
|
|
52
297
|
```tsx
|
|
53
298
|
import { useLocalization } from '@umituz/react-native-localization';
|
package/package.json
CHANGED
|
@@ -31,45 +31,14 @@ const packageTranslations = loadPackageTranslations();
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Load project translations for all supported languages
|
|
34
|
-
*
|
|
34
|
+
* This function is a placeholder for future extensibility
|
|
35
|
+
* Currently returns empty translations as projects should manage their own translations
|
|
35
36
|
*/
|
|
36
37
|
const loadProjectTranslations = (): Record<string, any> => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
// Dynamic loading through filesystem package
|
|
43
|
-
const { loadJsonModules } = require('@umituz/react-native-filesystem');
|
|
44
|
-
|
|
45
|
-
// Try to load each language dynamically
|
|
46
|
-
const supportedLanguages = [
|
|
47
|
-
'en-US', 'ar-SA', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR',
|
|
48
|
-
'en-AU', 'en-CA', 'en-GB', 'es-ES', 'es-MX', 'fi-FI', 'fr-CA',
|
|
49
|
-
'fr-FR', 'hi-IN', 'hr-HR', 'hu-HU', 'id-ID', 'it-IT', 'ja-JP',
|
|
50
|
-
'ko-KR', 'ms-MY', 'nl-NL', 'no-NO', 'pl-PL', 'pt-BR', 'pt-PT',
|
|
51
|
-
'ro-RO', 'ru-RU', 'sk-SK', 'sv-SE', 'th-TH', 'tl-PH', 'tr-TR',
|
|
52
|
-
'uk-UA', 'vi-VN', 'zh-CN', 'zh-TW'
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
for (const langCode of supportedLanguages) {
|
|
56
|
-
try {
|
|
57
|
-
// Attempt to load language module dynamically
|
|
58
|
-
// This will work if the project has set up locales properly
|
|
59
|
-
const langModule = require(`../../../../../../src/locales/${langCode}`);
|
|
60
|
-
if (langModule?.default || langModule) {
|
|
61
|
-
translations[langCode] = langModule.default || langModule;
|
|
62
|
-
}
|
|
63
|
-
} catch {
|
|
64
|
-
// Language not available - skip silently
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
// Filesystem package not available or dynamic loading failed
|
|
69
|
-
// Fallback to no project translations
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return translations;
|
|
38
|
+
// Projects should create their own localization domains
|
|
39
|
+
// and manage translations within their app structure
|
|
40
|
+
// This package provides only the core i18n infrastructure
|
|
41
|
+
return {};
|
|
73
42
|
};
|
|
74
43
|
|
|
75
44
|
const projectTranslations = loadProjectTranslations();
|
|
@@ -114,13 +83,13 @@ const mergeTranslations = (packageTranslations: any, projectTranslations: any):
|
|
|
114
83
|
*/
|
|
115
84
|
const buildResources = (): Record<string, { translation: any }> => {
|
|
116
85
|
const resources: Record<string, { translation: any }> = {};
|
|
117
|
-
|
|
86
|
+
|
|
118
87
|
// Build resources for each supported language
|
|
119
88
|
for (const lang of SUPPORTED_LANGUAGES) {
|
|
120
89
|
const langCode = lang.code;
|
|
121
90
|
const packageTranslation = langCode === 'en-US' ? (packageTranslations['en-US'] || {}) : {};
|
|
122
91
|
const projectTranslation = projectTranslations[langCode] || {};
|
|
123
|
-
|
|
92
|
+
|
|
124
93
|
// For en-US, merge package and project translations
|
|
125
94
|
// For other languages, use project translations only (fallback to en-US handled by i18n)
|
|
126
95
|
if (langCode === 'en-US') {
|
|
@@ -133,14 +102,14 @@ const buildResources = (): Record<string, { translation: any }> => {
|
|
|
133
102
|
};
|
|
134
103
|
}
|
|
135
104
|
}
|
|
136
|
-
|
|
105
|
+
|
|
137
106
|
// Ensure en-US is always present
|
|
138
107
|
if (!resources['en-US']) {
|
|
139
108
|
resources['en-US'] = {
|
|
140
109
|
translation: packageTranslations['en-US'] || {},
|
|
141
110
|
};
|
|
142
111
|
}
|
|
143
|
-
|
|
112
|
+
|
|
144
113
|
return resources;
|
|
145
114
|
};
|
|
146
115
|
|