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 +28 -0
- package/demo/camera_4126.jpg +0 -0
- package/demo/es/camera_4126.jpg +0 -0
- package/demo/example-image-translation.js +84 -0
- package/index.js +94 -0
- package/package.json +2 -1
- package/test-image-translation.js +70 -0
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.
|
|
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
|
+
|