gptrans 2.0.0 ā 2.0.4
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 +27 -0
- package/index.js +17 -15
- package/package.json +1 -1
- package/prompt/translate.md +2 -1
- package/skills/gptrans/SKILL.md +13 -0
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Whether you're building a multilingual website, a mobile app, or a localization
|
|
|
15
15
|
- **Caching with JSON:** Quickly retrieves cached translations to boost performance
|
|
16
16
|
- **Parameter Substitution:** Dynamically replace placeholders in your translations
|
|
17
17
|
- **Smart Context Handling:** Add contextual information to improve translation accuracy. Perfect for gender-aware translations, domain-specific content, or any scenario where additional context helps produce better results. The context is automatically cleared after each translation to prevent unintended effects.
|
|
18
|
+
- **Translation Instructions:** Pass additional instructions (e.g., "Use natural tone") to guide the AI translator's style.
|
|
18
19
|
|
|
19
20
|
## š¦ Installation
|
|
20
21
|
|
|
@@ -76,6 +77,7 @@ When creating a new instance of GPTrans, you can customize:
|
|
|
76
77
|
| `model` | Translation model key or array of models for fallback | `sonnet45` `gpt41` |
|
|
77
78
|
| `batchThreshold` | Maximum number of characters to accumulate before triggering batch processing | `1500` |
|
|
78
79
|
| `debounceTimeout` | Time in milliseconds to wait before processing translations | `500` |
|
|
80
|
+
| `instruction` | Additional instruction for the translator (e.g., tone, style). Does not affect the cache key | `''` |
|
|
79
81
|
| `freeze` | Freeze mode to prevent translations from being queued | `false` |
|
|
80
82
|
|
|
81
83
|
### BCP 47 Language Tags
|
|
@@ -120,6 +122,31 @@ GPTrans stands out by combining advanced AI capabilities with efficient batching
|
|
|
120
122
|
|
|
121
123
|
If you're looking to streamline your translation workflow and bring your applications to a global audience effortlessly, GPTrans is the perfect choice!
|
|
122
124
|
|
|
125
|
+
## š Translation Instructions
|
|
126
|
+
|
|
127
|
+
The `instruction` option lets you guide the AI translator's style, tone, or behavior without creating a separate cache entry. Unlike `context` (which produces separate translations for each unique context), `instruction` does not affect the cache key ā so the same text with the same context always maps to the same cache entry regardless of the instruction used.
|
|
128
|
+
|
|
129
|
+
### Usage
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
const gptrans = new GPTrans({
|
|
133
|
+
from: 'en',
|
|
134
|
+
target: 'es-AR',
|
|
135
|
+
instruction: 'Use natural and colloquial tone'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log(gptrans.t('Welcome to our platform'));
|
|
139
|
+
console.log(gptrans.t('Please verify your identity'));
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Instruction vs Context
|
|
143
|
+
|
|
144
|
+
| | `context` | `instruction` |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| **Purpose** | Semantic information (gender, domain) | Style guidance (tone, formality) |
|
|
147
|
+
| **Affects cache key** | Yes ā different contexts create separate translations | No ā same key regardless of instruction |
|
|
148
|
+
| **Example** | `'Use natural tone'` |
|
|
149
|
+
|
|
123
150
|
## š Preloading Translations
|
|
124
151
|
|
|
125
152
|
The `preload()` method allows you to pre-translate all texts in your database. It now supports advanced options for improved translation accuracy through reference translations and alternate base languages.
|
package/index.js
CHANGED
|
@@ -64,7 +64,7 @@ class GPTrans {
|
|
|
64
64
|
return isLanguageAvailable(langCode);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
constructor({ from = 'en-US', target = 'es', model = 'sonnet45', batchThreshold = 1500, debounceTimeout = 500, promptFile = null, name = '', context = '', freeze = false, debug = false } = {}) {
|
|
67
|
+
constructor({ from = 'en-US', target = 'es', model = 'sonnet45', batchThreshold = 1500, debounceTimeout = 500, promptFile = null, name = '', context = '', instruction = '', freeze = false, debug = false } = {}) {
|
|
68
68
|
|
|
69
69
|
target = this.normalizeBCP47(target);
|
|
70
70
|
from = this.normalizeBCP47(from);
|
|
@@ -99,6 +99,7 @@ class GPTrans {
|
|
|
99
99
|
this.modelKey = model;
|
|
100
100
|
this.promptFile = promptFile ?? new URL('./prompt/translate.md', import.meta.url).pathname;
|
|
101
101
|
this.context = context;
|
|
102
|
+
this.instruction = instruction;
|
|
102
103
|
this.freeze = freeze;
|
|
103
104
|
this.modelConfig = {
|
|
104
105
|
options: {
|
|
@@ -215,15 +216,15 @@ class GPTrans {
|
|
|
215
216
|
const textsToTranslate = batch.map(([_, text]) => text).join(`\n${this.divider}\n`);
|
|
216
217
|
try {
|
|
217
218
|
const translations = await this._translate(textsToTranslate, batch, batchReferences, this.preloadBaseLanguage);
|
|
218
|
-
|
|
219
|
+
|
|
219
220
|
// Try different split strategies to be more robust
|
|
220
221
|
let translatedTexts = translations.split(`\n${this.divider}\n`);
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
// If split doesn't match batch size, try alternative separators
|
|
223
224
|
if (translatedTexts.length !== batch.length) {
|
|
224
225
|
// Try without newlines around divider
|
|
225
226
|
translatedTexts = translations.split(this.divider);
|
|
226
|
-
|
|
227
|
+
|
|
227
228
|
// If still doesn't match, try with just newline
|
|
228
229
|
if (translatedTexts.length !== batch.length) {
|
|
229
230
|
translatedTexts = translations.split(/\n{2,}/); // Split by multiple newlines
|
|
@@ -231,7 +232,7 @@ class GPTrans {
|
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
const contextHash = this._hash(context);
|
|
234
|
-
|
|
235
|
+
|
|
235
236
|
// Validate we have the right number of translations
|
|
236
237
|
if (translatedTexts.length !== batch.length) {
|
|
237
238
|
console.error(`ā Translation count mismatch:`);
|
|
@@ -239,7 +240,7 @@ class GPTrans {
|
|
|
239
240
|
console.error(` Received: ${translatedTexts.length} translations`);
|
|
240
241
|
console.error(` Batch keys: ${batch.map(([key]) => key).join(', ')}`);
|
|
241
242
|
console.error(`\nš Full response from model:\n${translations}\n`);
|
|
242
|
-
|
|
243
|
+
|
|
243
244
|
// Try to save what we can
|
|
244
245
|
const minLength = Math.min(translatedTexts.length, batch.length);
|
|
245
246
|
for (let i = 0; i < minLength; i++) {
|
|
@@ -322,6 +323,7 @@ class GPTrans {
|
|
|
322
323
|
model.replace({
|
|
323
324
|
'{INPUT}': text,
|
|
324
325
|
'{CONTEXT}': this.context,
|
|
326
|
+
'{INSTRUCTION}': this.instruction,
|
|
325
327
|
'{REFERENCES}': referencesText,
|
|
326
328
|
'{TARGET_ISO}': this.replaceTarget.TARGET_ISO,
|
|
327
329
|
'{TARGET_LANG}': this.replaceTarget.TARGET_LANG,
|
|
@@ -333,7 +335,7 @@ class GPTrans {
|
|
|
333
335
|
'{FROM_DENONYM}': fromReplace.FROM_DENONYM,
|
|
334
336
|
});
|
|
335
337
|
|
|
336
|
-
return model.block();
|
|
338
|
+
return await model.block({ addSystemExtra: false });
|
|
337
339
|
|
|
338
340
|
} finally {
|
|
339
341
|
// Always release the lock
|
|
@@ -407,14 +409,14 @@ class GPTrans {
|
|
|
407
409
|
|
|
408
410
|
// Track which keys need translation
|
|
409
411
|
const keysNeedingTranslation = [];
|
|
410
|
-
|
|
412
|
+
|
|
411
413
|
for (const [context, pairs] of this.dbFrom.entries()) {
|
|
412
414
|
// Skip the _context metadata
|
|
413
415
|
if (context === '_context') continue;
|
|
414
|
-
|
|
416
|
+
|
|
415
417
|
this.setContext(context);
|
|
416
418
|
const contextHash = this._hash(context);
|
|
417
|
-
|
|
419
|
+
|
|
418
420
|
for (const [key, text] of Object.entries(pairs)) {
|
|
419
421
|
// Check if translation already exists
|
|
420
422
|
if (!this.dbTarget.get(contextHash, key)) {
|
|
@@ -438,7 +440,7 @@ class GPTrans {
|
|
|
438
440
|
const checkInterval = setInterval(() => {
|
|
439
441
|
// Check if there are still pending translations or batch being processed
|
|
440
442
|
const hasPending = this.pendingTranslations.size > 0 || this.isProcessingBatch;
|
|
441
|
-
|
|
443
|
+
|
|
442
444
|
// Check if all needed translations are now complete
|
|
443
445
|
let allTranslated = true;
|
|
444
446
|
for (const { contextHash, key } of keysNeedingTranslation) {
|
|
@@ -447,7 +449,7 @@ class GPTrans {
|
|
|
447
449
|
break;
|
|
448
450
|
}
|
|
449
451
|
}
|
|
450
|
-
|
|
452
|
+
|
|
451
453
|
if (allTranslated && !hasPending) {
|
|
452
454
|
clearInterval(checkInterval);
|
|
453
455
|
resolve();
|
|
@@ -610,7 +612,7 @@ class GPTrans {
|
|
|
610
612
|
'{FROM_DENONYM}': this.replaceFrom.FROM_DENONYM,
|
|
611
613
|
});
|
|
612
614
|
|
|
613
|
-
return await model.block();
|
|
615
|
+
return await model.block({ addSystemExtra: false });
|
|
614
616
|
|
|
615
617
|
} finally {
|
|
616
618
|
releaseLock();
|
|
@@ -696,7 +698,7 @@ class GPTrans {
|
|
|
696
698
|
// Save translated image - preserve original file format
|
|
697
699
|
const filename = path.basename(targetPath, path.extname(targetPath));
|
|
698
700
|
const formatOptions = generator.getReferenceMetadata();
|
|
699
|
-
|
|
701
|
+
|
|
700
702
|
// Apply default quality settings for JPEG images
|
|
701
703
|
if (formatOptions && (formatOptions.format === 'jpeg' || formatOptions.format === 'jpg')) {
|
|
702
704
|
// Apply custom quality if specified, otherwise use defaults
|
|
@@ -705,7 +707,7 @@ class GPTrans {
|
|
|
705
707
|
formatOptions.chromaSubsampling = '4:4:4'; // Better color quality
|
|
706
708
|
formatOptions.optimiseCoding = true; // Lossless size reduction
|
|
707
709
|
}
|
|
708
|
-
|
|
710
|
+
|
|
709
711
|
await generator.save({ directory: targetDir, filename, formatOptions });
|
|
710
712
|
} else {
|
|
711
713
|
throw new Error('No translated image was generated');
|
package/package.json
CHANGED
package/prompt/translate.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Goal
|
|
2
2
|
Translation from {FROM_ISO} to {TARGET_ISO} ({TARGET_DENONYM} {TARGET_LANG}) with cultural adaptations.
|
|
3
|
+
{INSTRUCTION}
|
|
3
4
|
|
|
4
5
|
## Text to translate
|
|
5
6
|
```
|
|
@@ -22,4 +23,4 @@ Translation from {FROM_ISO} to {TARGET_ISO} ({TARGET_DENONYM} {TARGET_LANG}) wit
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
# Context
|
|
25
|
-
{CONTEXT}
|
|
26
|
+
{CONTEXT}
|
package/skills/gptrans/SKILL.md
CHANGED
|
@@ -110,6 +110,7 @@ console.log(gptrans.setContext().t('Welcome back'));
|
|
|
110
110
|
| `freeze` | `boolean` | `false` | Prevent new translations from being queued |
|
|
111
111
|
| `name` | `string` | `''` | Instance name (isolates cache files) |
|
|
112
112
|
| `context` | `string` | `''` | Default context for translations |
|
|
113
|
+
| `instruction` | `string` | `''` | Additional instruction for the translator (tone, style). Does not affect cache key |
|
|
113
114
|
| `promptFile` | `string` | `null` | Custom prompt file path (overrides built-in) |
|
|
114
115
|
| `debug` | `boolean` | `false` | Enable debug output |
|
|
115
116
|
|
|
@@ -186,6 +187,17 @@ console.log(gptrans.setContext('The user is female').t('You are welcome'));
|
|
|
186
187
|
console.log(gptrans.setContext().t('Thank you'));
|
|
187
188
|
```
|
|
188
189
|
|
|
190
|
+
### Set instruction for translation style
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
const gptrans = new GPTrans({
|
|
194
|
+
from: 'en', target: 'es-AR',
|
|
195
|
+
instruction: 'Use a formal and professional tone'
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
console.log(gptrans.t('Welcome to our platform'));
|
|
199
|
+
```
|
|
200
|
+
|
|
189
201
|
### Refine existing translations
|
|
190
202
|
|
|
191
203
|
Improve cached translations with specific instructions:
|
|
@@ -276,6 +288,7 @@ if (GPTrans.isLanguageAvailable('pt-BR')) {
|
|
|
276
288
|
- The `t()` method is synchronous and non-blocking. It returns the original text on first call and the cached translation on subsequent calls. Do NOT `await` it.
|
|
277
289
|
- To ensure translations are complete before using them, call `await gptrans.preload()` after registering texts with `t()`.
|
|
278
290
|
- Use `setContext()` for gender-aware or domain-specific translations. Context is captured per-batch and auto-resets when changed.
|
|
291
|
+
- Use the `instruction` constructor option for style/tone guidance (e.g., "Use a more natural tone"). Unlike `context`, `instruction` does NOT affect the cache key ā different instructions for the same text overwrite the same translation entry.
|
|
279
292
|
- Prefer passing an array of instructions to `refine()` over multiple calls ā it processes everything in a single API pass.
|
|
280
293
|
- Use model arrays (`model: ['sonnet45', 'gpt41']`) for production resilience with automatic fallback.
|
|
281
294
|
- Translation caches live in `db/gptrans_<locale>.json`. These files can be manually edited to override specific translations.
|