@umituz/react-native-google-translate 1.0.5 → 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.
Files changed (30) hide show
  1. package/dist/domain/entities/Language.entity.js +2 -1
  2. package/dist/domain/entities/Translation.entity.js +2 -1
  3. package/dist/domain/entities/index.js +2 -1
  4. package/dist/domain/index.js +18 -2
  5. package/dist/domain/interfaces/ITranslationService.interface.js +2 -1
  6. package/dist/domain/interfaces/index.js +2 -1
  7. package/dist/infrastructure/constants/api.constants.js +7 -4
  8. package/dist/infrastructure/constants/index.js +12 -2
  9. package/dist/infrastructure/constants/languages.constants.js +6 -3
  10. package/dist/infrastructure/index.js +25 -0
  11. package/dist/infrastructure/services/GoogleTranslate.service.js +123 -39
  12. package/dist/infrastructure/services/index.js +16 -3
  13. package/dist/infrastructure/utils/index.js +16 -0
  14. package/dist/infrastructure/utils/rateLimit.util.js +5 -1
  15. package/dist/infrastructure/utils/textValidator.util.js +18 -10
  16. package/dist/scripts/index.js +20 -4
  17. package/dist/scripts/setup.js +23 -15
  18. package/dist/scripts/sync.js +39 -30
  19. package/dist/scripts/translate.js +28 -20
  20. package/dist/scripts/utils/file-parser.js +19 -11
  21. package/dist/scripts/utils/index.js +21 -5
  22. package/dist/scripts/utils/key-detector.js +6 -2
  23. package/dist/scripts/utils/key-extractor.js +17 -11
  24. package/dist/scripts/utils/object-helper.js +6 -2
  25. package/dist/scripts/utils/sync-helper.js +6 -2
  26. package/package.json +1 -1
  27. package/src/infrastructure/services/GoogleTranslate.service.ts +123 -24
  28. package/src/scripts/setup.ts +2 -1
  29. package/src/scripts/sync.ts +2 -1
  30. package/src/scripts/translate.ts +2 -1
@@ -1,33 +1,41 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
  /**
3
4
  * Sync Translations Script
4
5
  * Synchronizes translation keys from en-US.ts to all other language files
5
6
  */
6
- import fs from 'fs';
7
- import path from 'path';
8
- import { parseTypeScriptFile, generateTypeScriptContent, } from './utils/file-parser';
9
- import { addMissingKeys, removeExtraKeys, } from './utils/sync-helper';
10
- import { detectNewKeys } from './utils/key-detector';
11
- import { extractUsedKeys } from './utils/key-extractor';
12
- import { setDeep, countKeys } from './utils/object-helper';
13
- export function syncLanguageFile(enUSPath, targetPath, langCode) {
14
- const enUS = parseTypeScriptFile(enUSPath);
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.syncLanguageFile = syncLanguageFile;
12
+ exports.syncTranslations = syncTranslations;
13
+ exports.runSyncTranslations = runSyncTranslations;
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const file_parser_1 = require("./utils/file-parser");
17
+ const sync_helper_1 = require("./utils/sync-helper");
18
+ const key_detector_1 = require("./utils/key-detector");
19
+ const key_extractor_1 = require("./utils/key-extractor");
20
+ const object_helper_1 = require("./utils/object-helper");
21
+ function syncLanguageFile(enUSPath, targetPath, langCode) {
22
+ const enUS = (0, file_parser_1.parseTypeScriptFile)(enUSPath);
15
23
  let target;
16
24
  try {
17
- target = parseTypeScriptFile(targetPath);
25
+ target = (0, file_parser_1.parseTypeScriptFile)(targetPath);
18
26
  }
19
27
  catch {
20
28
  target = {};
21
29
  }
22
- const detectedNewKeys = detectNewKeys(enUS, target);
30
+ const detectedNewKeys = (0, key_detector_1.detectNewKeys)(enUS, target);
23
31
  const addStats = { added: 0, newKeys: [] };
24
32
  const removeStats = { removed: 0, removedKeys: [] };
25
- addMissingKeys(enUS, target, addStats);
26
- removeExtraKeys(enUS, target, removeStats);
33
+ (0, sync_helper_1.addMissingKeys)(enUS, target, addStats);
34
+ (0, sync_helper_1.removeExtraKeys)(enUS, target, removeStats);
27
35
  const changed = (addStats.added || 0) > 0 || (removeStats.removed || 0) > 0;
28
36
  if (changed) {
29
- const content = generateTypeScriptContent(target, langCode);
30
- fs.writeFileSync(targetPath, content);
37
+ const content = (0, file_parser_1.generateTypeScriptContent)(target, langCode);
38
+ fs_1.default.writeFileSync(targetPath, content);
31
39
  }
32
40
  return {
33
41
  added: addStats.added,
@@ -42,9 +50,9 @@ function processExtraction(srcDir, enUSPath) {
42
50
  if (!srcDir)
43
51
  return;
44
52
  console.log(`🔍 Scanning source code and dependencies: ${srcDir}...`);
45
- const usedKeyMap = extractUsedKeys(srcDir);
53
+ const usedKeyMap = (0, key_extractor_1.extractUsedKeys)(srcDir);
46
54
  console.log(` Found ${usedKeyMap.size} unique keys.`);
47
- const oldEnUS = parseTypeScriptFile(enUSPath);
55
+ const oldEnUS = (0, file_parser_1.parseTypeScriptFile)(enUSPath);
48
56
  const newEnUS = {};
49
57
  let addedCount = 0;
50
58
  for (const [key, defaultValue] of usedKeyMap) {
@@ -58,34 +66,34 @@ function processExtraction(srcDir, enUSPath) {
58
66
  // We treat it as "not translated" if the value is exactly the key string
59
67
  const isActuallyTranslated = typeof existingValue === 'string' && existingValue !== key;
60
68
  const valueToSet = isActuallyTranslated ? existingValue : defaultValue;
61
- if (setDeep(newEnUS, key, valueToSet)) {
69
+ if ((0, object_helper_1.setDeep)(newEnUS, key, valueToSet)) {
62
70
  if (!isActuallyTranslated)
63
71
  addedCount++;
64
72
  }
65
73
  }
66
- const oldTotal = countKeys(oldEnUS);
67
- const newTotal = countKeys(newEnUS);
74
+ const oldTotal = (0, object_helper_1.countKeys)(oldEnUS);
75
+ const newTotal = (0, object_helper_1.countKeys)(newEnUS);
68
76
  const removedCount = Math.max(0, oldTotal - (newTotal - addedCount));
69
77
  console.log(` ✨ Optimized en-US.ts: ${addedCount} keys populated/updated, pruned ${removedCount} unused.`);
70
- const content = generateTypeScriptContent(newEnUS, 'en-US');
71
- fs.writeFileSync(enUSPath, content);
78
+ const content = (0, file_parser_1.generateTypeScriptContent)(newEnUS, 'en-US');
79
+ fs_1.default.writeFileSync(enUSPath, content);
72
80
  }
73
- export function syncTranslations(options) {
81
+ function syncTranslations(options) {
74
82
  const { targetDir, srcDir } = options;
75
- const localesDir = path.resolve(process.cwd(), targetDir);
76
- const enUSPath = path.join(localesDir, 'en-US.ts');
77
- if (!fs.existsSync(localesDir) || !fs.existsSync(enUSPath)) {
83
+ const localesDir = path_1.default.resolve(process.cwd(), targetDir);
84
+ const enUSPath = path_1.default.join(localesDir, 'en-US.ts');
85
+ if (!fs_1.default.existsSync(localesDir) || !fs_1.default.existsSync(enUSPath)) {
78
86
  console.error(`❌ Localization files not found in: ${localesDir}`);
79
87
  return false;
80
88
  }
81
89
  processExtraction(srcDir, enUSPath);
82
- const files = fs.readdirSync(localesDir)
90
+ const files = fs_1.default.readdirSync(localesDir)
83
91
  .filter(f => f.match(/^[a-z]{2}-[A-Z]{2}\.ts$/) && f !== 'en-US.ts')
84
92
  .sort();
85
93
  console.log(`📊 Languages to sync: ${files.length}\n`);
86
94
  files.forEach(file => {
87
95
  const langCode = file.replace('.ts', '');
88
- const targetPath = path.join(localesDir, file);
96
+ const targetPath = path_1.default.join(localesDir, file);
89
97
  const result = syncLanguageFile(enUSPath, targetPath, langCode);
90
98
  if (result.changed) {
91
99
  console.log(` 🌍 ${langCode}: ✏️ +${result.added || 0} keys, -${result.removed || 0} keys`);
@@ -95,12 +103,13 @@ export function syncTranslations(options) {
95
103
  return true;
96
104
  }
97
105
  // CLI interface
98
- export function runSyncTranslations() {
106
+ function runSyncTranslations() {
99
107
  const targetDir = process.argv[2] || 'src/infrastructure/locales';
100
108
  const srcDir = process.argv[3];
101
109
  console.log('🚀 Starting translation synchronization...\n');
102
110
  syncTranslations({ targetDir, srcDir });
103
111
  }
104
- if (import.meta.url === `file://${process.argv[1]}`) {
112
+ // Check if this file is being run directly
113
+ if (require.main === module || process.argv[1].endsWith('/sync.js') || process.argv[1].endsWith('\\sync.js')) {
105
114
  runSyncTranslations();
106
115
  }
@@ -1,45 +1,52 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
  /**
3
4
  * Translate Missing Script
4
5
  * Automatically translates missing strings using Google Translate
5
6
  */
6
- import fs from 'fs';
7
- import path from 'path';
8
- import { parseTypeScriptFile, generateTypeScriptContent, } from './utils/file-parser';
9
- import { googleTranslateService, getTargetLanguage, getLanguageDisplayName } from '../infrastructure/services';
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.translateMissing = translateMissing;
12
+ exports.runTranslateMissing = runTranslateMissing;
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const file_parser_1 = require("./utils/file-parser");
16
+ const services_1 = require("../infrastructure/services");
10
17
  // Width of terminal line to clear for progress updates
11
18
  const PROGRESS_LINE_WIDTH = 80;
12
- export async function translateMissing(options) {
19
+ async function translateMissing(options) {
13
20
  const { targetDir, srcDir, skipSync = false } = options;
14
21
  // Initialize the translation service
15
- googleTranslateService.initialize({
22
+ services_1.googleTranslateService.initialize({
16
23
  minDelay: 100,
17
24
  maxRetries: 3,
18
25
  timeout: 10000,
19
26
  });
20
- const localesDir = path.resolve(process.cwd(), targetDir);
21
- const enUSPath = path.join(localesDir, 'en-US.ts');
22
- if (!fs.existsSync(localesDir) || !fs.existsSync(enUSPath)) {
27
+ const localesDir = path_1.default.resolve(process.cwd(), targetDir);
28
+ const enUSPath = path_1.default.join(localesDir, 'en-US.ts');
29
+ if (!fs_1.default.existsSync(localesDir) || !fs_1.default.existsSync(enUSPath)) {
23
30
  console.error(`❌ Localization files not found in: ${localesDir}`);
24
31
  return;
25
32
  }
26
- const files = fs.readdirSync(localesDir)
33
+ const files = fs_1.default.readdirSync(localesDir)
27
34
  .filter(f => f.match(/^[a-z]{2}-[A-Z]{2}\.ts$/) && f !== 'en-US.ts')
28
35
  .sort();
29
36
  console.log(`\n📊 Languages to translate: ${files.length}\n`);
30
- const enUS = parseTypeScriptFile(enUSPath);
37
+ const enUS = (0, file_parser_1.parseTypeScriptFile)(enUSPath);
31
38
  for (const file of files) {
32
39
  const langCode = file.replace('.ts', '');
33
40
  // Skip English variants
34
- const targetLang = getTargetLanguage(langCode);
41
+ const targetLang = (0, services_1.getTargetLanguage)(langCode);
35
42
  if (!targetLang || targetLang === 'en') {
36
43
  console.log(`⏭️ Skipping ${langCode} (English variant)`);
37
44
  continue;
38
45
  }
39
- const langName = getLanguageDisplayName(langCode);
46
+ const langName = (0, services_1.getLanguageDisplayName)(langCode);
40
47
  console.log(`🌍 Translating ${langCode} (${langName})...`);
41
- const targetPath = path.join(localesDir, file);
42
- const target = parseTypeScriptFile(targetPath);
48
+ const targetPath = path_1.default.join(localesDir, file);
49
+ const target = (0, file_parser_1.parseTypeScriptFile)(targetPath);
43
50
  const stats = {
44
51
  totalCount: 0,
45
52
  successCount: 0,
@@ -47,12 +54,12 @@ export async function translateMissing(options) {
47
54
  skippedCount: 0,
48
55
  translatedKeys: [],
49
56
  };
50
- await googleTranslateService.translateObject(enUS, target, targetLang, '', stats);
57
+ await services_1.googleTranslateService.translateObject(enUS, target, targetLang, '', stats);
51
58
  // Clear progress line
52
59
  process.stdout.write('\r' + ' '.repeat(PROGRESS_LINE_WIDTH) + '\r');
53
60
  if (stats.successCount > 0) {
54
- const content = generateTypeScriptContent(target, langCode);
55
- fs.writeFileSync(targetPath, content);
61
+ const content = (0, file_parser_1.generateTypeScriptContent)(target, langCode);
62
+ fs_1.default.writeFileSync(targetPath, content);
56
63
  console.log(` ✅ Successfully translated ${stats.successCount} keys:`);
57
64
  // Detailed logging of translated keys
58
65
  const displayCount = Math.min(stats.translatedKeys.length, 15);
@@ -70,7 +77,7 @@ export async function translateMissing(options) {
70
77
  console.log('\n✅ All translations completed!');
71
78
  }
72
79
  // CLI interface
73
- export function runTranslateMissing() {
80
+ function runTranslateMissing() {
74
81
  const args = process.argv.slice(2).filter(arg => !arg.startsWith('--'));
75
82
  const targetDir = args[0] || 'src/infrastructure/locales';
76
83
  const srcDir = args[1];
@@ -81,6 +88,7 @@ export function runTranslateMissing() {
81
88
  process.exit(1);
82
89
  });
83
90
  }
84
- if (import.meta.url === `file://${process.argv[1]}`) {
91
+ // Check if this file is being run directly
92
+ if (require.main === module || process.argv[1].endsWith('/translate.js') || process.argv[1].endsWith('\\translate.js')) {
85
93
  runTranslateMissing();
86
94
  }
@@ -1,12 +1,20 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getLanguageDisplayName } from '../../infrastructure/utils/textValidator.util';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseTypeScriptFile = parseTypeScriptFile;
7
+ exports.stringifyValue = stringifyValue;
8
+ exports.generateTypeScriptContent = generateTypeScriptContent;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const textValidator_util_1 = require("../../infrastructure/utils/textValidator.util");
4
12
  /**
5
13
  * File Parser
6
14
  * Parse and generate TypeScript translation files
7
15
  */
8
- export function parseTypeScriptFile(filePath) {
9
- const content = fs.readFileSync(filePath, 'utf8');
16
+ function parseTypeScriptFile(filePath) {
17
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
10
18
  // Match: export default { ... } OR export const NAME = { ... }
11
19
  const match = content.match(/export\s+(?:default|const\s+\w+\s*=)\s*(\{[\s\S]*\});?\s*$/);
12
20
  if (!match) {
@@ -21,13 +29,13 @@ export function parseTypeScriptFile(filePath) {
21
29
  }
22
30
  catch (error) {
23
31
  // File might be a barrel file with named imports
24
- const dir = path.dirname(filePath);
32
+ const dir = path_1.default.dirname(filePath);
25
33
  const importMatches = [...content.matchAll(/import\s*\{\s*(\w+)\s*\}\s*from\s*["']\.\/(\w+)["']/g)];
26
34
  if (importMatches.length > 0) {
27
35
  const result = {};
28
36
  for (const [, varName, moduleName] of importMatches) {
29
- const subFilePath = path.join(dir, `${moduleName}.ts`);
30
- if (fs.existsSync(subFilePath)) {
37
+ const subFilePath = path_1.default.join(dir, `${moduleName}.ts`);
38
+ if (fs_1.default.existsSync(subFilePath)) {
31
39
  try {
32
40
  result[varName] = parseTypeScriptFile(subFilePath);
33
41
  }
@@ -44,7 +52,7 @@ export function parseTypeScriptFile(filePath) {
44
52
  }
45
53
  return {};
46
54
  }
47
- export function stringifyValue(value, indent = 2) {
55
+ function stringifyValue(value, indent = 2) {
48
56
  if (typeof value === 'string') {
49
57
  const escaped = value
50
58
  .replace(/\\/g, '\\\\')
@@ -76,8 +84,8 @@ export function stringifyValue(value, indent = 2) {
76
84
  }
77
85
  return String(value);
78
86
  }
79
- export function generateTypeScriptContent(obj, langCode) {
80
- const langName = getLanguageDisplayName(langCode);
87
+ function generateTypeScriptContent(obj, langCode) {
88
+ const langName = (0, textValidator_util_1.getLanguageDisplayName)(langCode);
81
89
  const isBase = langCode === 'en-US';
82
90
  const objString = stringifyValue(obj, 0);
83
91
  return `/**
@@ -1,9 +1,25 @@
1
+ "use strict";
1
2
  /**
2
3
  * Scripts Utils
3
4
  * Utility functions for translation scripts
4
5
  */
5
- export * from './file-parser';
6
- export * from './key-detector';
7
- export * from './key-extractor';
8
- export * from './object-helper';
9
- export * from './sync-helper';
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./file-parser"), exports);
22
+ __exportStar(require("./key-detector"), exports);
23
+ __exportStar(require("./key-extractor"), exports);
24
+ __exportStar(require("./object-helper"), exports);
25
+ __exportStar(require("./sync-helper"), exports);
@@ -1,8 +1,12 @@
1
+ "use strict";
1
2
  /**
2
3
  * Key Detector
3
4
  * Detects new, missing, and removed keys between source and target objects
4
5
  */
5
- export function detectNewKeys(sourceObj, targetObj, path = '', newKeys = []) {
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.detectNewKeys = detectNewKeys;
8
+ exports.detectMissingKeys = detectMissingKeys;
9
+ function detectNewKeys(sourceObj, targetObj, path = '', newKeys = []) {
6
10
  for (const key in sourceObj) {
7
11
  const currentPath = path ? `${path}.${key}` : key;
8
12
  const sourceValue = sourceObj[key];
@@ -20,7 +24,7 @@ export function detectNewKeys(sourceObj, targetObj, path = '', newKeys = []) {
20
24
  }
21
25
  return newKeys;
22
26
  }
23
- export function detectMissingKeys(sourceObj, targetObj, path = '', missingKeys = []) {
27
+ function detectMissingKeys(sourceObj, targetObj, path = '', missingKeys = []) {
24
28
  for (const key in targetObj) {
25
29
  const currentPath = path ? `${path}.${key}` : key;
26
30
  if (!Object.prototype.hasOwnProperty.call(sourceObj, key)) {
@@ -1,5 +1,11 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractUsedKeys = extractUsedKeys;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
3
9
  /**
4
10
  * Generic Key Extractor
5
11
  * Scans source code for i18n translation keys
@@ -61,34 +67,34 @@ function extractFromFile(content, keyMap) {
61
67
  }
62
68
  }
63
69
  function walkDirectory(dir, keyMap, skipDirs = ['node_modules', '.expo', '.git', 'build', 'ios', 'android', 'assets', 'locales', '__tests__']) {
64
- if (!fs.existsSync(dir))
70
+ if (!fs_1.default.existsSync(dir))
65
71
  return;
66
- const files = fs.readdirSync(dir);
72
+ const files = fs_1.default.readdirSync(dir);
67
73
  for (const file of files) {
68
- const fullPath = path.join(dir, file);
69
- const stat = fs.statSync(fullPath);
74
+ const fullPath = path_1.default.join(dir, file);
75
+ const stat = fs_1.default.statSync(fullPath);
70
76
  if (stat.isDirectory()) {
71
77
  if (!skipDirs.includes(file)) {
72
78
  walkDirectory(fullPath, keyMap, skipDirs);
73
79
  }
74
80
  }
75
81
  else if (/\.(ts|tsx|js|jsx)$/.test(file)) {
76
- const content = fs.readFileSync(fullPath, 'utf8');
82
+ const content = fs_1.default.readFileSync(fullPath, 'utf8');
77
83
  extractFromFile(content, keyMap);
78
84
  }
79
85
  }
80
86
  }
81
- export function extractUsedKeys(srcDir) {
87
+ function extractUsedKeys(srcDir) {
82
88
  const keyMap = new Map();
83
89
  if (!srcDir)
84
90
  return keyMap;
85
91
  const projectRoot = process.cwd();
86
- const absoluteSrcDir = path.resolve(projectRoot, srcDir);
92
+ const absoluteSrcDir = path_1.default.resolve(projectRoot, srcDir);
87
93
  // Scan project source
88
94
  walkDirectory(absoluteSrcDir, keyMap);
89
95
  // Scan @umituz packages for shared keys
90
- const packagesDir = path.resolve(projectRoot, 'node_modules/@umituz');
91
- if (fs.existsSync(packagesDir)) {
96
+ const packagesDir = path_1.default.resolve(projectRoot, 'node_modules/@umituz');
97
+ if (fs_1.default.existsSync(packagesDir)) {
92
98
  walkDirectory(packagesDir, keyMap);
93
99
  }
94
100
  return keyMap;
@@ -1,12 +1,16 @@
1
+ "use strict";
1
2
  /**
2
3
  * Object Helper
3
4
  * Utilities for deep object manipulation
4
5
  */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.setDeep = setDeep;
8
+ exports.countKeys = countKeys;
5
9
  /**
6
10
  * Set a value in a nested object, creating intermediate objects if necessary
7
11
  * Returns true if the key was newly added, false if it already existed
8
12
  */
9
- export function setDeep(obj, path, value) {
13
+ function setDeep(obj, path, value) {
10
14
  const keys = path.split('.');
11
15
  let current = obj;
12
16
  for (let i = 0; i < keys.length - 1; i++) {
@@ -26,7 +30,7 @@ export function setDeep(obj, path, value) {
26
30
  /**
27
31
  * Count all leaf keys in a nested object
28
32
  */
29
- export function countKeys(obj) {
33
+ function countKeys(obj) {
30
34
  let count = 0;
31
35
  const walk = (o) => {
32
36
  for (const k in o) {
@@ -1,8 +1,12 @@
1
+ "use strict";
1
2
  /**
2
3
  * Sync Helper
3
4
  * Helper functions for synchronizing translation keys
4
5
  */
5
- export function addMissingKeys(sourceObj, targetObj, stats = {}) {
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.addMissingKeys = addMissingKeys;
8
+ exports.removeExtraKeys = removeExtraKeys;
9
+ function addMissingKeys(sourceObj, targetObj, stats = {}) {
6
10
  stats.added = stats.added || 0;
7
11
  stats.newKeys = stats.newKeys || [];
8
12
  for (const key in sourceObj) {
@@ -24,7 +28,7 @@ export function addMissingKeys(sourceObj, targetObj, stats = {}) {
24
28
  }
25
29
  return stats;
26
30
  }
27
- export function removeExtraKeys(sourceObj, targetObj, stats = {}) {
31
+ function removeExtraKeys(sourceObj, targetObj, stats = {}) {
28
32
  stats.removed = stats.removed || 0;
29
33
  stats.removedKeys = stats.removedKeys || [];
30
34
  for (const key in targetObj) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-google-translate",
3
- "version": "1.0.5",
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 (const request of requests) {
129
- const result = await this.translate(request);
130
+ for (let i = 0; i < requests.length; i += batchSize) {
131
+ const batch = requests.slice(i, i + batchSize);
130
132
 
131
- if (result.success) {
132
- if (result.translatedText === result.originalText) {
133
- stats.skippedCount++;
134
- } else {
135
- stats.successCount++;
136
- stats.translatedKeys.push({
137
- key: request.text,
138
- from: result.originalText,
139
- to: result.translatedText,
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
- const request: TranslationRequest = {
201
- text: enValue,
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
- const result = await this.translate(request);
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 (result.success && result.translatedText !== enValue) {
208
- targetObject[key] = result.translatedText;
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: result.translatedText,
265
+ to: translatedKey.to,
214
266
  });
215
- } else if (!result.success) {
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();