@umituz/react-native-localization 1.6.6 → 1.7.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/package.json
CHANGED
|
@@ -81,8 +81,12 @@ const SKIP_WORDS = new Set([
|
|
|
81
81
|
/**
|
|
82
82
|
* Simple Google Translate API call using free endpoint
|
|
83
83
|
* Note: This uses Google's unofficial API. For production, use official API with key.
|
|
84
|
+
* Includes retry mechanism for rate limiting and HTML error responses.
|
|
84
85
|
*/
|
|
85
|
-
async function translateText(text, targetLang) {
|
|
86
|
+
async function translateText(text, targetLang, retryCount = 0) {
|
|
87
|
+
const MAX_RETRIES = 3;
|
|
88
|
+
const RETRY_DELAY = 1000; // 1 second
|
|
89
|
+
|
|
86
90
|
return new Promise((resolve, _reject) => {
|
|
87
91
|
if (SKIP_WORDS.has(text)) {
|
|
88
92
|
resolve(text);
|
|
@@ -99,28 +103,96 @@ async function translateText(text, targetLang) {
|
|
|
99
103
|
data += chunk;
|
|
100
104
|
});
|
|
101
105
|
res.on('end', () => {
|
|
106
|
+
// Check if response is HTML (error page)
|
|
107
|
+
if (data.trim().startsWith('<') || data.trim().startsWith('<!')) {
|
|
108
|
+
// HTML response - likely rate limit or error page
|
|
109
|
+
if (retryCount < MAX_RETRIES) {
|
|
110
|
+
if (options.verbose) {
|
|
111
|
+
console.warn(
|
|
112
|
+
` ⚠️ HTML response received for "${text}" to ${targetLang}, retrying... (${retryCount + 1}/${MAX_RETRIES})`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
// Retry after delay
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
translateText(text, targetLang, retryCount + 1).then(resolve);
|
|
118
|
+
}, RETRY_DELAY * (retryCount + 1)); // Exponential backoff
|
|
119
|
+
return;
|
|
120
|
+
} else {
|
|
121
|
+
console.warn(
|
|
122
|
+
`⚠️ Translation failed for "${text}" to ${targetLang}: HTML response (rate limit or API error)`
|
|
123
|
+
);
|
|
124
|
+
resolve(text); // Fallback to original
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check HTTP status code
|
|
130
|
+
if (res.statusCode !== 200) {
|
|
131
|
+
if (retryCount < MAX_RETRIES) {
|
|
132
|
+
if (options.verbose) {
|
|
133
|
+
console.warn(
|
|
134
|
+
` ⚠️ HTTP ${res.statusCode} for "${text}" to ${targetLang}, retrying... (${retryCount + 1}/${MAX_RETRIES})`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
translateText(text, targetLang, retryCount + 1).then(resolve);
|
|
139
|
+
}, RETRY_DELAY * (retryCount + 1));
|
|
140
|
+
return;
|
|
141
|
+
} else {
|
|
142
|
+
console.warn(
|
|
143
|
+
`⚠️ Translation failed for "${text}" to ${targetLang}: HTTP ${res.statusCode}`
|
|
144
|
+
);
|
|
145
|
+
resolve(text);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
102
150
|
try {
|
|
103
151
|
const parsed = JSON.parse(data);
|
|
152
|
+
if (!parsed || !parsed[0] || !Array.isArray(parsed[0])) {
|
|
153
|
+
throw new Error('Invalid response format');
|
|
154
|
+
}
|
|
104
155
|
const translated = parsed[0]
|
|
105
156
|
.map(item => item[0])
|
|
106
157
|
.join('')
|
|
107
158
|
.trim();
|
|
108
159
|
resolve(translated || text);
|
|
109
160
|
} catch (error) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
161
|
+
// JSON parse error - might be HTML or malformed response
|
|
162
|
+
if (retryCount < MAX_RETRIES) {
|
|
163
|
+
if (options.verbose) {
|
|
164
|
+
console.warn(
|
|
165
|
+
` ⚠️ Parse error for "${text}" to ${targetLang}, retrying... (${retryCount + 1}/${MAX_RETRIES}): ${error.message}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
translateText(text, targetLang, retryCount + 1).then(resolve);
|
|
170
|
+
}, RETRY_DELAY * (retryCount + 1));
|
|
171
|
+
} else {
|
|
172
|
+
console.warn(
|
|
173
|
+
`⚠️ Translation failed for "${text}" to ${targetLang}: ${error.message}`
|
|
174
|
+
);
|
|
175
|
+
resolve(text); // Fallback to original
|
|
176
|
+
}
|
|
115
177
|
}
|
|
116
178
|
});
|
|
117
179
|
})
|
|
118
180
|
.on('error', err => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
181
|
+
if (retryCount < MAX_RETRIES) {
|
|
182
|
+
if (options.verbose) {
|
|
183
|
+
console.warn(
|
|
184
|
+
` ⚠️ Network error for "${text}" to ${targetLang}, retrying... (${retryCount + 1}/${MAX_RETRIES}): ${err.message}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
translateText(text, targetLang, retryCount + 1).then(resolve);
|
|
189
|
+
}, RETRY_DELAY * (retryCount + 1));
|
|
190
|
+
} else {
|
|
191
|
+
console.warn(
|
|
192
|
+
`⚠️ Network error translating "${text}" to ${targetLang}: ${err.message}`
|
|
193
|
+
);
|
|
194
|
+
resolve(text); // Fallback to original
|
|
195
|
+
}
|
|
124
196
|
});
|
|
125
197
|
});
|
|
126
198
|
}
|
|
@@ -276,8 +348,8 @@ async function translateObject(enObj, targetObj, targetLang, path = '') {
|
|
|
276
348
|
translatedCount++;
|
|
277
349
|
stats.translated++;
|
|
278
350
|
|
|
279
|
-
// Add delay to avoid rate limiting (
|
|
280
|
-
await delay(
|
|
351
|
+
// Add delay to avoid rate limiting (300ms between requests - increased for stability)
|
|
352
|
+
await delay(300);
|
|
281
353
|
} catch (error) {
|
|
282
354
|
console.error(` ❌ Failed to translate "${currentPath}":`, error.message);
|
|
283
355
|
stats.errors++;
|
|
@@ -214,6 +214,9 @@ if (typeof global !== 'undefined' && !(global as any).__i18n_resources_logged) {
|
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// Global flag to ensure initReactI18next is only used once
|
|
218
|
+
let reactI18nextInitialized = false;
|
|
219
|
+
|
|
217
220
|
/**
|
|
218
221
|
* Initialize i18next
|
|
219
222
|
* CRITICAL: Check i18n.isInitialized to prevent multiple initializations
|
|
@@ -231,7 +234,13 @@ const initializeI18n = () => {
|
|
|
231
234
|
throw new Error('initReactI18next is undefined');
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
|
|
237
|
+
// CRITICAL: Only use initReactI18next once (prevents context registration issues)
|
|
238
|
+
if (!reactI18nextInitialized) {
|
|
239
|
+
i18n.use(initReactI18next);
|
|
240
|
+
reactI18nextInitialized = true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
i18n.init({
|
|
235
244
|
resources,
|
|
236
245
|
lng: DEFAULT_LANGUAGE,
|
|
237
246
|
fallbackLng: DEFAULT_LANGUAGE,
|