gptrans 1.6.6 → 1.6.8

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 CHANGED
@@ -27,6 +27,7 @@ GPTrans uses dotenv for environment configuration. Create a `.env` file in your
27
27
  ```env
28
28
  OPENAI_API_KEY=your_openai_api_key
29
29
  ANTHROPIC_API_KEY=your_anthropic_api_key
30
+ GEMINI_API_KEY=for_image_translations
30
31
  ```
31
32
 
32
33
  ## šŸš€ Quick Start
@@ -138,6 +139,33 @@ When using multiple models:
138
139
  - If the primary model fails (due to API errors, rate limits, etc.), GPTrans automatically falls back to the next model
139
140
  - This ensures higher availability and resilience of your translation service
140
141
 
142
+ # šŸ–¼ļø Image Translation
143
+
144
+ Intelligent image translation using Google's Gemini AI.
145
+
146
+ ## ✨ Smart Path Detection
147
+
148
+ The translation system automatically detects if your image is already in a language folder and adjusts the output path accordingly:
149
+
150
+ ### Example
151
+
152
+ ```javascript
153
+ // Input: en/image.jpg
154
+ // Output: es/image.jpg (sibling folder at same level)
155
+
156
+ const result = await gptrans.img('en/image.jpg');
157
+ ```
158
+
159
+ **Directory Structure:**
160
+ ```
161
+ project/
162
+ ā”œā”€ā”€ en/
163
+ │ └── image.jpg ← Original
164
+ └── es/
165
+ └── image.jpg ← Translation (sibling folder)
166
+ ```
167
+
168
+
141
169
  ## Contributing
142
170
 
143
171
  Contributions are welcome! Please open an issue or submit a pull request on GitHub to contribute improvements or fixes.
Binary file
Binary file
@@ -0,0 +1,84 @@
1
+ // Example usage based on user's requirements
2
+ import GPTrans from '../index.js';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ async function main() {
11
+ try {
12
+ // Initialize GPTrans with Spanish as target
13
+ const gptrans = new GPTrans({
14
+ from: 'en-US',
15
+ target: 'es'
16
+ });
17
+
18
+ console.log('šŸŒ Testing Image Translation with Smart Path Detection\n');
19
+ console.log('═══════════════════════════════════════════════════\n');
20
+
21
+ // Example 1: Image in language folder (en/image.jpg -> es/image.jpg)
22
+ console.log('šŸ“ Example 1: Image already in language folder');
23
+ console.log(' Input: en/image.jpg');
24
+ console.log(' Expected output: es/image.jpg (sibling folder)\n');
25
+
26
+ const imageInLangFolder = path.join(__dirname, 'camera_4126.jpg');
27
+ if (fs.existsSync(imageInLangFolder)) {
28
+ const result1 = await gptrans.img(imageInLangFolder);
29
+ console.log(' Result:', result1);
30
+ console.log('');
31
+ } else {
32
+ console.log(' āš ļø Image not found at:', imageInLangFolder);
33
+ console.log(' (Create en/camera_4126.jpg to test this case)\n');
34
+ }
35
+
36
+ // // Example 2: Image in root folder (./image.jpg -> es/image.jpg)
37
+ // console.log('šŸ“ Example 2: Image in root/current folder');
38
+ // console.log(' Input: ./image.jpg');
39
+ // console.log(' Expected output: ./es/image.jpg (subfolder)\n');
40
+
41
+ // const imageInRoot = path.join(__dirname, 'camera_4126.jpg');
42
+ // if (fs.existsSync(imageInRoot)) {
43
+ // const result2 = await gptrans.img(imageInRoot);
44
+ // console.log(' Result:', result2);
45
+ // console.log('');
46
+ // } else {
47
+ // console.log(' āš ļø Image not found at:', imageInRoot);
48
+ // console.log(' (Create camera_4126.jpg to test this case)\n');
49
+ // }
50
+
51
+ // // Example 3: Image in non-language folder (images/photo.jpg -> images/es/photo.jpg)
52
+ // console.log('šŸ“ Example 3: Image in custom folder');
53
+ // console.log(' Input: images/photo.jpg');
54
+ // console.log(' Expected output: images/es/photo.jpg (subfolder)\n');
55
+
56
+ // const imageInCustomFolder = path.join(__dirname, 'images', 'photo.jpg');
57
+ // if (fs.existsSync(imageInCustomFolder)) {
58
+ // const result3 = await gptrans.img(imageInCustomFolder);
59
+ // console.log(' Result:', result3);
60
+ // console.log('');
61
+ // } else {
62
+ // console.log(' āš ļø Image not found at:', imageInCustomFolder);
63
+ // console.log(' (Create images/photo.jpg to test this case)\n');
64
+ // }
65
+
66
+ // console.log('═══════════════════════════════════════════════════');
67
+ // console.log('šŸŽ‰ Example completed successfully!\n');
68
+
69
+ } catch (error) {
70
+ console.error('āŒ Error:', error.message);
71
+
72
+ if (error.message.includes('API Key')) {
73
+ console.error('\nšŸ’” Tip: Make sure you have GEMINI_API_KEY in your .env file');
74
+ console.error('Get your API key from: https://aistudio.google.com/apikey');
75
+ }
76
+
77
+ if (error.message.includes('reference image')) {
78
+ console.error('\nšŸ’” Tip: Verify that the image path is correct');
79
+ }
80
+ }
81
+ }
82
+
83
+ main();
84
+
package/index.js CHANGED
@@ -3,6 +3,9 @@ import stringHash from 'string-hash';
3
3
  import { ModelMix } from 'modelmix';
4
4
  import { isoAssoc, isLanguageAvailable } from './isoAssoc.js';
5
5
  import dotenv from 'dotenv';
6
+ import { GeminiGenerator } from 'genmix';
7
+ import fs from 'fs';
8
+ import path from 'path';
6
9
 
7
10
  class GPTrans {
8
11
  static #mmixInstances = new Map();
@@ -255,6 +258,97 @@ class GPTrans {
255
258
  this.freeze = freeze;
256
259
  return this;
257
260
  }
261
+
262
+ async img(imagePath, options = {}) {
263
+ const {
264
+ quality = '1K',
265
+ numberOfImages = 1,
266
+ prompt = null
267
+ } = options;
268
+
269
+ // Parse image filename and extension
270
+ const parsedPath = path.parse(imagePath);
271
+ const filename = parsedPath.base;
272
+ const targetLang = this.replaceTarget.TARGET_ISO || 'en';
273
+
274
+ // Check if image is already in a language folder
275
+ const dirName = path.basename(path.dirname(imagePath));
276
+ const parentDir = path.dirname(path.dirname(imagePath));
277
+
278
+ // If the image is in a language folder (e.g., en/image.jpg)
279
+ // create the target at the same level (e.g., es/image.jpg)
280
+ // Otherwise, create it as a subfolder (e.g., ./image.jpg -> es/image.jpg)
281
+ let targetDir, targetPath;
282
+
283
+ if (this._isLanguageFolder(dirName)) {
284
+ // Image is in a language folder: create sibling folder
285
+ targetDir = path.join(parentDir, targetLang);
286
+ targetPath = path.join(targetDir, filename);
287
+ } else {
288
+ // Image is not in a language folder: create subfolder
289
+ targetDir = path.join(path.dirname(imagePath), targetLang);
290
+ targetPath = path.join(targetDir, filename);
291
+ }
292
+
293
+ // Check if translated image already exists
294
+ if (fs.existsSync(targetPath)) {
295
+ return targetPath;
296
+ }
297
+
298
+ // Generate translated image and wait for completion
299
+ await this._generateTranslatedImage(imagePath, targetPath, targetDir, prompt, quality, numberOfImages);
300
+
301
+ // Return path of translated image
302
+ return targetPath;
303
+ }
304
+
305
+ _isLanguageFolder(folderName) {
306
+ // Check if folder name is a valid language code
307
+ // Common formats: en, es, pt, en-US, pt-BR, es-AR, etc.
308
+ const languageCodePattern = /^[a-z]{2}(-[A-Z]{2})?$/;
309
+ return languageCodePattern.test(folderName);
310
+ }
311
+
312
+ async _generateTranslatedImage(imagePath, targetPath, targetDir, customPrompt, quality, numberOfImages) {
313
+ try {
314
+ // Initialize GeminiGenerator
315
+ const generator = new GeminiGenerator();
316
+
317
+ // Generate translation prompt
318
+ const translationPrompt = customPrompt ||
319
+ `Translate all text in this image to ${this.replaceTarget.TARGET_DENONYM} ${this.replaceTarget.TARGET_LANG}. Maintain the exact same layout, style, colors, and design. Only change the text content.`;
320
+
321
+ // Generate translated image
322
+ const result = await generator.generate(translationPrompt, {
323
+ referenceImage: imagePath,
324
+ quality: quality,
325
+ numberOfImages: numberOfImages
326
+ });
327
+
328
+ if (result.images && result.images.length > 0) {
329
+ // Create target directory if it doesn't exist
330
+ if (!fs.existsSync(targetDir)) {
331
+ fs.mkdirSync(targetDir, { recursive: true });
332
+ }
333
+
334
+ // Save translated image
335
+ const originalName = path.basename(targetPath, path.extname(targetPath));
336
+ await generator.save({
337
+ directory: targetDir,
338
+ filename: originalName
339
+ });
340
+
341
+ console.log(`āœ… Translated image saved: ${targetPath}`);
342
+ } else {
343
+ console.error('No translated image was generated');
344
+ }
345
+ } catch (error) {
346
+ if (error.message.includes('API Key')) {
347
+ console.error('āŒ GEMINI_API_KEY not found in .env file');
348
+ }
349
+ throw error;
350
+ }
351
+ }
258
352
  }
259
353
 
260
354
  export default GPTrans;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gptrans",
3
3
  "type": "module",
4
- "version": "1.6.6",
4
+ "version": "1.6.8",
5
5
  "description": "šŸš† GPTrans - The smarter AI-powered way to translate.",
6
6
  "keywords": [
7
7
  "translate",
@@ -36,6 +36,7 @@
36
36
  "deepbase": "^1.4.8",
37
37
  "dotenv": "^16.4.7",
38
38
  "form-data": "^4.0.4",
39
+ "genmix": "^1.0.0",
39
40
  "modelmix": "^3.9.0",
40
41
  "string-hash": "^1.1.3"
41
42
  }
@@ -0,0 +1,70 @@
1
+ import GPTrans from './index.js';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ async function testImageTranslation() {
10
+ try {
11
+ console.log('šŸš€ Starting Image Translation Test\n');
12
+
13
+ // Initialize GPTrans with Portuguese as target language
14
+ const gptrans = new GPTrans({
15
+ from: 'en-US',
16
+ target: 'pt-BR',
17
+ model: 'sonnet45'
18
+ });
19
+
20
+ // Test image path (you'll need to provide an actual image)
21
+ const testImagePath = path.join(__dirname, 'test-image.jpg');
22
+
23
+ // Check if test image exists
24
+ if (!fs.existsSync(testImagePath)) {
25
+ console.log('āš ļø Test image not found. Please create a test image at:', testImagePath);
26
+ console.log('You can use any image with text in it for testing.\n');
27
+ return;
28
+ }
29
+
30
+ console.log('šŸ“ø Test image found:', testImagePath);
31
+ console.log('šŸŽÆ Target language: Portuguese (pt-BR)\n');
32
+
33
+ // First call - should return original image and start generation
34
+ console.log('šŸ”„ First call (should return original and start generation)...');
35
+ const result1 = await gptrans.translateImage(testImagePath);
36
+ console.log('Result 1:', result1);
37
+ console.log('');
38
+
39
+ // Wait a moment
40
+ console.log('ā³ Waiting 5 seconds before second call...\n');
41
+ await new Promise(resolve => setTimeout(resolve, 5000));
42
+
43
+ // Second call - check status
44
+ console.log('šŸ”„ Second call (checking if translation is ready)...');
45
+ const result2 = await gptrans.translateImage(testImagePath);
46
+ console.log('Result 2:', result2);
47
+ console.log('');
48
+
49
+ if (result2.isTranslated) {
50
+ console.log('āœ… Translation completed! Translated image at:', result2.path);
51
+ } else {
52
+ console.log('ā³ Translation still generating... Check again later.');
53
+ console.log('Expected path:', path.join(path.dirname(testImagePath), 'pt-BR', path.basename(testImagePath)));
54
+ }
55
+
56
+ console.log('\nšŸŽ‰ Test completed!\n');
57
+
58
+ } catch (error) {
59
+ console.error('āŒ Error:', error.message);
60
+
61
+ if (error.message.includes('API Key') || error.message.includes('GEMINI_API_KEY')) {
62
+ console.error('\nšŸ’” Tip: Make sure you have GEMINI_API_KEY in your .env file');
63
+ console.error('You can get a free API key from: https://aistudio.google.com/apikey');
64
+ }
65
+ }
66
+ }
67
+
68
+ // Run the test
69
+ testImageTranslation();
70
+