@umituz/react-native-google-translate 1.0.6 → 1.0.7
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.
|
@@ -84,6 +84,8 @@ class GoogleTranslateService {
|
|
|
84
84
|
translatedKeys: [],
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
+
// Use batch API call for better performance
|
|
88
|
+
const batchSize = 50;
|
|
87
89
|
const stats = {
|
|
88
90
|
totalCount: requests.length,
|
|
89
91
|
successCount: 0,
|
|
@@ -91,23 +93,52 @@ class GoogleTranslateService {
|
|
|
91
93
|
skippedCount: 0,
|
|
92
94
|
translatedKeys: [],
|
|
93
95
|
};
|
|
94
|
-
for (
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
for (let i = 0; i < requests.length; i += batchSize) {
|
|
97
|
+
const batch = requests.slice(i, i + batchSize);
|
|
98
|
+
// Wait for rate limiter once per batch instead of once per request
|
|
99
|
+
await this.rateLimiter.waitForSlot();
|
|
100
|
+
try {
|
|
101
|
+
const translations = await this.callTranslateAPIBatch(batch, requests[0].targetLanguage);
|
|
102
|
+
for (let j = 0; j < batch.length; j++) {
|
|
103
|
+
const request = batch[j];
|
|
104
|
+
const translatedText = translations[j];
|
|
105
|
+
if (translatedText && translatedText !== request.text) {
|
|
106
|
+
stats.successCount++;
|
|
107
|
+
stats.translatedKeys.push({
|
|
108
|
+
key: request.text,
|
|
109
|
+
from: request.text,
|
|
110
|
+
to: translatedText,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (!translatedText) {
|
|
114
|
+
stats.failureCount++;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
stats.skippedCount++;
|
|
118
|
+
}
|
|
107
119
|
}
|
|
108
120
|
}
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
catch (error) {
|
|
122
|
+
// Fallback to individual requests on batch failure
|
|
123
|
+
for (const request of batch) {
|
|
124
|
+
const result = await this.translate(request);
|
|
125
|
+
if (result.success) {
|
|
126
|
+
if (result.translatedText === result.originalText) {
|
|
127
|
+
stats.skippedCount++;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
stats.successCount++;
|
|
131
|
+
stats.translatedKeys.push({
|
|
132
|
+
key: request.text,
|
|
133
|
+
from: result.originalText,
|
|
134
|
+
to: result.translatedText,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
stats.failureCount++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
111
142
|
}
|
|
112
143
|
}
|
|
113
144
|
return stats;
|
|
@@ -129,6 +160,8 @@ class GoogleTranslateService {
|
|
|
129
160
|
return;
|
|
130
161
|
}
|
|
131
162
|
const keys = Object.keys(sourceObject);
|
|
163
|
+
// Collect all texts to translate first
|
|
164
|
+
const textsToTranslate = [];
|
|
132
165
|
for (const key of keys) {
|
|
133
166
|
const enValue = sourceObject[key];
|
|
134
167
|
const targetValue = targetObject[key];
|
|
@@ -143,30 +176,42 @@ class GoogleTranslateService {
|
|
|
143
176
|
else if (typeof enValue === "string") {
|
|
144
177
|
stats.totalCount++;
|
|
145
178
|
if ((0, textValidator_util_1.needsTranslation)(targetValue, enValue)) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
textsToTranslate.push({ key, enValue, currentPath });
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
stats.skippedCount++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Batch translate all texts at once
|
|
187
|
+
if (textsToTranslate.length > 0) {
|
|
188
|
+
const batchSize = 50; // Google Translate API can handle ~50 texts at once
|
|
189
|
+
for (let i = 0; i < textsToTranslate.length; i += batchSize) {
|
|
190
|
+
const batch = textsToTranslate.slice(i, i + batchSize);
|
|
191
|
+
const results = await this.translateBatch(batch.map(item => ({
|
|
192
|
+
text: item.enValue,
|
|
193
|
+
targetLanguage,
|
|
194
|
+
})));
|
|
195
|
+
// Apply translations
|
|
196
|
+
for (let j = 0; j < batch.length; j++) {
|
|
197
|
+
const { key, enValue, currentPath } = batch[j];
|
|
198
|
+
const translatedKey = results.translatedKeys[j];
|
|
199
|
+
if (translatedKey && translatedKey.from !== enValue) {
|
|
200
|
+
targetObject[key] = translatedKey.to;
|
|
153
201
|
stats.successCount++;
|
|
154
202
|
stats.translatedKeys.push({
|
|
155
203
|
key: currentPath,
|
|
156
204
|
from: enValue,
|
|
157
|
-
to:
|
|
205
|
+
to: translatedKey.to,
|
|
158
206
|
});
|
|
159
207
|
}
|
|
160
|
-
else if (!
|
|
208
|
+
else if (!translatedKey) {
|
|
161
209
|
stats.failureCount++;
|
|
162
210
|
}
|
|
163
211
|
else {
|
|
164
212
|
stats.skippedCount++;
|
|
165
213
|
}
|
|
166
214
|
}
|
|
167
|
-
else {
|
|
168
|
-
stats.skippedCount++;
|
|
169
|
-
}
|
|
170
215
|
}
|
|
171
216
|
}
|
|
172
217
|
}
|
|
@@ -199,5 +244,41 @@ class GoogleTranslateService {
|
|
|
199
244
|
clearTimeout(timeoutId);
|
|
200
245
|
}
|
|
201
246
|
}
|
|
247
|
+
async callTranslateAPIBatch(requests, targetLanguage, sourceLanguage = "en") {
|
|
248
|
+
const timeout = this.config?.timeout || constants_1.DEFAULT_TIMEOUT;
|
|
249
|
+
// Build batch request URL
|
|
250
|
+
const queryParts = requests.map(req => encodeURIComponent(req.text)).join('&q=');
|
|
251
|
+
const url = `${constants_1.GOOGLE_TRANSLATE_API_URL}?client=gtx&sl=${sourceLanguage}&tl=${targetLanguage}&dt=t&q=${queryParts}`;
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
254
|
+
try {
|
|
255
|
+
const response = await fetch(url, {
|
|
256
|
+
signal: controller.signal,
|
|
257
|
+
});
|
|
258
|
+
if (!response.ok) {
|
|
259
|
+
throw new Error(`Batch API request failed: ${response.status}`);
|
|
260
|
+
}
|
|
261
|
+
const data = await response.json();
|
|
262
|
+
// Extract translations from batch response
|
|
263
|
+
const translations = [];
|
|
264
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
265
|
+
for (let i = 0; i < requests.length; i++) {
|
|
266
|
+
if (Array.isArray(data[i]) &&
|
|
267
|
+
data[i].length > 0 &&
|
|
268
|
+
Array.isArray(data[i][0]) &&
|
|
269
|
+
typeof data[i][0][0] === "string") {
|
|
270
|
+
translations.push(data[i][0][0]);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
translations.push(requests[i].text); // Fallback to original
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return translations;
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
clearTimeout(timeoutId);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
202
283
|
}
|
|
203
284
|
exports.googleTranslateService = new GoogleTranslateService();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-google-translate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Google Translate integration for React Native apps with rate limiting, batch translation, and TypeScript support",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -117,6 +117,8 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// Use batch API call for better performance
|
|
121
|
+
const batchSize = 50;
|
|
120
122
|
const stats: TranslationStats = {
|
|
121
123
|
totalCount: requests.length,
|
|
122
124
|
successCount: 0,
|
|
@@ -125,22 +127,52 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
125
127
|
translatedKeys: [],
|
|
126
128
|
};
|
|
127
129
|
|
|
128
|
-
for (
|
|
129
|
-
const
|
|
130
|
+
for (let i = 0; i < requests.length; i += batchSize) {
|
|
131
|
+
const batch = requests.slice(i, i + batchSize);
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
// Wait for rate limiter once per batch instead of once per request
|
|
134
|
+
await this.rateLimiter!.waitForSlot();
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const translations = await this.callTranslateAPIBatch(batch, requests[0].targetLanguage);
|
|
138
|
+
|
|
139
|
+
for (let j = 0; j < batch.length; j++) {
|
|
140
|
+
const request = batch[j];
|
|
141
|
+
const translatedText = translations[j];
|
|
142
|
+
|
|
143
|
+
if (translatedText && translatedText !== request.text) {
|
|
144
|
+
stats.successCount++;
|
|
145
|
+
stats.translatedKeys.push({
|
|
146
|
+
key: request.text,
|
|
147
|
+
from: request.text,
|
|
148
|
+
to: translatedText,
|
|
149
|
+
});
|
|
150
|
+
} else if (!translatedText) {
|
|
151
|
+
stats.failureCount++;
|
|
152
|
+
} else {
|
|
153
|
+
stats.skippedCount++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
// Fallback to individual requests on batch failure
|
|
158
|
+
for (const request of batch) {
|
|
159
|
+
const result = await this.translate(request);
|
|
160
|
+
|
|
161
|
+
if (result.success) {
|
|
162
|
+
if (result.translatedText === result.originalText) {
|
|
163
|
+
stats.skippedCount++;
|
|
164
|
+
} else {
|
|
165
|
+
stats.successCount++;
|
|
166
|
+
stats.translatedKeys.push({
|
|
167
|
+
key: request.text,
|
|
168
|
+
from: result.originalText,
|
|
169
|
+
to: result.translatedText,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
stats.failureCount++;
|
|
174
|
+
}
|
|
141
175
|
}
|
|
142
|
-
} else {
|
|
143
|
-
stats.failureCount++;
|
|
144
176
|
}
|
|
145
177
|
}
|
|
146
178
|
|
|
@@ -174,6 +206,9 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
174
206
|
|
|
175
207
|
const keys = Object.keys(sourceObject);
|
|
176
208
|
|
|
209
|
+
// Collect all texts to translate first
|
|
210
|
+
const textsToTranslate: Array<{key: string; enValue: string; currentPath: string}> = [];
|
|
211
|
+
|
|
177
212
|
for (const key of keys) {
|
|
178
213
|
const enValue = sourceObject[key];
|
|
179
214
|
const targetValue = targetObject[key];
|
|
@@ -197,28 +232,43 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
197
232
|
stats.totalCount++;
|
|
198
233
|
|
|
199
234
|
if (needsTranslation(targetValue, enValue)) {
|
|
200
|
-
|
|
201
|
-
|
|
235
|
+
textsToTranslate.push({key, enValue, currentPath});
|
|
236
|
+
} else {
|
|
237
|
+
stats.skippedCount++;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Batch translate all texts at once
|
|
243
|
+
if (textsToTranslate.length > 0) {
|
|
244
|
+
const batchSize = 50; // Google Translate API can handle ~50 texts at once
|
|
245
|
+
for (let i = 0; i < textsToTranslate.length; i += batchSize) {
|
|
246
|
+
const batch = textsToTranslate.slice(i, i + batchSize);
|
|
247
|
+
const results = await this.translateBatch(
|
|
248
|
+
batch.map(item => ({
|
|
249
|
+
text: item.enValue,
|
|
202
250
|
targetLanguage,
|
|
203
|
-
}
|
|
251
|
+
}))
|
|
252
|
+
);
|
|
204
253
|
|
|
205
|
-
|
|
254
|
+
// Apply translations
|
|
255
|
+
for (let j = 0; j < batch.length; j++) {
|
|
256
|
+
const {key, enValue, currentPath} = batch[j];
|
|
257
|
+
const translatedKey = results.translatedKeys[j];
|
|
206
258
|
|
|
207
|
-
if (
|
|
208
|
-
targetObject[key] =
|
|
259
|
+
if (translatedKey && translatedKey.from !== enValue) {
|
|
260
|
+
targetObject[key] = translatedKey.to;
|
|
209
261
|
stats.successCount++;
|
|
210
262
|
stats.translatedKeys.push({
|
|
211
263
|
key: currentPath,
|
|
212
264
|
from: enValue,
|
|
213
|
-
to:
|
|
265
|
+
to: translatedKey.to,
|
|
214
266
|
});
|
|
215
|
-
} else if (!
|
|
267
|
+
} else if (!translatedKey) {
|
|
216
268
|
stats.failureCount++;
|
|
217
269
|
} else {
|
|
218
270
|
stats.skippedCount++;
|
|
219
271
|
}
|
|
220
|
-
} else {
|
|
221
|
-
stats.skippedCount++;
|
|
222
272
|
}
|
|
223
273
|
}
|
|
224
274
|
}
|
|
@@ -264,6 +314,55 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
264
314
|
clearTimeout(timeoutId);
|
|
265
315
|
}
|
|
266
316
|
}
|
|
317
|
+
|
|
318
|
+
private async callTranslateAPIBatch(
|
|
319
|
+
requests: TranslationRequest[],
|
|
320
|
+
targetLanguage: string,
|
|
321
|
+
sourceLanguage: string = "en"
|
|
322
|
+
): Promise<string[]> {
|
|
323
|
+
const timeout = this.config?.timeout || DEFAULT_TIMEOUT;
|
|
324
|
+
|
|
325
|
+
// Build batch request URL
|
|
326
|
+
const queryParts = requests.map(req => encodeURIComponent(req.text)).join('&q=');
|
|
327
|
+
const url = `${GOOGLE_TRANSLATE_API_URL}?client=gtx&sl=${sourceLanguage}&tl=${targetLanguage}&dt=t&q=${queryParts}`;
|
|
328
|
+
|
|
329
|
+
const controller = new AbortController();
|
|
330
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
const response = await fetch(url, {
|
|
334
|
+
signal: controller.signal,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
throw new Error(`Batch API request failed: ${response.status}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const data = await response.json();
|
|
342
|
+
|
|
343
|
+
// Extract translations from batch response
|
|
344
|
+
const translations: string[] = [];
|
|
345
|
+
|
|
346
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
347
|
+
for (let i = 0; i < requests.length; i++) {
|
|
348
|
+
if (
|
|
349
|
+
Array.isArray(data[i]) &&
|
|
350
|
+
data[i].length > 0 &&
|
|
351
|
+
Array.isArray(data[i][0]) &&
|
|
352
|
+
typeof data[i][0][0] === "string"
|
|
353
|
+
) {
|
|
354
|
+
translations.push(data[i][0][0]);
|
|
355
|
+
} else {
|
|
356
|
+
translations.push(requests[i].text); // Fallback to original
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return translations;
|
|
362
|
+
} finally {
|
|
363
|
+
clearTimeout(timeoutId);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
267
366
|
}
|
|
268
367
|
|
|
269
368
|
export const googleTranslateService = new GoogleTranslateService();
|