@umituz/react-native-localization 2.0.0 → 2.1.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.
@@ -1,216 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable no-console */
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { getLocalesDir } = require('./utils/findLocalesDir');
7
-
8
- // Function to get all keys from an object recursively
9
- function getAllKeys(obj, prefix = '') {
10
- let keys = [];
11
- for (const key in obj) {
12
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
13
- const fullKey = prefix ? `${prefix}.${key}` : key;
14
- if (
15
- typeof obj[key] === 'object' &&
16
- obj[key] !== null &&
17
- !Array.isArray(obj[key])
18
- ) {
19
- keys = keys.concat(getAllKeys(obj[key], fullKey));
20
- } else {
21
- keys.push(fullKey);
22
- }
23
- }
24
- }
25
- return keys;
26
- }
27
-
28
- // Function to remove a key from an object by path
29
- function removeKeyByPath(obj, keyPath) {
30
- const keys = keyPath.split('.');
31
- let current = obj;
32
-
33
- for (let i = 0; i < keys.length - 1; i++) {
34
- if (!current[keys[i]] || typeof current[keys[i]] !== 'object') {
35
- return false; // Path doesn't exist
36
- }
37
- current = current[keys[i]];
38
- }
39
-
40
- const lastKey = keys[keys.length - 1];
41
- if (current.hasOwnProperty(lastKey)) {
42
- delete current[keys[keys.length - 1]];
43
-
44
- // Clean up empty parent objects
45
- if (keys.length > 1) {
46
- let parent = obj;
47
- for (let i = 0; i < keys.length - 2; i++) {
48
- parent = parent[keys[i]];
49
- }
50
- const parentKey = keys[keys.length - 2];
51
- if (Object.keys(parent[parentKey]).length === 0) {
52
- delete parent[parentKey];
53
- }
54
- }
55
-
56
- return true;
57
- }
58
- return false;
59
- }
60
-
61
- // Function to remove unused keys from a translation file
62
- function removeUnusedKeys(enUS, targetLang, filePath, langCode, dryRun = false) {
63
- const enKeys = getAllKeys(enUS);
64
- const targetKeys = getAllKeys(targetLang);
65
-
66
- const extraKeys = targetKeys.filter(key => !enKeys.includes(key));
67
-
68
- if (extraKeys.length === 0) {
69
- return { removed: 0, keys: [] };
70
- }
71
-
72
- // Create a deep copy to modify
73
- const cleanedLang = JSON.parse(JSON.stringify(targetLang));
74
-
75
- let removedCount = 0;
76
- for (const key of extraKeys) {
77
- if (removeKeyByPath(cleanedLang, key)) {
78
- removedCount++;
79
- }
80
- }
81
-
82
- if (!dryRun && removedCount > 0) {
83
- // Write cleaned file
84
- const targetFile = filePath;
85
- fs.writeFileSync(targetFile, JSON.stringify(cleanedLang, null, 2) + '\n', 'utf8');
86
- }
87
-
88
- return { removed: removedCount, keys: extraKeys };
89
- }
90
-
91
- // Main function
92
- function main() {
93
- const args = process.argv.slice(2);
94
- const targetLanguage = args.find(arg => !arg.startsWith('--'));
95
- const dryRun = args.includes('--dry-run') || args.includes('-d');
96
- const allLanguages = args.includes('--all') || args.includes('-a');
97
-
98
- if (!targetLanguage && !allLanguages) {
99
- console.log('Usage: npm run i18n:remove-unused <language-code> [--dry-run]');
100
- console.log('Example: npm run i18n:remove-unused tr-TR');
101
- console.log('Example: npm run i18n:remove-unused tr-TR --dry-run (preview only, no removal)');
102
- console.log('Example: npm run i18n:remove-unused --all (all languages)');
103
- process.exit(1);
104
- }
105
-
106
- // Find project's locales directory
107
- const localesDir = getLocalesDir();
108
- const enUSDir = path.join(localesDir, 'en-US');
109
-
110
- // Auto-discover all JSON files
111
- const files = fs
112
- .readdirSync(enUSDir)
113
- .filter(file => file.endsWith('.json'))
114
- .sort();
115
-
116
- if (files.length === 0) {
117
- console.error('āŒ No JSON files found in en-US directory!');
118
- process.exit(1);
119
- }
120
-
121
- // Get language directories
122
- const allLanguageDirs = fs
123
- .readdirSync(localesDir)
124
- .filter(dir => {
125
- const fullPath = path.join(localesDir, dir);
126
- return fs.statSync(fullPath).isDirectory() && dir !== 'en-US';
127
- })
128
- .sort();
129
-
130
- // Determine which languages to process
131
- let languageDirs = allLanguageDirs;
132
- if (!allLanguages) {
133
- if (targetLanguage === 'spanish' || targetLanguage === 'es') {
134
- languageDirs = allLanguageDirs.filter(dir => dir.startsWith('es-'));
135
- } else if (allLanguageDirs.includes(targetLanguage)) {
136
- languageDirs = [targetLanguage];
137
- } else {
138
- console.log(`āŒ Language ${targetLanguage} not found. Available: ${allLanguageDirs.join(', ')}`);
139
- process.exit(1);
140
- }
141
- }
142
-
143
- if (dryRun) {
144
- console.log('šŸ” DRY RUN MODE - No files will be modified\n');
145
- }
146
-
147
- let totalRemoved = 0;
148
- let totalFiles = 0;
149
-
150
- console.log(`🧹 Removing unused keys from ${languageDirs.length} language(s)...\n`);
151
-
152
- languageDirs.forEach(langCode => {
153
- const langDir = path.join(localesDir, langCode);
154
- let langRemoved = 0;
155
- let langFiles = 0;
156
-
157
- console.log(`\nšŸŒ Processing ${langCode}...`);
158
-
159
- files.forEach(file => {
160
- const enUSPath = path.join(enUSDir, file);
161
- const langPath = path.join(langDir, file);
162
-
163
- if (!fs.existsSync(enUSPath)) {
164
- return;
165
- }
166
-
167
- if (!fs.existsSync(langPath)) {
168
- return;
169
- }
170
-
171
- try {
172
- const enUS = JSON.parse(fs.readFileSync(enUSPath, 'utf8'));
173
- const langData = JSON.parse(fs.readFileSync(langPath, 'utf8'));
174
-
175
- const result = removeUnusedKeys(enUS, langData, langPath, langCode, dryRun);
176
-
177
- if (result.removed > 0) {
178
- langRemoved += result.removed;
179
- langFiles++;
180
- totalRemoved += result.removed;
181
- totalFiles++;
182
-
183
- if (dryRun) {
184
- console.log(` šŸ” ${file}: Would remove ${result.removed} key(s): ${result.keys.slice(0, 5).join(', ')}${result.keys.length > 5 ? '...' : ''}`);
185
- } else {
186
- console.log(` āœ… ${file}: Removed ${result.removed} unused key(s): ${result.keys.slice(0, 5).join(', ')}${result.keys.length > 5 ? '...' : ''}`);
187
- }
188
- }
189
- } catch (error) {
190
- console.log(` āŒ Error processing ${file}: ${error.message}`);
191
- }
192
- });
193
-
194
- if (langRemoved === 0) {
195
- console.log(` āœ… ${langCode}: No unused keys found`);
196
- } else {
197
- console.log(` šŸ“Š ${langCode}: Removed ${langRemoved} unused key(s) from ${langFiles} file(s)`);
198
- }
199
- });
200
-
201
- console.log('\nšŸ“Š Summary:');
202
- console.log(` Languages processed: ${languageDirs.length}`);
203
- console.log(` Files modified: ${totalFiles}`);
204
- console.log(` Total keys removed: ${totalRemoved}`);
205
-
206
- if (dryRun) {
207
- console.log('\nšŸ’” Run without --dry-run to actually remove the keys.');
208
- } else if (totalRemoved > 0) {
209
- console.log('\nāœ… Unused keys removed successfully!');
210
- } else {
211
- console.log('\nāœ… No unused keys found!');
212
- }
213
- }
214
-
215
- main();
216
-
@@ -1,290 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable no-console */
3
-
4
- const fs = require("fs");
5
- const path = require("path");
6
- const { getLocalesDir } = require('./utils/findLocalesDir');
7
-
8
- // All 38 languages (excluding en-US)
9
- const LANGUAGES = [
10
- "ar-SA", // Arabic
11
- "bg-BG", // Bulgarian
12
- "cs-CZ", // Czech
13
- "da-DK", // Danish
14
- "de-DE", // German
15
- "el-GR", // Greek
16
- "en-AU", // English (Australia)
17
- "en-CA", // English (Canada)
18
- "en-GB", // English (UK)
19
- "es-ES", // Spanish (Spain)
20
- "es-MX", // Spanish (Mexico)
21
- "fi-FI", // Finnish
22
- "fr-CA", // French (Canada)
23
- "fr-FR", // French (France)
24
- "hi-IN", // Hindi
25
- "hr-HR", // Croatian
26
- "hu-HU", // Hungarian
27
- "id-ID", // Indonesian
28
- "it-IT", // Italian
29
- "ja-JP", // Japanese
30
- "ko-KR", // Korean
31
- "ms-MY", // Malay
32
- "nl-NL", // Dutch
33
- "no-NO", // Norwegian
34
- "pl-PL", // Polish
35
- "pt-BR", // Portuguese (Brazil)
36
- "pt-PT", // Portuguese (Portugal)
37
- "ro-RO", // Romanian
38
- "ru-RU", // Russian
39
- "sk-SK", // Slovak
40
- "sv-SE", // Swedish
41
- "th-TH", // Thai
42
- "tl-PH", // Tagalog
43
- "tr-TR", // Turkish
44
- "uk-UA", // Ukrainian
45
- "vi-VN", // Vietnamese
46
- "zh-CN", // Chinese Simplified
47
- "zh-TW", // Chinese Traditional
48
- ];
49
-
50
- // Default translation files from package
51
- const DEFAULT_FILES = [
52
- 'auth.json',
53
- 'branding.json',
54
- 'datetime.json',
55
- 'errors.json',
56
- 'flashcards.json',
57
- 'general.json',
58
- 'navigation.json',
59
- 'onboarding.json',
60
- 'settings.json',
61
- ];
62
-
63
- // Get path to package's default en-US files
64
- function getPackageEnUSPath() {
65
- // Script is in: node_modules/@umituz/react-native-localization/scripts/setup-languages.js
66
- // Package en-US is in: node_modules/@umituz/react-native-localization/src/infrastructure/locales/en-US
67
- const scriptDir = __dirname;
68
- const packageRoot = path.resolve(scriptDir, '..');
69
- return path.join(packageRoot, 'src/infrastructure/locales/en-US');
70
- }
71
-
72
- async function setupLanguages() {
73
- // Find or create project's locales directory
74
- const localesDir = getLocalesDir(true); // Create if not exists
75
- const enUSDir = path.join(localesDir, "en-US");
76
- const packageEnUSPath = getPackageEnUSPath();
77
-
78
- console.log("šŸš€ Setting up language directories and files...\n");
79
-
80
- // Create en-US directory if it doesn't exist
81
- if (!fs.existsSync(enUSDir)) {
82
- fs.mkdirSync(enUSDir, { recursive: true });
83
- console.log(`šŸ“ Created directory: en-US/`);
84
- }
85
-
86
- // Check if en-US directory has any JSON files
87
- let files = [];
88
- if (fs.existsSync(enUSDir)) {
89
- files = fs
90
- .readdirSync(enUSDir)
91
- .filter((file) => file.endsWith(".json"))
92
- .sort();
93
- }
94
-
95
- // If no files found, copy default files from package
96
- if (files.length === 0) {
97
- console.log("šŸ“¦ No JSON files found in en-US directory.");
98
- console.log(" Copying default files from package...\n");
99
-
100
- if (!fs.existsSync(packageEnUSPath)) {
101
- console.error("āŒ Package default files not found!");
102
- console.error(" Expected path:", packageEnUSPath);
103
- console.error(" Please ensure @umituz/react-native-localization is installed.");
104
- process.exit(1);
105
- }
106
-
107
- // Copy default files from package
108
- for (const file of DEFAULT_FILES) {
109
- const packageFile = path.join(packageEnUSPath, file);
110
- const targetFile = path.join(enUSDir, file);
111
-
112
- if (fs.existsSync(packageFile)) {
113
- const content = fs.readFileSync(packageFile, "utf8");
114
- fs.writeFileSync(targetFile, content);
115
- console.log(` šŸ“„ Created: en-US/${file}`);
116
- files.push(file);
117
- } else {
118
- console.warn(` āš ļø Warning: Default file not found in package: ${file}`);
119
- }
120
- }
121
-
122
- if (files.length === 0) {
123
- console.error("āŒ Failed to copy default files from package!");
124
- process.exit(1);
125
- }
126
-
127
- console.log("");
128
- }
129
-
130
- console.log(`šŸ“„ Found ${files.length} translation files in en-US:`);
131
- files.forEach((file) => console.log(` - ${file}`));
132
- console.log("");
133
-
134
- let createdDirs = 0;
135
- let createdFiles = 0;
136
-
137
- for (const langCode of LANGUAGES) {
138
- const langDir = path.join(localesDir, langCode);
139
-
140
- // Create language directory if it doesn't exist
141
- if (!fs.existsSync(langDir)) {
142
- fs.mkdirSync(langDir, { recursive: true });
143
- console.log(`šŸ“ Created directory: ${langCode}/`);
144
- createdDirs++;
145
- }
146
-
147
- // Copy each file from en-US
148
- for (const file of files) {
149
- const enUSFile = path.join(enUSDir, file);
150
- const targetFile = path.join(langDir, file);
151
-
152
- if (!fs.existsSync(targetFile)) {
153
- // Copy the en-US file as a starting point
154
- const content = fs.readFileSync(enUSFile, "utf8");
155
- fs.writeFileSync(targetFile, content);
156
- console.log(` šŸ“„ Created: ${langCode}/${file}`);
157
- createdFiles++;
158
- }
159
- }
160
- }
161
-
162
- // Create index.ts files for all languages (including en-US)
163
- console.log(`\nšŸ“ Creating index.ts loaders for all languages...`);
164
- let createdIndexFiles = 0;
165
-
166
- // Create index.ts for en-US
167
- const enUSIndexPath = path.join(enUSDir, 'index.ts');
168
- if (!fs.existsSync(enUSIndexPath)) {
169
- const enUSIndexContent = `/**
170
- * Auto-loader for en-US translation modules
171
- *
172
- * AUTOMATIC TRANSLATION FILE DETECTION:
173
- * - Uses Metro bundler's require.context to auto-discover .json files
174
- * - Adding new translation file = just create .json file (zero manual updates)
175
- * - Build-time resolution (fast, no runtime overhead)
176
- * - Works for all languages automatically
177
- *
178
- * OFFLINE-ONLY TRANSLATIONS (All domains work without backend):
179
- * - Automatically imports ALL .json files in this directory
180
- * - Alphabetically sorted for consistency
181
- * - Type-safe with TypeScript
182
- *
183
- * USAGE:
184
- * 1. Create new translation file: my_domain.json
185
- * 2. File is auto-discovered and loaded
186
- * 3. Access via t('my_domain.key')
187
- *
188
- * This file is automatically generated by setup-languages.js
189
- * but can be manually edited if needed.
190
- */
191
-
192
- // Metro bundler require.context - auto-discover all .json files
193
- // eslint-disable-next-line @typescript-eslint/no-require-imports
194
- const translationContext = (require as any).context('./', false, /\\.json$/);
195
-
196
- /**
197
- * Load all JSON modules automatically
198
- * Extracts module name from path (e.g., './auth.json' -> 'auth')
199
- */
200
- const translations: Record<string, any> = {};
201
-
202
- translationContext.keys().forEach((key: string) => {
203
- // Extract module name from path: './auth.json' -> 'auth'
204
- const match = key.match(/\\.\\/([^/]+)\\.json$/);
205
- if (match) {
206
- const moduleName = match[1];
207
- translations[moduleName] = translationContext(key);
208
- }
209
- });
210
-
211
- export default translations;
212
- `;
213
- fs.writeFileSync(enUSIndexPath, enUSIndexContent, 'utf8');
214
- console.log(` šŸ“„ Created: en-US/index.ts`);
215
- createdIndexFiles++;
216
- }
217
-
218
- // Create index.ts for all other languages
219
- for (const langCode of LANGUAGES) {
220
- const langDir = path.join(localesDir, langCode);
221
- const indexPath = path.join(langDir, 'index.ts');
222
-
223
- if (fs.existsSync(langDir) && !fs.existsSync(indexPath)) {
224
- const indexContent = `/**
225
- * Auto-loader for ${langCode} translation modules
226
- *
227
- * AUTOMATIC TRANSLATION FILE DETECTION:
228
- * - Uses Metro bundler's require.context to auto-discover .json files
229
- * - Adding new translation file = just create .json file (zero manual updates)
230
- * - Build-time resolution (fast, no runtime overhead)
231
- * - Works for all languages automatically
232
- *
233
- * OFFLINE-ONLY TRANSLATIONS (All domains work without backend):
234
- * - Automatically imports ALL .json files in this directory
235
- * - Alphabetically sorted for consistency
236
- * - Type-safe with TypeScript
237
- *
238
- * USAGE:
239
- * 1. Create new translation file: my_domain.json
240
- * 2. File is auto-discovered and loaded
241
- * 3. Access via t('my_domain.key')
242
- *
243
- * This file is automatically generated by setup-languages.js
244
- * but can be manually edited if needed.
245
- */
246
-
247
- // Metro bundler require.context - auto-discover all .json files
248
- // eslint-disable-next-line @typescript-eslint/no-require-imports
249
- const translationContext = (require as any).context('./', false, /\\.json$/);
250
-
251
- /**
252
- * Load all JSON modules automatically
253
- * Extracts module name from path (e.g., './auth.json' -> 'auth')
254
- */
255
- const translations: Record<string, any> = {};
256
-
257
- translationContext.keys().forEach((key: string) => {
258
- // Extract module name from path: './auth.json' -> 'auth'
259
- const match = key.match(/\\.\\/([^/]+)\\.json$/);
260
- if (match) {
261
- const moduleName = match[1];
262
- translations[moduleName] = translationContext(key);
263
- }
264
- });
265
-
266
- export default translations;
267
- `;
268
- fs.writeFileSync(indexPath, indexContent, 'utf8');
269
- console.log(` šŸ“„ Created: ${langCode}/index.ts`);
270
- createdIndexFiles++;
271
- }
272
- }
273
-
274
- console.log(`\nāœ… Setup completed!`);
275
- console.log(` Directories created: ${createdDirs}`);
276
- console.log(` Files created: ${createdFiles}`);
277
- console.log(` Index files created: ${createdIndexFiles}`);
278
- console.log(` Total languages: ${LANGUAGES.length + 1} (including en-US)`);
279
- console.log(
280
- `\nšŸ“ Next steps:`,
281
- );
282
- console.log(` 1. Add your translation keys to en-US/*.json files`);
283
- console.log(` 2. Run 'npm run i18n:translate' to auto-translate all languages`);
284
- console.log(` 3. Run 'npm run i18n:check all' to verify completeness`);
285
- }
286
-
287
- setupLanguages().catch((error) => {
288
- console.error("āŒ Setup failed:", error);
289
- process.exit(1);
290
- });