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 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gptrans",
3
3
  "type": "module",
4
- "version": "2.0.0",
4
+ "version": "2.0.4",
5
5
  "description": "šŸš† GPTrans - The smarter AI-powered way to translate.",
6
6
  "keywords": [
7
7
  "translate",
@@ -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}
@@ -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.