wuchale 0.22.6 → 0.22.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.
@@ -5,6 +5,7 @@ type Batch = {
5
5
  targetLocales: string[];
6
6
  messages: Item[];
7
7
  };
8
+ type GroupKey = string | string[];
8
9
  export type AIPassThruOpts = {
9
10
  batchSize: number;
10
11
  parallel: number;
@@ -16,7 +17,7 @@ export type AI = AIPassThruOpts & {
16
17
  };
17
18
  export default class AIQueue {
18
19
  #private;
19
- batches: Batch[];
20
+ batches: Map<GroupKey, Batch[]>;
20
21
  nextBatchId: number;
21
22
  running: Promise<void> | null;
22
23
  sourceLocale: string;
@@ -27,6 +28,8 @@ export default class AIQueue {
27
28
  constructor(sourceLocale: string, ai: AI, onComplete: () => Promise<void>, log: Logger);
28
29
  translate: (batch: Batch, attempt?: number) => Promise<void>;
29
30
  run: () => Promise<void>;
30
- add: (messages: Item[]) => void;
31
+ groupItemsByLocales: (items: Item[]) => Map<GroupKey, Item[]>;
32
+ prepItemsInBatches: (itemsByGroup: Map<GroupKey, Item[]>) => [string, Batch, number][];
33
+ add: (items: Item[]) => void;
31
34
  }
32
35
  export {};
package/dist/ai/index.js CHANGED
@@ -48,7 +48,7 @@ Respond ONLY with raw compact JSON. Do not wrap it in markdown code fences or ad
48
48
  // implements a queue for a sequential translation useful for vite's transform during dev
49
49
  // as vite can do async transform
50
50
  export default class AIQueue {
51
- batches = [];
51
+ batches = new Map();
52
52
  nextBatchId = 0;
53
53
  running = null;
54
54
  sourceLocale;
@@ -138,62 +138,80 @@ export default class AIQueue {
138
138
  await this.translate(batch, attempt);
139
139
  };
140
140
  run = async () => {
141
- while (this.batches.length > 0) {
141
+ while (this.batches.size > 0) {
142
142
  const allBatches = [];
143
- while (this.batches.length > 0 && allBatches.length < this.ai.parallel) {
144
- allBatches.push(this.batches.pop());
143
+ for (const [group, batches] of this.batches) {
144
+ while (batches.length > 0 && allBatches.length < this.ai.parallel) {
145
+ allBatches.push(batches.pop());
146
+ }
147
+ if (batches.length === 0) {
148
+ this.batches.delete(group);
149
+ }
145
150
  }
146
151
  await Promise.all(allBatches.map(this.translate));
147
152
  }
148
153
  await this.onComplete();
149
154
  this.running = null;
150
155
  };
151
- add = (messages) => {
152
- if (!this.ai) {
153
- return;
154
- }
156
+ groupItemsByLocales = (items) => {
155
157
  const itemsByLocales = new Map();
156
- for (const item of messages) {
158
+ for (const item of items) {
157
159
  for (const [loc, transl] of item.translations.entries()) {
158
160
  if (loc === this.sourceLocale || transl[0]) {
159
161
  continue;
160
162
  }
161
163
  const group = this.ai.group[this.sourceLocale]?.find(g => g.includes(loc));
162
164
  const groupKey = group ?? loc;
163
- if (!itemsByLocales.has(groupKey)) {
164
- itemsByLocales.set(groupKey, []);
165
+ const groupItems = itemsByLocales.get(groupKey);
166
+ if (groupItems == null) {
167
+ itemsByLocales.set(groupKey, [item]);
165
168
  }
166
- itemsByLocales.get(groupKey)?.push(item);
169
+ else {
170
+ groupItems.push(item);
171
+ }
172
+ }
173
+ }
174
+ return itemsByLocales;
175
+ };
176
+ prepItemsInBatches = (itemsByGroup) => {
177
+ const opInfo = [];
178
+ for (let [groupKey, items] of itemsByGroup) {
179
+ const groupBatches = this.batches.get(groupKey) ?? [];
180
+ const lastBatch = groupBatches.at(-1);
181
+ if (lastBatch && lastBatch.messages.length < this.ai.batchSize) {
182
+ const lastBatchFree = this.ai.batchSize - lastBatch.messages.length;
183
+ const itemsToAdd = items.slice(0, lastBatchFree);
184
+ opInfo.push(['(add)', lastBatch, itemsToAdd.length]);
185
+ lastBatch.messages.push(...itemsToAdd);
186
+ items = items.slice(lastBatchFree);
167
187
  }
188
+ for (let i = 0; i < items.length; i += this.ai.batchSize) {
189
+ const chunk = items.slice(i, i + this.ai.batchSize);
190
+ const batch = {
191
+ id: this.nextBatchId,
192
+ targetLocales: Array.isArray(groupKey) ? groupKey : [groupKey],
193
+ messages: chunk,
194
+ };
195
+ groupBatches.push(batch);
196
+ opInfo.push([color.yellow('(new)'), batch, chunk.length]);
197
+ this.nextBatchId++;
198
+ }
199
+ if (!this.batches.has(groupKey)) {
200
+ this.batches.set(groupKey, groupBatches);
201
+ }
202
+ }
203
+ return opInfo;
204
+ };
205
+ add = (items) => {
206
+ if (!this.ai) {
207
+ return;
168
208
  }
209
+ const itemsByLocales = this.groupItemsByLocales(items);
169
210
  if (itemsByLocales.size === 0) {
170
211
  // all translated
171
212
  return;
172
213
  }
173
- const opInfo = [];
174
- const lastBatch = this.batches.at(-1);
175
- if (lastBatch && lastBatch.messages.length < this.ai.batchSize && itemsByLocales.has(lastBatch.targetLocales)) {
176
- const lastBatchFree = this.ai.batchSize - lastBatch.messages.length;
177
- const localeItems = itemsByLocales.get(lastBatch.targetLocales);
178
- const msgs = localeItems.slice(0, lastBatchFree);
179
- opInfo.push(['(add)', lastBatch, msgs.length]);
180
- lastBatch.messages.push(...msgs);
181
- itemsByLocales.set(lastBatch.targetLocales, localeItems.slice(lastBatchFree));
182
- }
183
- for (const [groupKey, items] of itemsByLocales) {
184
- if (items.length === 0) {
185
- continue;
186
- }
187
- const batch = {
188
- id: this.nextBatchId,
189
- targetLocales: Array.isArray(groupKey) ? groupKey : [groupKey],
190
- messages: items,
191
- };
192
- this.batches.push(batch);
193
- opInfo.push([color.yellow('(new)'), batch, items.length]);
194
- this.nextBatchId++;
195
- }
196
- for (const [opType, batch, msgsLen] of opInfo) {
214
+ for (const [opType, batch, msgsLen] of this.prepItemsInBatches(itemsByLocales)) {
197
215
  this.log.info(`${this.#requestName(batch.id, batch.targetLocales)}: ${opType} translate ${color.cyan(msgsLen)} messages`);
198
216
  }
199
217
  if (!this.running) {
@@ -251,7 +251,8 @@ export class AdapterHandler {
251
251
  let getRuntimePlain = getRuntimeVars.plain;
252
252
  let getRuntimeReactive = getRuntimeVars.reactive;
253
253
  if (hmrData != null) {
254
- head.push(`const ${varNames.hmrUpdate} = ${JSON.stringify(hmrData)}`);
254
+ const hmrDataStr = JSON.stringify(hmrData).replaceAll('</script>', '\\x3C/script>');
255
+ head.push(`const ${varNames.hmrUpdate} = ${hmrDataStr}`);
255
256
  getRuntimePlain += 'hmr_';
256
257
  getRuntimeReactive += 'hmr_';
257
258
  head.push(this.#hmrUpdateFunc(getRuntimeVars.plain, getRuntimePlain), this.#hmrUpdateFunc(getRuntimeVars.reactive, getRuntimeReactive));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wuchale",
3
- "version": "0.22.6",
3
+ "version": "0.22.8",
4
4
  "description": "Protobuf-like i18n from plain code",
5
5
  "scripts": {
6
6
  "dev": "tsc --watch",
@@ -96,7 +96,7 @@
96
96
  "devDependencies": {
97
97
  "@types/node": "~24.12.0",
98
98
  "@types/picomatch": "^4.0.1",
99
- "typescript": "^5.9.3"
99
+ "typescript": "^6.0.2"
100
100
  },
101
101
  "funding": "https://github.com/sponsors/wuchalejs",
102
102
  "type": "module"