transduck 0.4.0 → 0.4.2

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/dist/cli.js CHANGED
@@ -81,7 +81,7 @@ export async function runTranslate(opts) {
81
81
  if (!validateTranslation(opts.text, translated)) {
82
82
  await store.insert({
83
83
  sourceText: opts.text, sourceLang: cfg.sourceLang, targetLang,
84
- projectContextHash, stringContextHash,
84
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
85
85
  translatedText: translated, model: cfg.backendModel, status: 'failed',
86
86
  });
87
87
  store.close();
@@ -89,7 +89,7 @@ export async function runTranslate(opts) {
89
89
  }
90
90
  await store.insert({
91
91
  sourceText: opts.text, sourceLang: cfg.sourceLang, targetLang,
92
- projectContextHash, stringContextHash,
92
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
93
93
  translatedText: translated, model: cfg.backendModel, status: 'translated',
94
94
  });
95
95
  store.close();
@@ -155,7 +155,7 @@ export async function runTranslatePlural(opts) {
155
155
  for (const [cat, translatedText] of Object.entries(forms)) {
156
156
  await store.insertPlural({
157
157
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang,
158
- projectContextHash, stringContextHash,
158
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
159
159
  pluralCategory: cat, translatedText,
160
160
  model: cfg.backendModel, status: 'failed',
161
161
  });
@@ -170,7 +170,7 @@ export async function runTranslatePlural(opts) {
170
170
  for (const [cat, translatedText] of Object.entries(forms)) {
171
171
  await store.insertPlural({
172
172
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang,
173
- projectContextHash, stringContextHash,
173
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
174
174
  pluralCategory: cat, translatedText,
175
175
  model: cfg.backendModel, status: 'translated',
176
176
  });
@@ -239,7 +239,7 @@ export async function runWarm(opts) {
239
239
  }
240
240
  await store.insertPlural({
241
241
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: lang,
242
- projectContextHash, stringContextHash,
242
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
243
243
  pluralCategory: cat, translatedText: translatedText,
244
244
  model: cfg.backendModel, status: allPresent ? 'translated' : 'failed',
245
245
  });
@@ -272,7 +272,7 @@ export async function runWarm(opts) {
272
272
  if (validateTranslation(entry.text, result)) {
273
273
  await store.insert({
274
274
  sourceText: entry.text, sourceLang: cfg.sourceLang, targetLang: lang,
275
- projectContextHash, stringContextHash,
275
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
276
276
  translatedText: result, model: cfg.backendModel, status: 'translated',
277
277
  });
278
278
  translated++;
@@ -280,7 +280,7 @@ export async function runWarm(opts) {
280
280
  else {
281
281
  await store.insert({
282
282
  sourceText: entry.text, sourceLang: cfg.sourceLang, targetLang: lang,
283
- projectContextHash, stringContextHash,
283
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
284
284
  translatedText: result, model: cfg.backendModel, status: 'failed',
285
285
  });
286
286
  failed++;
@@ -374,7 +374,7 @@ export async function runScan(opts) {
374
374
  for (const [cat, translatedText] of Object.entries(forms)) {
375
375
  await store.insertPlural({
376
376
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: lang,
377
- projectContextHash, stringContextHash,
377
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
378
378
  pluralCategory: cat, translatedText: translatedText,
379
379
  model: cfg.backendModel, status: 'translated',
380
380
  });
@@ -407,7 +407,7 @@ export async function runScan(opts) {
407
407
  if (validateTranslation(entry.text, result)) {
408
408
  await store.insert({
409
409
  sourceText: entry.text, sourceLang: cfg.sourceLang, targetLang: lang,
410
- projectContextHash, stringContextHash,
410
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
411
411
  translatedText: result, model: cfg.backendModel, status: 'translated',
412
412
  });
413
413
  translated++;
@@ -454,7 +454,7 @@ export async function runStats(opts) {
454
454
  }
455
455
  // CLI entry point
456
456
  const program = new Command();
457
- program.name('transduck').description('AI-native translation tool').version('0.4.0');
457
+ program.name('transduck').description('AI-native translation tool').version('0.4.2');
458
458
  program.command('init')
459
459
  .description('Initialize a new transduck project')
460
460
  .action(async () => {
package/dist/handler.js CHANGED
@@ -55,6 +55,7 @@ export async function handleTranslationRequest(body, configPath) {
55
55
  targetLang,
56
56
  projectContextHash,
57
57
  stringContextHash,
58
+ stringContext: item.context ?? '',
58
59
  translatedText: translated,
59
60
  model: cfg.backendModel,
60
61
  status: 'translated',
@@ -96,6 +97,7 @@ export async function handleTranslationRequest(body, configPath) {
96
97
  targetLang,
97
98
  projectContextHash,
98
99
  stringContextHash,
100
+ stringContext: item.context ?? '',
99
101
  pluralCategory: cat,
100
102
  translatedText: translatedText,
101
103
  model: cfg.backendModel,
package/dist/index.js CHANGED
@@ -73,14 +73,14 @@ export async function ait(sourceText, context, vars) {
73
73
  console.warn(`[transduck] Validation failed for: ${sourceText} -> ${translated}`);
74
74
  await state.store.insert({
75
75
  sourceText, sourceLang: cfg.sourceLang, targetLang: state.targetLang,
76
- projectContextHash, stringContextHash,
76
+ projectContextHash, stringContextHash, stringContext: context ?? '',
77
77
  translatedText: translated, model: cfg.backendModel, status: 'failed',
78
78
  });
79
79
  return sourceText;
80
80
  }
81
81
  await state.store.insert({
82
82
  sourceText, sourceLang: cfg.sourceLang, targetLang: state.targetLang,
83
- projectContextHash, stringContextHash,
83
+ projectContextHash, stringContextHash, stringContext: context ?? '',
84
84
  translatedText: translated, model: cfg.backendModel, status: 'translated',
85
85
  });
86
86
  return translated;
@@ -176,7 +176,7 @@ export async function aitPlural(one, other, count, opts) {
176
176
  const status = allPresent ? 'translated' : 'failed';
177
177
  await state.store.insertPlural({
178
178
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: state.targetLang,
179
- projectContextHash, stringContextHash,
179
+ projectContextHash, stringContextHash, stringContext: context ?? '',
180
180
  pluralCategory: cat, translatedText: text,
181
181
  model: cfg.backendModel, status,
182
182
  });
@@ -35,6 +35,7 @@ export function useTransDuck() {
35
35
  };
36
36
  }
37
37
  export function _resetReactState() {
38
+ flushScheduled = false;
38
39
  _state = {
39
40
  language: '',
40
41
  sourceLang: 'EN',
@@ -62,6 +63,19 @@ function interpolateVars(text, vars) {
62
63
  }
63
64
  return result;
64
65
  }
66
+ // --- Self-flushing microtask scheduler ---
67
+ // Ensures pending strings are fetched even when the provider doesn't re-render
68
+ // (e.g., client-side navigation in Next.js App Router persistent layouts).
69
+ let flushScheduled = false;
70
+ function schedulePendingFlush() {
71
+ if (flushScheduled || !_state.triggerFetch || _state.isLanguageSwitch)
72
+ return;
73
+ flushScheduled = true;
74
+ queueMicrotask(() => {
75
+ flushScheduled = false;
76
+ _state.triggerFetch?.();
77
+ });
78
+ }
65
79
  // --- Stable exported functions ---
66
80
  export function t(sourceText, context, vars) {
67
81
  const key = `${sourceText}||${context ?? ''}`;
@@ -75,8 +89,9 @@ export function t(sourceText, context, vars) {
75
89
  if (cached !== undefined) {
76
90
  return interpolateVars(cached, vars);
77
91
  }
78
- // Queue for fetch (useEffect in provider will flush after render)
92
+ // Queue for fetch
79
93
  _state.pendingStrings.add(key);
94
+ schedulePendingFlush();
80
95
  // Return source text as fallback
81
96
  return interpolateVars(sourceText, vars);
82
97
  }
@@ -109,8 +124,9 @@ export function tPlural(one, other, count, opts) {
109
124
  const form = cachedForms[category] ?? cachedForms['other'] ?? other;
110
125
  return interpolateVars(form, vars);
111
126
  }
112
- // Queue for fetch (useEffect in provider will flush after render)
127
+ // Queue for fetch
113
128
  _state.pendingPlurals.add(cacheKey);
129
+ schedulePendingFlush();
114
130
  // Fallback to source form
115
131
  const rules = new Intl.PluralRules(_state.sourceLang.toLowerCase());
116
132
  const form = rules.select(count) === 'one' ? one : other;
package/dist/storage.d.ts CHANGED
@@ -6,11 +6,13 @@ export interface LookupParams {
6
6
  stringContextHash: string;
7
7
  }
8
8
  export interface InsertParams extends LookupParams {
9
+ stringContext: string;
9
10
  translatedText: string;
10
11
  model: string;
11
12
  status: string;
12
13
  }
13
14
  export interface InsertPluralParams extends LookupParams {
15
+ stringContext: string;
14
16
  pluralCategory: string;
15
17
  translatedText: string;
16
18
  model: string;
package/dist/storage.js CHANGED
@@ -29,7 +29,7 @@ export class TranslationStore {
29
29
  this.db = open({
30
30
  path: this.dbPath,
31
31
  mapSize: DEFAULT_MAP_SIZE,
32
- // Use msgpack (default) for efficient storage
32
+ encoding: 'json',
33
33
  });
34
34
  }
35
35
  getDb() {
@@ -62,6 +62,7 @@ export class TranslationStore {
62
62
  created_at: new Date().toISOString(),
63
63
  project_context_hash: params.projectContextHash,
64
64
  string_context_hash: params.stringContextHash,
65
+ string_context: params.stringContext,
65
66
  });
66
67
  }
67
68
  async lookupPlural(params) {
@@ -98,6 +99,7 @@ export class TranslationStore {
98
99
  created_at: new Date().toISOString(),
99
100
  project_context_hash: params.projectContextHash,
100
101
  string_context_hash: params.stringContextHash,
102
+ string_context: params.stringContext,
101
103
  });
102
104
  }
103
105
  async stats() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transduck",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "AI-native translation tool using source text as keys",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/cli.ts CHANGED
@@ -120,7 +120,7 @@ export async function runTranslate(opts: TranslateOptions): Promise<string> {
120
120
  if (!validateTranslation(opts.text, translated)) {
121
121
  await store.insert({
122
122
  sourceText: opts.text, sourceLang: cfg.sourceLang, targetLang,
123
- projectContextHash, stringContextHash,
123
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
124
124
  translatedText: translated, model: cfg.backendModel, status: 'failed',
125
125
  });
126
126
  store.close();
@@ -129,7 +129,7 @@ export async function runTranslate(opts: TranslateOptions): Promise<string> {
129
129
 
130
130
  await store.insert({
131
131
  sourceText: opts.text, sourceLang: cfg.sourceLang, targetLang,
132
- projectContextHash, stringContextHash,
132
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
133
133
  translatedText: translated, model: cfg.backendModel, status: 'translated',
134
134
  });
135
135
  store.close();
@@ -210,7 +210,7 @@ export async function runTranslatePlural(opts: TranslatePluralOptions): Promise<
210
210
  for (const [cat, translatedText] of Object.entries(forms)) {
211
211
  await store.insertPlural({
212
212
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang,
213
- projectContextHash, stringContextHash,
213
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
214
214
  pluralCategory: cat, translatedText,
215
215
  model: cfg.backendModel, status: 'failed',
216
216
  });
@@ -226,7 +226,7 @@ export async function runTranslatePlural(opts: TranslatePluralOptions): Promise<
226
226
  for (const [cat, translatedText] of Object.entries(forms)) {
227
227
  await store.insertPlural({
228
228
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang,
229
- projectContextHash, stringContextHash,
229
+ projectContextHash, stringContextHash, stringContext: opts.stringContext ?? '',
230
230
  pluralCategory: cat, translatedText,
231
231
  model: cfg.backendModel, status: 'translated',
232
232
  });
@@ -313,7 +313,7 @@ export async function runWarm(opts: WarmOptions): Promise<string> {
313
313
 
314
314
  await store.insertPlural({
315
315
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: lang,
316
- projectContextHash, stringContextHash,
316
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
317
317
  pluralCategory: cat, translatedText: translatedText as string,
318
318
  model: cfg.backendModel, status: allPresent ? 'translated' : 'failed',
319
319
  });
@@ -344,14 +344,14 @@ export async function runWarm(opts: WarmOptions): Promise<string> {
344
344
  if (validateTranslation(entry.text, result)) {
345
345
  await store.insert({
346
346
  sourceText: entry.text, sourceLang: cfg.sourceLang, targetLang: lang,
347
- projectContextHash, stringContextHash,
347
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
348
348
  translatedText: result, model: cfg.backendModel, status: 'translated',
349
349
  });
350
350
  translated++;
351
351
  } else {
352
352
  await store.insert({
353
353
  sourceText: entry.text, sourceLang: cfg.sourceLang, targetLang: lang,
354
- projectContextHash, stringContextHash,
354
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
355
355
  translatedText: result, model: cfg.backendModel, status: 'failed',
356
356
  });
357
357
  failed++;
@@ -468,7 +468,7 @@ export async function runScan(opts: ScanOptions): Promise<string> {
468
468
  for (const [cat, translatedText] of Object.entries(forms)) {
469
469
  await store.insertPlural({
470
470
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: lang,
471
- projectContextHash, stringContextHash,
471
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
472
472
  pluralCategory: cat, translatedText: translatedText as string,
473
473
  model: cfg.backendModel, status: 'translated',
474
474
  });
@@ -506,7 +506,7 @@ export async function runScan(opts: ScanOptions): Promise<string> {
506
506
  if (validateTranslation(entry.text!, result)) {
507
507
  await store.insert({
508
508
  sourceText: entry.text!, sourceLang: cfg.sourceLang, targetLang: lang,
509
- projectContextHash, stringContextHash,
509
+ projectContextHash, stringContextHash, stringContext: entry.context ?? '',
510
510
  translatedText: result, model: cfg.backendModel, status: 'translated',
511
511
  });
512
512
  translated++;
@@ -562,7 +562,7 @@ export async function runStats(opts: StatsOptions): Promise<string> {
562
562
  // CLI entry point
563
563
  const program = new Command();
564
564
 
565
- program.name('transduck').description('AI-native translation tool').version('0.4.0');
565
+ program.name('transduck').description('AI-native translation tool').version('0.4.2');
566
566
 
567
567
  program.command('init')
568
568
  .description('Initialize a new transduck project')
package/src/handler.ts CHANGED
@@ -99,6 +99,7 @@ export async function handleTranslationRequest(
99
99
  targetLang,
100
100
  projectContextHash,
101
101
  stringContextHash,
102
+ stringContext: item.context ?? '',
102
103
  translatedText: translated,
103
104
  model: cfg.backendModel,
104
105
  status: 'translated',
@@ -151,6 +152,7 @@ export async function handleTranslationRequest(
151
152
  targetLang,
152
153
  projectContextHash,
153
154
  stringContextHash,
155
+ stringContext: item.context ?? '',
154
156
  pluralCategory: cat,
155
157
  translatedText: translatedText as string,
156
158
  model: cfg.backendModel,
package/src/index.ts CHANGED
@@ -102,7 +102,7 @@ export async function ait(
102
102
  console.warn(`[transduck] Validation failed for: ${sourceText} -> ${translated}`);
103
103
  await state.store!.insert({
104
104
  sourceText, sourceLang: cfg.sourceLang, targetLang: state.targetLang!,
105
- projectContextHash, stringContextHash,
105
+ projectContextHash, stringContextHash, stringContext: context ?? '',
106
106
  translatedText: translated, model: cfg.backendModel, status: 'failed',
107
107
  });
108
108
  return sourceText;
@@ -110,7 +110,7 @@ export async function ait(
110
110
 
111
111
  await state.store!.insert({
112
112
  sourceText, sourceLang: cfg.sourceLang, targetLang: state.targetLang!,
113
- projectContextHash, stringContextHash,
113
+ projectContextHash, stringContextHash, stringContext: context ?? '',
114
114
  translatedText: translated, model: cfg.backendModel, status: 'translated',
115
115
  });
116
116
  return translated;
@@ -224,7 +224,7 @@ export async function aitPlural(
224
224
  const status = allPresent ? 'translated' : 'failed';
225
225
  await state.store!.insertPlural({
226
226
  sourceText: sourceKey, sourceLang: cfg.sourceLang, targetLang: state.targetLang!,
227
- projectContextHash, stringContextHash,
227
+ projectContextHash, stringContextHash, stringContext: context ?? '',
228
228
  pluralCategory: cat, translatedText: text,
229
229
  model: cfg.backendModel, status,
230
230
  });
@@ -82,6 +82,7 @@ export function useTransDuck(): UseTransDuckReturn {
82
82
  }
83
83
 
84
84
  export function _resetReactState(): void {
85
+ flushScheduled = false;
85
86
  _state = {
86
87
  language: '',
87
88
  sourceLang: 'EN',
@@ -111,6 +112,21 @@ function interpolateVars(text: string, vars?: Record<string, string | number> |
111
112
  return result;
112
113
  }
113
114
 
115
+ // --- Self-flushing microtask scheduler ---
116
+ // Ensures pending strings are fetched even when the provider doesn't re-render
117
+ // (e.g., client-side navigation in Next.js App Router persistent layouts).
118
+
119
+ let flushScheduled = false;
120
+
121
+ function schedulePendingFlush() {
122
+ if (flushScheduled || !_state.triggerFetch || _state.isLanguageSwitch) return;
123
+ flushScheduled = true;
124
+ queueMicrotask(() => {
125
+ flushScheduled = false;
126
+ _state.triggerFetch?.();
127
+ });
128
+ }
129
+
114
130
  // --- Stable exported functions ---
115
131
 
116
132
  export function t(
@@ -132,8 +148,9 @@ export function t(
132
148
  return interpolateVars(cached, vars);
133
149
  }
134
150
 
135
- // Queue for fetch (useEffect in provider will flush after render)
151
+ // Queue for fetch
136
152
  _state.pendingStrings.add(key);
153
+ schedulePendingFlush();
137
154
 
138
155
  // Return source text as fallback
139
156
  return interpolateVars(sourceText, vars);
@@ -175,8 +192,9 @@ export function tPlural(
175
192
  return interpolateVars(form, vars);
176
193
  }
177
194
 
178
- // Queue for fetch (useEffect in provider will flush after render)
195
+ // Queue for fetch
179
196
  _state.pendingPlurals.add(cacheKey);
197
+ schedulePendingFlush();
180
198
 
181
199
  // Fallback to source form
182
200
  const rules = new Intl.PluralRules(_state.sourceLang.toLowerCase());
package/src/storage.ts CHANGED
@@ -13,12 +13,14 @@ export interface LookupParams {
13
13
  }
14
14
 
15
15
  export interface InsertParams extends LookupParams {
16
+ stringContext: string;
16
17
  translatedText: string;
17
18
  model: string;
18
19
  status: string;
19
20
  }
20
21
 
21
22
  export interface InsertPluralParams extends LookupParams {
23
+ stringContext: string;
22
24
  pluralCategory: string;
23
25
  translatedText: string;
24
26
  model: string;
@@ -33,6 +35,7 @@ interface StoredEntry {
33
35
  created_at: string;
34
36
  project_context_hash: string;
35
37
  string_context_hash: string;
38
+ string_context: string;
36
39
  }
37
40
 
38
41
  interface Stats {
@@ -73,7 +76,7 @@ export class TranslationStore {
73
76
  this.db = open<StoredEntry, string>({
74
77
  path: this.dbPath,
75
78
  mapSize: DEFAULT_MAP_SIZE,
76
- // Use msgpack (default) for efficient storage
79
+ encoding: 'json',
77
80
  });
78
81
  }
79
82
 
@@ -107,6 +110,7 @@ export class TranslationStore {
107
110
  created_at: new Date().toISOString(),
108
111
  project_context_hash: params.projectContextHash,
109
112
  string_context_hash: params.stringContextHash,
113
+ string_context: params.stringContext,
110
114
  });
111
115
  }
112
116
 
@@ -143,6 +147,7 @@ export class TranslationStore {
143
147
  created_at: new Date().toISOString(),
144
148
  project_context_hash: params.projectContextHash,
145
149
  string_context_hash: params.stringContextHash,
150
+ string_context: params.stringContext,
146
151
  });
147
152
  }
148
153
 
package/tests/ait.test.ts CHANGED
@@ -56,7 +56,7 @@ describe('ait', () => {
56
56
  await store!.insert({
57
57
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
58
58
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
59
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
59
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
60
60
  });
61
61
 
62
62
  const result = await ait('Hello');
@@ -94,7 +94,7 @@ describe('ait', () => {
94
94
  await store!.insert({
95
95
  sourceText: 'Welcome {name}', sourceLang: 'EN', targetLang: 'DE',
96
96
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
97
- translatedText: 'Willkommen {name}', model: 'gpt-4.1-mini', status: 'translated',
97
+ translatedText: 'Willkommen {name}', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
98
98
  });
99
99
 
100
100
  const result = await ait('Welcome {name}', undefined, { name: 'Tim' });
@@ -179,13 +179,13 @@ describe('aitPlural', () => {
179
179
  ...lookupParams,
180
180
  pluralCategory: 'one',
181
181
  translatedText: '{count} Nachricht',
182
- model: 'gpt-4.1-mini', status: 'translated',
182
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
183
183
  });
184
184
  await store!.insertPlural({
185
185
  ...lookupParams,
186
186
  pluralCategory: 'other',
187
187
  translatedText: '{count} Nachrichten',
188
- model: 'gpt-4.1-mini', status: 'translated',
188
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
189
189
  });
190
190
 
191
191
  const result1 = await aitPlural('{count} message', '{count} messages', 1);
package/tests/cli.test.ts CHANGED
@@ -76,7 +76,7 @@ describe('CLI functions', () => {
76
76
  await store.insert({
77
77
  sourceText: 'Welcome {name}', sourceLang: 'EN', targetLang: 'DE',
78
78
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
79
- translatedText: 'Willkommen {name}', model: 'gpt-4.1-mini', status: 'translated',
79
+ translatedText: 'Willkommen {name}', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
80
80
  });
81
81
  store.close();
82
82
 
@@ -105,13 +105,13 @@ describe('CLI functions', () => {
105
105
  ...baseParams,
106
106
  pluralCategory: 'one',
107
107
  translatedText: '{count} Nachricht',
108
- model: 'gpt-4.1-mini', status: 'translated',
108
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
109
109
  });
110
110
  await store.insertPlural({
111
111
  ...baseParams,
112
112
  pluralCategory: 'other',
113
113
  translatedText: '{count} Nachrichten',
114
- model: 'gpt-4.1-mini', status: 'translated',
114
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
115
115
  });
116
116
  store.close();
117
117
 
@@ -143,7 +143,7 @@ describe('CLI functions', () => {
143
143
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
144
144
  pluralCategory: 'one',
145
145
  translatedText: '{count} Nachricht',
146
- model: 'gpt-4.1-mini', status: 'translated',
146
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
147
147
  });
148
148
  store.close();
149
149
 
@@ -208,7 +208,7 @@ describe('CLI functions', () => {
208
208
  await store.insert({
209
209
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
210
210
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
211
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
211
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
212
212
  });
213
213
  store.close();
214
214
 
@@ -53,7 +53,7 @@ describe('handleTranslationRequest', () => {
53
53
  await store.insert({
54
54
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
55
55
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
56
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
56
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
57
57
  });
58
58
  store.close();
59
59
 
@@ -88,7 +88,7 @@ describe('handleTranslationRequest', () => {
88
88
  await store.insert({
89
89
  sourceText: 'Book', sourceLang: 'EN', targetLang: 'DE',
90
90
  projectContextHash: hash('A test site'), stringContextHash: hash('Hotel booking'),
91
- translatedText: 'Buchen', model: 'gpt-4.1-mini', status: 'translated',
91
+ translatedText: 'Buchen', model: 'gpt-4.1-mini', status: 'translated', stringContext: 'Hotel booking',
92
92
  });
93
93
  store.close();
94
94
 
@@ -113,13 +113,13 @@ describe('handleTranslationRequest', () => {
113
113
  sourceText: sourceKey, sourceLang: 'EN', targetLang: 'DE',
114
114
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
115
115
  pluralCategory: 'one', translatedText: '{count} Artikel',
116
- model: 'gpt-4.1-mini', status: 'translated',
116
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
117
117
  });
118
118
  await store.insertPlural({
119
119
  sourceText: sourceKey, sourceLang: 'EN', targetLang: 'DE',
120
120
  projectContextHash: hash('A test site'), stringContextHash: hash(''),
121
121
  pluralCategory: 'other', translatedText: '{count} Artikel',
122
- model: 'gpt-4.1-mini', status: 'translated',
122
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
123
123
  });
124
124
  store.close();
125
125
 
@@ -31,7 +31,7 @@ describe('TranslationStore', () => {
31
31
  await store.insert({
32
32
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
33
33
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
34
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
34
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
35
35
  });
36
36
  const result = await store.lookup({
37
37
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
@@ -52,7 +52,7 @@ describe('TranslationStore', () => {
52
52
  await store.insert({
53
53
  sourceText: 'Bad', sourceLang: 'EN', targetLang: 'DE',
54
54
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
55
- translatedText: 'bad translation', model: 'gpt-4.1-mini', status: 'failed',
55
+ translatedText: 'bad translation', model: 'gpt-4.1-mini', status: 'failed', stringContext: '',
56
56
  });
57
57
  const result = await store.lookup({
58
58
  sourceText: 'Bad', sourceLang: 'EN', targetLang: 'DE',
@@ -65,7 +65,7 @@ describe('TranslationStore', () => {
65
65
  const entry = {
66
66
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
67
67
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
68
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
68
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
69
69
  };
70
70
  await store.insert(entry);
71
71
  await store.insert(entry); // should not throw
@@ -75,12 +75,12 @@ describe('TranslationStore', () => {
75
75
  await store.insert({
76
76
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
77
77
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
78
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
78
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
79
79
  });
80
80
  await store.insert({
81
81
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'ES',
82
82
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
83
- translatedText: 'Hola', model: 'gpt-4.1-mini', status: 'translated',
83
+ translatedText: 'Hola', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
84
84
  });
85
85
  const stats = await store.stats();
86
86
  expect(stats.totalTranslations).toBe(2);
@@ -103,13 +103,13 @@ describe('TranslationStore', () => {
103
103
  ...lookupParams,
104
104
  pluralCategory: 'one',
105
105
  translatedText: '{count} Nachricht',
106
- model: 'gpt-4.1-mini', status: 'translated',
106
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
107
107
  });
108
108
  await store.insertPlural({
109
109
  ...lookupParams,
110
110
  pluralCategory: 'other',
111
111
  translatedText: '{count} Nachrichten',
112
- model: 'gpt-4.1-mini', status: 'translated',
112
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
113
113
  });
114
114
 
115
115
  // Lookup should return all forms
@@ -140,13 +140,13 @@ describe('TranslationStore', () => {
140
140
  ...lookupParams,
141
141
  pluralCategory: 'one',
142
142
  translatedText: '{count} элемент',
143
- model: 'gpt-4.1-mini', status: 'translated',
143
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
144
144
  });
145
145
  await store.insertPlural({
146
146
  ...lookupParams,
147
147
  pluralCategory: 'few',
148
148
  translatedText: 'bad translation',
149
- model: 'gpt-4.1-mini', status: 'failed',
149
+ model: 'gpt-4.1-mini', status: 'failed', stringContext: '',
150
150
  });
151
151
 
152
152
  const result = await store.lookupPlural(lookupParams);
@@ -165,7 +165,7 @@ describe('TranslationStore', () => {
165
165
  ...baseParams,
166
166
  sourceText: 'Hello',
167
167
  translatedText: 'Hallo',
168
- model: 'gpt-4.1-mini', status: 'translated',
168
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
169
169
  });
170
170
 
171
171
  // Plural insert with same-ish source text
@@ -174,7 +174,7 @@ describe('TranslationStore', () => {
174
174
  sourceText: 'Hello',
175
175
  pluralCategory: 'one',
176
176
  translatedText: 'Hallo singular',
177
- model: 'gpt-4.1-mini', status: 'translated',
177
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
178
178
  });
179
179
 
180
180
  // Regular lookup should return regular entry
@@ -193,7 +193,7 @@ describe('TranslationStore', () => {
193
193
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
194
194
  pluralCategory: 'other',
195
195
  translatedText: '{count} Nachrichten',
196
- model: 'gpt-4.1-mini', status: 'translated',
196
+ model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
197
197
  };
198
198
  await store.insertPlural(entry);
199
199
  await store.insertPlural(entry); // should not throw
@@ -205,17 +205,17 @@ describe('TranslationStore', () => {
205
205
  await store.insert({
206
206
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'DE',
207
207
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
208
- translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated',
208
+ translatedText: 'Hallo', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
209
209
  });
210
210
  await store.insert({
211
211
  sourceText: 'Bad', sourceLang: 'EN', targetLang: 'DE',
212
212
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
213
- translatedText: 'schlecht', model: 'gpt-4.1-mini', status: 'failed',
213
+ translatedText: 'schlecht', model: 'gpt-4.1-mini', status: 'failed', stringContext: '',
214
214
  });
215
215
  await store.insert({
216
216
  sourceText: 'Hello', sourceLang: 'EN', targetLang: 'ES',
217
217
  projectContextHash: hash('ctx'), stringContextHash: hash(''),
218
- translatedText: 'Hola', model: 'gpt-4.1-mini', status: 'translated',
218
+ translatedText: 'Hola', model: 'gpt-4.1-mini', status: 'translated', stringContext: '',
219
219
  });
220
220
 
221
221
  expect(store.count()).toBe(3);