@walkinissue/angy 0.2.18 → 0.2.19
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 +52 -278
- package/dist/plugin.js +71 -2
- package/dist/server.d.ts +28 -3
- package/dist/server.js +136 -46
- package/dist/server.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Angy
|
|
2
2
|
|
|
3
|
-
Dev-only SvelteKit translation helper for in-app PO
|
|
3
|
+
Dev-only SvelteKit translation helper for in-app PO workflows.
|
|
4
|
+
|
|
5
|
+
<video src="./docs/images/Backdrop_And_Caching.webm" controls muted playsinline width="100%"></video>
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -8,134 +10,30 @@ Dev-only SvelteKit translation helper for in-app PO workflow.
|
|
|
8
10
|
npm install @walkinissue/angy
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
- Repository: [github.com/walkingIssue/Angy](https://github.com/walkingIssue/Angy)
|
|
14
|
-
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
|
15
|
-
- Roadmap: [docs/roadmap.md](./docs/roadmap.md)
|
|
16
|
-
- Wuchale runtime wiring: [docs/wuchale-runtime.md](./docs/wuchale-runtime.md)
|
|
17
|
-
|
|
18
|
-
## What it is
|
|
19
|
-
|
|
20
|
-
- A QA widget for in-app translation work
|
|
21
|
-
- A PO catalog lookup and commit layer
|
|
22
|
-
- A suggestion pipeline for untranslated strings
|
|
23
|
-
- A SvelteKit-friendly integration shape
|
|
24
|
-
|
|
25
|
-
## Showcase
|
|
26
|
-
|
|
27
|
-
Angy is meant to keep translators and developers inside the app while they work.
|
|
28
|
-
|
|
29
|
-
Core loop:
|
|
13
|
+
## What it does
|
|
30
14
|
|
|
31
|
-
- select
|
|
32
|
-
-
|
|
33
|
-
- stage
|
|
15
|
+
- select copy directly in the app
|
|
16
|
+
- resolve the best PO key with nearby alternatives
|
|
17
|
+
- stage and commit translations without leaving the page
|
|
34
18
|
- request AI suggestions for untranslated strings
|
|
35
|
-
- rotate locales
|
|
36
|
-
- commit reviewed changes back to the working PO
|
|
37
|
-
|
|
38
|
-
Planned repo assets:
|
|
39
|
-
|
|
40
|
-
- screenshots in [`docs/images`](./docs/images)
|
|
41
|
-
- short workflow recordings once the capture set is ready
|
|
42
|
-
|
|
43
|
-
## UX flow
|
|
44
|
-
|
|
45
|
-
### 1. Capture text in the app
|
|
46
|
-
|
|
47
|
-
- select text on the page
|
|
48
|
-
- or hold `Alt` and click an interactive element
|
|
49
|
-
- Angy opens with the selected string prefilled
|
|
50
|
-
|
|
51
|
-
### 2. Resolve the key
|
|
52
|
-
|
|
53
|
-
- Angy looks up the best PO match
|
|
54
|
-
- it shows the matched key, context, references, comments, and flags
|
|
55
|
-
- it also expands nearby alternatives so repeated copy can be resolved safely
|
|
19
|
+
- rotate locales for visual QA
|
|
56
20
|
|
|
57
|
-
|
|
21
|
+
## Quick flow
|
|
58
22
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
23
|
+
1. Select text or `Alt`+click an interactive element.
|
|
24
|
+
2. Review the matched key and alternatives.
|
|
25
|
+
3. Edit or accept a suggestion.
|
|
26
|
+
4. Stage and commit.
|
|
27
|
+
5. Rotate locales and verify the page visually.
|
|
63
28
|
|
|
64
|
-
|
|
29
|
+
## Docs site
|
|
65
30
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
- commit reviewed changes into the working catalog
|
|
69
|
-
|
|
70
|
-
### 5. QA in the page
|
|
71
|
-
|
|
72
|
-
- use the QA locale toggle to rotate rendered locales
|
|
73
|
-
- compare source and target visually
|
|
74
|
-
- catch wrapping, spacing, and hierarchy regressions before leaving the page
|
|
75
|
-
|
|
76
|
-
### 6. Promotion workflow
|
|
77
|
-
|
|
78
|
-
- if a project is using Angy for migration or catalog promotion work, the rotate action can promote the working catalog into the base catalog
|
|
79
|
-
- this is intentionally a specialized workflow, not the everyday translation path
|
|
80
|
-
- base vs working catalog design still needs a stricter product pass
|
|
81
|
-
|
|
82
|
-
## Packages used
|
|
83
|
-
|
|
84
|
-
- `svelte`
|
|
85
|
-
UI for the helper widget
|
|
86
|
-
- `@sveltejs/kit`
|
|
87
|
-
Route handler shape and dev/server integration
|
|
88
|
-
- `wuchale`
|
|
89
|
-
Runtime i18n layer and locale switching
|
|
90
|
-
- `gettext-parser`
|
|
91
|
-
Read and write `.po` catalogs
|
|
92
|
-
- `fuse.js`
|
|
93
|
-
Fuzzy lookup for key discovery
|
|
94
|
-
- `esbuild`
|
|
95
|
-
Load `angy.config.ts` in dev and server contexts
|
|
96
|
-
- OpenAI API
|
|
97
|
-
Optional translation suggestions
|
|
98
|
-
|
|
99
|
-
## What problem it solves
|
|
100
|
-
|
|
101
|
-
- Large apps are painful to internationalize late
|
|
102
|
-
- Many strings have weak or missing context
|
|
103
|
-
- Repeated copy appears in many components
|
|
104
|
-
- PO workflows are slow when you must search manually
|
|
105
|
-
- Existing i18n libraries solve extraction/runtime, not translator workflow
|
|
106
|
-
- Teams need a way to select text in the app, inspect context, and commit safely
|
|
107
|
-
|
|
108
|
-
## Why it exists
|
|
109
|
-
|
|
110
|
-
- Plain catalog editing was too slow
|
|
111
|
-
- Context from extraction alone was not enough
|
|
112
|
-
- Similar strings caused ambiguity
|
|
113
|
-
- Repeated strings needed reference-aware lookup
|
|
114
|
-
- Suggestion workflows needed to stay inside the app
|
|
115
|
-
- A dev-only helper reduced friction enough to keep the migration moving
|
|
116
|
-
|
|
117
|
-
## Workflow
|
|
118
|
-
|
|
119
|
-
- Developer runs app in dev
|
|
120
|
-
- Developer mounts `<Angy />` in layout
|
|
121
|
-
- App exports `POST` from the package handler
|
|
122
|
-
- User selects text or uses the helper trigger in the UI
|
|
123
|
-
- Client asks server for PO context and alternatives
|
|
124
|
-
- Server matches best key, expands related entries, and returns alternatives
|
|
125
|
-
- User edits, stages, tabs through unresolved strings, and commits
|
|
126
|
-
- Suggestions can be requested for untranslated strings
|
|
127
|
-
- Commits are written to a draft working catalog
|
|
128
|
-
- Entering `*-working` preview promotes that draft into the runtime working catalog
|
|
31
|
+
- [Showcase and docs](https://walkingissue.github.io/Angy/)
|
|
32
|
+
- [Release notes](./docs/changelog/0.2.19.md)
|
|
129
33
|
|
|
130
34
|
## Integration
|
|
131
35
|
|
|
132
|
-
Use the plugin for
|
|
133
|
-
|
|
134
|
-
The plugin is optional dev glue:
|
|
135
|
-
|
|
136
|
-
- injects route/locale defaults into the client
|
|
137
|
-
- coordinates watcher behavior during catalog writes
|
|
138
|
-
- does not replace the core library/runtime pieces
|
|
36
|
+
Use the plugin for dev glue, mount the component, and expose the server handler.
|
|
139
37
|
|
|
140
38
|
```ts
|
|
141
39
|
// vite.config.ts
|
|
@@ -159,7 +57,7 @@ export default defineAngyConfig({
|
|
|
159
57
|
targetLocale: "en",
|
|
160
58
|
routePath: "/api/translations",
|
|
161
59
|
apiKey: "",
|
|
162
|
-
|
|
60
|
+
suggestionModel: { model: "gpt-4.1-mini" }
|
|
163
61
|
});
|
|
164
62
|
```
|
|
165
63
|
|
|
@@ -168,6 +66,8 @@ export default defineAngyConfig({
|
|
|
168
66
|
<script lang="ts">
|
|
169
67
|
import { dev } from "$app/environment";
|
|
170
68
|
import { Angy } from "@walkinissue/angy";
|
|
69
|
+
|
|
70
|
+
let { children } = $props();
|
|
171
71
|
</script>
|
|
172
72
|
|
|
173
73
|
{#if dev}
|
|
@@ -184,172 +84,46 @@ export { handler as POST } from "@walkinissue/angy/server";
|
|
|
184
84
|
|
|
185
85
|
## Catalog model
|
|
186
86
|
|
|
187
|
-
- `en.po` is
|
|
188
|
-
- `en-working.po` is
|
|
189
|
-
- `en-working.angy-draft.po` is the draft write target
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
-
- Lookup reads working state first
|
|
193
|
-
- Commit validates against base, then writes to the draft working catalog
|
|
194
|
-
- Working preview promotes draft into runtime working
|
|
195
|
-
|
|
196
|
-
## Discovery algorithm
|
|
197
|
-
|
|
198
|
-
- User sends selected text and current route path
|
|
199
|
-
- Server creates lookup variants from the selected text
|
|
200
|
-
- Fuzzy search runs against the effective working view of the catalog
|
|
201
|
-
- Best match must clear a score threshold
|
|
202
|
-
- Top 4 similar direct matches are kept
|
|
203
|
-
- Server infers a page reference like `src/routes/.../+page.svelte`
|
|
204
|
-
- It then pulls entries that share PO references with the best match
|
|
205
|
-
- It then pulls entries whose references look similar to the current route
|
|
206
|
-
- If space remains, it fills with similar unresolved strings
|
|
207
|
-
- It also keeps a smaller translated slice for cohesion
|
|
208
|
-
|
|
209
|
-
## Why the lookup works like this
|
|
210
|
-
|
|
211
|
-
- Direct fuzzy match alone is not enough
|
|
212
|
-
- Repeated UI copy often exists in several components
|
|
213
|
-
- Shared PO references are strong local context
|
|
214
|
-
- Route similarity helps reconstruct page-level context
|
|
215
|
-
- A translated slice helps keep wording consistent
|
|
216
|
-
- An untranslated-heavy pool focuses effort where work remains
|
|
217
|
-
|
|
218
|
-
## Retrieval targets
|
|
219
|
-
|
|
220
|
-
- Up to 300 alternatives
|
|
221
|
-
- Roughly 80% untranslated
|
|
222
|
-
- Roughly 20% already translated
|
|
223
|
-
- Enough translated context for tone and syntax
|
|
224
|
-
- Enough untranslated context for efficient batch work
|
|
225
|
-
|
|
226
|
-
## Suggestions
|
|
227
|
-
|
|
228
|
-
- Suggestions are dev-only
|
|
229
|
-
- Server-side request to OpenAI
|
|
230
|
-
- Prompt aims to preserve placeholders, markup, casing, and product terminology
|
|
231
|
-
- Existing translations are used as style anchors
|
|
232
|
-
- Suggestions are cached client-side to avoid repeat requests
|
|
233
|
-
- Built-in suggestions are disabled when `apiKey` is empty
|
|
234
|
-
- Consumers can replace the suggestion pipeline with `suggestionProvider`
|
|
87
|
+
- `en.po` is base
|
|
88
|
+
- `en-working.po` is runtime working state
|
|
89
|
+
- `en-working.angy-draft.po` is the draft write target
|
|
90
|
+
- commits write to draft first
|
|
91
|
+
- entering `*-working` preview promotes draft into runtime working
|
|
235
92
|
|
|
236
93
|
## Config
|
|
237
94
|
|
|
238
|
-
| Key | Required |
|
|
239
|
-
| --- | --- | --- |
|
|
240
|
-
| `basePoPath` | Yes |
|
|
241
|
-
| `workingPoPath` | Yes |
|
|
242
|
-
| `sourceLocale` | Yes |
|
|
243
|
-
| `targetLocale` | Yes |
|
|
244
|
-
| `routePath` | No | `/api/translations` |
|
|
245
|
-
| `apiKey` |
|
|
246
|
-
| `systemMessage` | No |
|
|
247
|
-
| `suggestionModel` | No | `gpt-4.1-mini` |
|
|
248
|
-
| `watchIgnore` | No |
|
|
249
|
-
| `suggestionProvider` | No |
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- The server route path is defined by the consumer app
|
|
254
|
-
- The default route path is `/api/translations`
|
|
255
|
-
- `routePath` lives in `angy.config.ts`
|
|
256
|
-
- The `Angy` `endpoint` prop is optional
|
|
257
|
-
- Use the prop only if you want to override `routePath` per instance
|
|
258
|
-
|
|
259
|
-
## Wuchale Runtime
|
|
260
|
-
|
|
261
|
-
Angy does not replace Wuchale's runtime setup. The host app still needs to:
|
|
262
|
-
|
|
263
|
-
- import the generated client loader
|
|
264
|
-
- preload server catalogs
|
|
265
|
-
- await `loadLocales(...)`
|
|
266
|
-
- wrap requests with `runWithLocale(...)`
|
|
267
|
-
|
|
268
|
-
If that wiring is missing, locale rotation can appear broken and Wuchale may warn:
|
|
269
|
-
|
|
270
|
-
```text
|
|
271
|
-
Catalog for 'main.main' not found.
|
|
272
|
-
Either 'runWithLocale' was not called or the environment has a problem.
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
See [docs/wuchale-runtime.md](./docs/wuchale-runtime.md) for a working pattern, including the localStorage-and-cookie sync used in the smoke test.
|
|
95
|
+
| Key | Required | Notes |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `basePoPath` | Yes | Base catalog path |
|
|
98
|
+
| `workingPoPath` | Yes | Working catalog path |
|
|
99
|
+
| `sourceLocale` | Yes | Source language for suggestions |
|
|
100
|
+
| `targetLocale` | Yes | Target language for suggestions |
|
|
101
|
+
| `routePath` | No | Defaults to `/api/translations` |
|
|
102
|
+
| `apiKey` | No | Empty string disables built-in suggestions |
|
|
103
|
+
| `systemMessage` | No | Custom suggestion prompt |
|
|
104
|
+
| `suggestionModel` | No | Object config, defaults to `{ model: "gpt-4.1-mini" }` |
|
|
105
|
+
| `watchIgnore` | No | Extra Vite watch ignore patterns |
|
|
106
|
+
| `suggestionProvider` | No | Custom suggestion pipeline |
|
|
107
|
+
|
|
108
|
+
Reasoning is validated per model. Non-reasoning models like `gpt-4.1-mini` do not accept it.
|
|
276
109
|
|
|
277
110
|
## Frequent issues
|
|
278
111
|
|
|
279
|
-
- Locale
|
|
280
|
-
- Wuchale is
|
|
281
|
-
-
|
|
282
|
-
-
|
|
112
|
+
- Locale changes in Angy, but the page does not rerender
|
|
113
|
+
- Wuchale usually is not fully bootstrapped in the host app.
|
|
114
|
+
- `*-working` is visible, but commit is disabled
|
|
115
|
+
- Working locale is preview-only by design.
|
|
283
116
|
- Rotation shows a warning modal
|
|
284
|
-
-
|
|
117
|
+
- That is a preview of what rotation will promote into base.
|
|
285
118
|
- Angy refuses to operate with a catalog integrity error
|
|
286
|
-
-
|
|
287
|
-
- Commit succeeded, but the helper still looks unsure
|
|
288
|
-
- Angy now keeps local drafts until the server truth matches the committed value. If Wuchale/Vite reloads at a bad moment, your staged edits stay cached instead of being silently discarded.
|
|
289
|
-
|
|
290
|
-
## Custom suggestion provider
|
|
291
|
-
|
|
292
|
-
Use `suggestionProvider` if you want your own AI pipeline.
|
|
293
|
-
|
|
294
|
-
Input:
|
|
295
|
-
|
|
296
|
-
- `context`
|
|
297
|
-
- `items`
|
|
298
|
-
- `sourceLocale`
|
|
299
|
-
- `targetLocale`
|
|
300
|
-
- `systemMessage`
|
|
301
|
-
- `model`
|
|
302
|
-
- `apiKey`
|
|
303
|
-
|
|
304
|
-
Return:
|
|
305
|
-
|
|
306
|
-
- `Array<{ msgid: string; msgctxt: string | null; suggestion: string }>`
|
|
307
|
-
|
|
308
|
-
Example:
|
|
309
|
-
|
|
310
|
-
```ts
|
|
311
|
-
import { defineAngyConfig, type SuggestionProvider } from "@walkinissue/angy/server";
|
|
312
|
-
|
|
313
|
-
const suggestionProvider: SuggestionProvider = async ({ items }) => {
|
|
314
|
-
return items.map((item) => ({
|
|
315
|
-
msgid: item.msgid,
|
|
316
|
-
msgctxt: item.msgctxt,
|
|
317
|
-
suggestion: `TODO: ${item.msgid}`
|
|
318
|
-
}));
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
export default defineAngyConfig({
|
|
322
|
-
basePoPath: "./src/locales/en.po",
|
|
323
|
-
workingPoPath: "./src/locales/en-working.po",
|
|
324
|
-
sourceLocale: "sv",
|
|
325
|
-
targetLocale: "en",
|
|
326
|
-
routePath: "/api/translations",
|
|
327
|
-
apiKey: "",
|
|
328
|
-
suggestionProvider
|
|
329
|
-
});
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## Exports
|
|
333
|
-
|
|
334
|
-
- `Angy` from `@walkinissue/angy`
|
|
335
|
-
- `handler` and `defineAngyConfig` from `@walkinissue/angy/server`
|
|
336
|
-
- `angy` from `@walkinissue/angy/plugin`
|
|
119
|
+
- Regenerate or replace the working catalog.
|
|
337
120
|
|
|
338
|
-
##
|
|
121
|
+
## Docs
|
|
339
122
|
|
|
340
|
-
-
|
|
341
|
-
-
|
|
342
|
-
-
|
|
343
|
-
- The default export surface is intentionally small: plugin, component, config helper, and server handler.
|
|
123
|
+
- [Release notes](./docs/changelog/0.2.19.md)
|
|
124
|
+
- [Roadmap](./docs/roadmap.md)
|
|
125
|
+
- [Wuchale runtime wiring](./docs/wuchale-runtime.md)
|
|
344
126
|
|
|
345
|
-
##
|
|
127
|
+
## License
|
|
346
128
|
|
|
347
|
-
|
|
348
|
-
- Support multiple catalogs
|
|
349
|
-
- Support multiple translations from a single source locale
|
|
350
|
-
- Add a compact focus mode so the helper can hide dead detail, keep a smaller matched-key summary, and only show the active alternative by default
|
|
351
|
-
- Revisit how commit feedback is surfaced so successful or failed commits stay visible without making the helper flow awkward
|
|
352
|
-
- Improve cross-page reference browsing, likely through a popup/details surface instead of always-visible reference noise
|
|
353
|
-
- Make working-locale rotation trustworthy and visually explicit so users can tell when the app is actually rendering the working catalog
|
|
354
|
-
- Harden the base-vs-working catalog design and decide how much of promotion/rotation should be exposed in the default UX
|
|
355
|
-
- This seems trivially implemented with the current setup
|
|
129
|
+
MIT
|
package/dist/plugin.js
CHANGED
|
@@ -11,6 +11,30 @@ var CONFIG_FILENAMES = [
|
|
|
11
11
|
"angy.config.mjs",
|
|
12
12
|
"angy.config.cjs"
|
|
13
13
|
];
|
|
14
|
+
var NON_REASONING_SUGGESTION_MODELS = [
|
|
15
|
+
"gpt-4.1",
|
|
16
|
+
"gpt-4.1-mini",
|
|
17
|
+
"gpt-4.1-nano"
|
|
18
|
+
];
|
|
19
|
+
var GPT54_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh"];
|
|
20
|
+
var GPT51_REASONING_EFFORTS = ["none", "low", "medium", "high"];
|
|
21
|
+
var GPT5_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
22
|
+
var GPT5_PRO_REASONING_EFFORTS = ["high"];
|
|
23
|
+
var GPT5X_PRO_REASONING_EFFORTS = ["medium", "high", "xhigh"];
|
|
24
|
+
var REASONING_EFFORTS_BY_MODEL = {
|
|
25
|
+
"gpt-5.4": GPT54_REASONING_EFFORTS,
|
|
26
|
+
"gpt-5.4-mini": GPT54_REASONING_EFFORTS,
|
|
27
|
+
"gpt-5.4-nano": GPT54_REASONING_EFFORTS,
|
|
28
|
+
"gpt-5.2": GPT54_REASONING_EFFORTS,
|
|
29
|
+
"gpt-5.1": GPT51_REASONING_EFFORTS,
|
|
30
|
+
"gpt-5": GPT5_REASONING_EFFORTS,
|
|
31
|
+
"gpt-5-pro": GPT5_PRO_REASONING_EFFORTS,
|
|
32
|
+
"gpt-5.2-pro": GPT5X_PRO_REASONING_EFFORTS,
|
|
33
|
+
"gpt-5.4-pro": GPT5X_PRO_REASONING_EFFORTS
|
|
34
|
+
};
|
|
35
|
+
var DEFAULT_SUGGESTION_MODEL = {
|
|
36
|
+
model: "gpt-4.1-mini"
|
|
37
|
+
};
|
|
14
38
|
function buildDefaultSystemMessage(sourceLocale, targetLocale) {
|
|
15
39
|
return `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;
|
|
16
40
|
}
|
|
@@ -37,6 +61,51 @@ function validateSuggestionProvider(suggestionProvider) {
|
|
|
37
61
|
throw new Error(`[angy] suggestionProvider must be a function.`);
|
|
38
62
|
}
|
|
39
63
|
}
|
|
64
|
+
function isNonReasoningSuggestionModel(model) {
|
|
65
|
+
return NON_REASONING_SUGGESTION_MODELS.includes(model);
|
|
66
|
+
}
|
|
67
|
+
function isSupportedSuggestionModel(model) {
|
|
68
|
+
return isNonReasoningSuggestionModel(model) || Object.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model);
|
|
69
|
+
}
|
|
70
|
+
function normalizeSuggestionModelConfig(input) {
|
|
71
|
+
const suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;
|
|
72
|
+
if (typeof suggestionModel !== "object" || suggestionModel == null || Array.isArray(suggestionModel)) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`[angy] suggestionModel must be an object like { model: "gpt-4.1-mini" }.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (typeof suggestionModel.model !== "string" || !suggestionModel.model.trim()) {
|
|
78
|
+
throw new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);
|
|
79
|
+
}
|
|
80
|
+
if (!isSupportedSuggestionModel(suggestionModel.model)) {
|
|
81
|
+
throw new Error(`[angy] Unsupported suggestionModel.model "${suggestionModel.model}".`);
|
|
82
|
+
}
|
|
83
|
+
if (isNonReasoningSuggestionModel(suggestionModel.model)) {
|
|
84
|
+
if (!("reasoning" in suggestionModel) || suggestionModel.reasoning === void 0 || suggestionModel.reasoning === null) {
|
|
85
|
+
return { model: suggestionModel.model, reasoning: null };
|
|
86
|
+
}
|
|
87
|
+
throw new Error(
|
|
88
|
+
`[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];
|
|
92
|
+
const reasoning = suggestionModel.reasoning;
|
|
93
|
+
if (reasoning == null) {
|
|
94
|
+
if (suggestionModel.model === "gpt-5-pro") {
|
|
95
|
+
return { model: suggestionModel.model, reasoning: "high" };
|
|
96
|
+
}
|
|
97
|
+
return { model: suggestionModel.model };
|
|
98
|
+
}
|
|
99
|
+
if (!allowedReasoning.includes(reasoning)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`[angy] suggestionModel.reasoning "${reasoning}" is not supported for ${suggestionModel.model}.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
model: suggestionModel.model,
|
|
106
|
+
reasoning
|
|
107
|
+
};
|
|
108
|
+
}
|
|
40
109
|
var config = {
|
|
41
110
|
basePoPath: resolve(__dirname, "../../locales/en.po"),
|
|
42
111
|
workingPoPath: resolve(__dirname, "../../locales/en-working.po"),
|
|
@@ -45,7 +114,7 @@ var config = {
|
|
|
45
114
|
routePath: "/api/translations",
|
|
46
115
|
apiKey: "",
|
|
47
116
|
systemMessage: buildDefaultSystemMessage("sv", "en"),
|
|
48
|
-
suggestionModel:
|
|
117
|
+
suggestionModel: DEFAULT_SUGGESTION_MODEL,
|
|
49
118
|
watchIgnore: ["**/en-working.po"]
|
|
50
119
|
};
|
|
51
120
|
var loadedConfigRoot = null;
|
|
@@ -149,7 +218,7 @@ function completeAngyConfig(input) {
|
|
|
149
218
|
routePath: input.routePath ?? "/api/translations",
|
|
150
219
|
apiKey: input.apiKey ?? "",
|
|
151
220
|
systemMessage: input.systemMessage ?? buildDefaultSystemMessage(input.sourceLocale, input.targetLocale),
|
|
152
|
-
suggestionModel: input.suggestionModel
|
|
221
|
+
suggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),
|
|
153
222
|
watchIgnore: input.watchIgnore ?? ["**/en-working.po"],
|
|
154
223
|
suggestionProvider: input.suggestionProvider
|
|
155
224
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type SuggestionProviderInput = {
|
|
|
56
56
|
sourceLocale: string;
|
|
57
57
|
targetLocale: string;
|
|
58
58
|
systemMessage: string;
|
|
59
|
-
|
|
59
|
+
suggestionModel: SuggestionModelConfig;
|
|
60
60
|
apiKey: string | undefined;
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -72,11 +72,36 @@ export type AngyConfigInput = {
|
|
|
72
72
|
routePath?: string;
|
|
73
73
|
apiKey?: string;
|
|
74
74
|
systemMessage?: string;
|
|
75
|
-
suggestionModel?:
|
|
75
|
+
suggestionModel?: SuggestionModelConfig;
|
|
76
76
|
watchIgnore?: string[];
|
|
77
77
|
suggestionProvider?: SuggestionProvider;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
export type SuggestionModelConfig =
|
|
81
|
+
| { model: "gpt-4.1"; reasoning?: null }
|
|
82
|
+
| { model: "gpt-4.1-mini"; reasoning?: null }
|
|
83
|
+
| { model: "gpt-4.1-nano"; reasoning?: null }
|
|
84
|
+
| {
|
|
85
|
+
model: "gpt-5.4" | "gpt-5.4-mini" | "gpt-5.4-nano" | "gpt-5.2";
|
|
86
|
+
reasoning?: "none" | "low" | "medium" | "high" | "xhigh";
|
|
87
|
+
}
|
|
88
|
+
| {
|
|
89
|
+
model: "gpt-5.1";
|
|
90
|
+
reasoning?: "none" | "low" | "medium" | "high";
|
|
91
|
+
}
|
|
92
|
+
| {
|
|
93
|
+
model: "gpt-5";
|
|
94
|
+
reasoning?: "minimal" | "low" | "medium" | "high";
|
|
95
|
+
}
|
|
96
|
+
| {
|
|
97
|
+
model: "gpt-5-pro";
|
|
98
|
+
reasoning?: "high";
|
|
99
|
+
}
|
|
100
|
+
| {
|
|
101
|
+
model: "gpt-5.2-pro" | "gpt-5.4-pro";
|
|
102
|
+
reasoning?: "medium" | "high" | "xhigh";
|
|
103
|
+
};
|
|
104
|
+
|
|
80
105
|
export type AngyResolvedConfig = {
|
|
81
106
|
basePoPath: string;
|
|
82
107
|
workingPoPath: string;
|
|
@@ -85,7 +110,7 @@ export type AngyResolvedConfig = {
|
|
|
85
110
|
routePath: string;
|
|
86
111
|
apiKey: string;
|
|
87
112
|
systemMessage: string;
|
|
88
|
-
suggestionModel:
|
|
113
|
+
suggestionModel: SuggestionModelConfig;
|
|
89
114
|
watchIgnore: string[];
|
|
90
115
|
suggestionProvider?: SuggestionProvider;
|
|
91
116
|
};
|
package/dist/server.js
CHANGED
|
@@ -25,6 +25,30 @@ var CONFIG_FILENAMES = [
|
|
|
25
25
|
"angy.config.mjs",
|
|
26
26
|
"angy.config.cjs"
|
|
27
27
|
];
|
|
28
|
+
var NON_REASONING_SUGGESTION_MODELS = [
|
|
29
|
+
"gpt-4.1",
|
|
30
|
+
"gpt-4.1-mini",
|
|
31
|
+
"gpt-4.1-nano"
|
|
32
|
+
];
|
|
33
|
+
var GPT54_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh"];
|
|
34
|
+
var GPT51_REASONING_EFFORTS = ["none", "low", "medium", "high"];
|
|
35
|
+
var GPT5_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
36
|
+
var GPT5_PRO_REASONING_EFFORTS = ["high"];
|
|
37
|
+
var GPT5X_PRO_REASONING_EFFORTS = ["medium", "high", "xhigh"];
|
|
38
|
+
var REASONING_EFFORTS_BY_MODEL = {
|
|
39
|
+
"gpt-5.4": GPT54_REASONING_EFFORTS,
|
|
40
|
+
"gpt-5.4-mini": GPT54_REASONING_EFFORTS,
|
|
41
|
+
"gpt-5.4-nano": GPT54_REASONING_EFFORTS,
|
|
42
|
+
"gpt-5.2": GPT54_REASONING_EFFORTS,
|
|
43
|
+
"gpt-5.1": GPT51_REASONING_EFFORTS,
|
|
44
|
+
"gpt-5": GPT5_REASONING_EFFORTS,
|
|
45
|
+
"gpt-5-pro": GPT5_PRO_REASONING_EFFORTS,
|
|
46
|
+
"gpt-5.2-pro": GPT5X_PRO_REASONING_EFFORTS,
|
|
47
|
+
"gpt-5.4-pro": GPT5X_PRO_REASONING_EFFORTS
|
|
48
|
+
};
|
|
49
|
+
var DEFAULT_SUGGESTION_MODEL = {
|
|
50
|
+
model: "gpt-4.1-mini"
|
|
51
|
+
};
|
|
28
52
|
function buildDefaultSystemMessage(sourceLocale, targetLocale) {
|
|
29
53
|
return `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;
|
|
30
54
|
}
|
|
@@ -51,6 +75,51 @@ function validateSuggestionProvider(suggestionProvider) {
|
|
|
51
75
|
throw new Error(`[angy] suggestionProvider must be a function.`);
|
|
52
76
|
}
|
|
53
77
|
}
|
|
78
|
+
function isNonReasoningSuggestionModel(model) {
|
|
79
|
+
return NON_REASONING_SUGGESTION_MODELS.includes(model);
|
|
80
|
+
}
|
|
81
|
+
function isSupportedSuggestionModel(model) {
|
|
82
|
+
return isNonReasoningSuggestionModel(model) || Object.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model);
|
|
83
|
+
}
|
|
84
|
+
function normalizeSuggestionModelConfig(input) {
|
|
85
|
+
const suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;
|
|
86
|
+
if (typeof suggestionModel !== "object" || suggestionModel == null || Array.isArray(suggestionModel)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`[angy] suggestionModel must be an object like { model: "gpt-4.1-mini" }.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (typeof suggestionModel.model !== "string" || !suggestionModel.model.trim()) {
|
|
92
|
+
throw new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);
|
|
93
|
+
}
|
|
94
|
+
if (!isSupportedSuggestionModel(suggestionModel.model)) {
|
|
95
|
+
throw new Error(`[angy] Unsupported suggestionModel.model "${suggestionModel.model}".`);
|
|
96
|
+
}
|
|
97
|
+
if (isNonReasoningSuggestionModel(suggestionModel.model)) {
|
|
98
|
+
if (!("reasoning" in suggestionModel) || suggestionModel.reasoning === void 0 || suggestionModel.reasoning === null) {
|
|
99
|
+
return { model: suggestionModel.model, reasoning: null };
|
|
100
|
+
}
|
|
101
|
+
throw new Error(
|
|
102
|
+
`[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];
|
|
106
|
+
const reasoning = suggestionModel.reasoning;
|
|
107
|
+
if (reasoning == null) {
|
|
108
|
+
if (suggestionModel.model === "gpt-5-pro") {
|
|
109
|
+
return { model: suggestionModel.model, reasoning: "high" };
|
|
110
|
+
}
|
|
111
|
+
return { model: suggestionModel.model };
|
|
112
|
+
}
|
|
113
|
+
if (!allowedReasoning.includes(reasoning)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`[angy] suggestionModel.reasoning "${reasoning}" is not supported for ${suggestionModel.model}.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
model: suggestionModel.model,
|
|
120
|
+
reasoning
|
|
121
|
+
};
|
|
122
|
+
}
|
|
54
123
|
var config = {
|
|
55
124
|
basePoPath: resolve(__dirname, "../../locales/en.po"),
|
|
56
125
|
workingPoPath: resolve(__dirname, "../../locales/en-working.po"),
|
|
@@ -59,7 +128,7 @@ var config = {
|
|
|
59
128
|
routePath: "/api/translations",
|
|
60
129
|
apiKey: "",
|
|
61
130
|
systemMessage: buildDefaultSystemMessage("sv", "en"),
|
|
62
|
-
suggestionModel:
|
|
131
|
+
suggestionModel: DEFAULT_SUGGESTION_MODEL,
|
|
63
132
|
watchIgnore: ["**/en-working.po"]
|
|
64
133
|
};
|
|
65
134
|
var loadedConfigRoot = null;
|
|
@@ -169,7 +238,7 @@ function completeAngyConfig(input) {
|
|
|
169
238
|
routePath: input.routePath ?? "/api/translations",
|
|
170
239
|
apiKey: input.apiKey ?? "",
|
|
171
240
|
systemMessage: input.systemMessage ?? buildDefaultSystemMessage(input.sourceLocale, input.targetLocale),
|
|
172
|
-
suggestionModel: input.suggestionModel
|
|
241
|
+
suggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),
|
|
173
242
|
watchIgnore: input.watchIgnore ?? ["**/en-working.po"],
|
|
174
243
|
suggestionProvider: input.suggestionProvider
|
|
175
244
|
};
|
|
@@ -1189,6 +1258,58 @@ function parseSuggestions(responseText) {
|
|
|
1189
1258
|
return [];
|
|
1190
1259
|
}
|
|
1191
1260
|
}
|
|
1261
|
+
function buildSuggestionRequestBody({
|
|
1262
|
+
context,
|
|
1263
|
+
items,
|
|
1264
|
+
modelConfig,
|
|
1265
|
+
systemMessage
|
|
1266
|
+
}) {
|
|
1267
|
+
const body = {
|
|
1268
|
+
model: modelConfig.model,
|
|
1269
|
+
input: [
|
|
1270
|
+
{
|
|
1271
|
+
role: "system",
|
|
1272
|
+
content: [{ type: "input_text", text: systemMessage }]
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
role: "user",
|
|
1276
|
+
content: [{ type: "input_text", text: buildUserMessage(context, items) }]
|
|
1277
|
+
}
|
|
1278
|
+
],
|
|
1279
|
+
text: {
|
|
1280
|
+
format: {
|
|
1281
|
+
type: "json_schema",
|
|
1282
|
+
name: "translation_suggestions",
|
|
1283
|
+
schema: {
|
|
1284
|
+
type: "object",
|
|
1285
|
+
additionalProperties: false,
|
|
1286
|
+
properties: {
|
|
1287
|
+
items: {
|
|
1288
|
+
type: "array",
|
|
1289
|
+
items: {
|
|
1290
|
+
type: "object",
|
|
1291
|
+
additionalProperties: false,
|
|
1292
|
+
properties: {
|
|
1293
|
+
msgid: { type: "string" },
|
|
1294
|
+
msgctxt: { type: ["string", "null"] },
|
|
1295
|
+
suggestion: { type: "string" }
|
|
1296
|
+
},
|
|
1297
|
+
required: ["msgid", "msgctxt", "suggestion"]
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
required: ["items"]
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
if (modelConfig.reasoning && modelConfig.reasoning !== "none") {
|
|
1307
|
+
body.reasoning = {
|
|
1308
|
+
effort: modelConfig.reasoning
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
return body;
|
|
1312
|
+
}
|
|
1192
1313
|
async function handleSuggestions(request) {
|
|
1193
1314
|
const {
|
|
1194
1315
|
apiKey,
|
|
@@ -1217,7 +1338,7 @@ async function handleSuggestions(request) {
|
|
|
1217
1338
|
sourceLocale,
|
|
1218
1339
|
targetLocale,
|
|
1219
1340
|
systemMessage,
|
|
1220
|
-
|
|
1341
|
+
suggestionModel,
|
|
1221
1342
|
apiKey
|
|
1222
1343
|
});
|
|
1223
1344
|
return json3({
|
|
@@ -1235,7 +1356,8 @@ async function handleSuggestions(request) {
|
|
|
1235
1356
|
}
|
|
1236
1357
|
try {
|
|
1237
1358
|
console.info("[angy] Suggestion request starting.", {
|
|
1238
|
-
model: suggestionModel,
|
|
1359
|
+
model: suggestionModel.model,
|
|
1360
|
+
reasoning: suggestionModel.reasoning ?? null,
|
|
1239
1361
|
sourceLocale,
|
|
1240
1362
|
targetLocale,
|
|
1241
1363
|
itemCount: items.length,
|
|
@@ -1247,48 +1369,14 @@ async function handleSuggestions(request) {
|
|
|
1247
1369
|
Authorization: `Bearer ${apiKey}`,
|
|
1248
1370
|
"content-type": "application/json"
|
|
1249
1371
|
},
|
|
1250
|
-
body: JSON.stringify(
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
content: [{ type: "input_text", text: systemMessage }]
|
|
1259
|
-
},
|
|
1260
|
-
{
|
|
1261
|
-
role: "user",
|
|
1262
|
-
content: [{ type: "input_text", text: buildUserMessage(context, items) }]
|
|
1263
|
-
}
|
|
1264
|
-
],
|
|
1265
|
-
text: {
|
|
1266
|
-
format: {
|
|
1267
|
-
type: "json_schema",
|
|
1268
|
-
name: "translation_suggestions",
|
|
1269
|
-
schema: {
|
|
1270
|
-
type: "object",
|
|
1271
|
-
additionalProperties: false,
|
|
1272
|
-
properties: {
|
|
1273
|
-
items: {
|
|
1274
|
-
type: "array",
|
|
1275
|
-
items: {
|
|
1276
|
-
type: "object",
|
|
1277
|
-
additionalProperties: false,
|
|
1278
|
-
properties: {
|
|
1279
|
-
msgid: { type: "string" },
|
|
1280
|
-
msgctxt: { type: ["string", "null"] },
|
|
1281
|
-
suggestion: { type: "string" }
|
|
1282
|
-
},
|
|
1283
|
-
required: ["msgid", "msgctxt", "suggestion"]
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
},
|
|
1287
|
-
required: ["items"]
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
})
|
|
1372
|
+
body: JSON.stringify(
|
|
1373
|
+
buildSuggestionRequestBody({
|
|
1374
|
+
context,
|
|
1375
|
+
items,
|
|
1376
|
+
modelConfig: suggestionModel,
|
|
1377
|
+
systemMessage
|
|
1378
|
+
})
|
|
1379
|
+
)
|
|
1292
1380
|
});
|
|
1293
1381
|
if (!response.ok) {
|
|
1294
1382
|
const errorBody = await response.text().catch(() => "");
|
|
@@ -1369,12 +1457,14 @@ function createAngyHandler(options) {
|
|
|
1369
1457
|
var handler = createAngyHandler();
|
|
1370
1458
|
export {
|
|
1371
1459
|
CatalogIntegrityError,
|
|
1460
|
+
buildSuggestionRequestBody,
|
|
1372
1461
|
collectCatalogIntegrityIssues,
|
|
1373
1462
|
collectRotationImpact,
|
|
1374
1463
|
defineAngyConfig,
|
|
1375
1464
|
getRotationPreflight,
|
|
1376
1465
|
handleTranslationRequest,
|
|
1377
1466
|
handler,
|
|
1467
|
+
normalizeSuggestionModelConfig,
|
|
1378
1468
|
readCatalogPair,
|
|
1379
1469
|
resolveTranslationState
|
|
1380
1470
|
};
|
package/dist/server.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/lib/server/route.ts", "../src/lib/server/commit.ts", "../src/lib/server/catalog.ts", "../src/lib/server/config.ts", "../src/lib/server/context.ts", "../src/lib/server/suggestions.ts"],
|
|
4
|
-
"sourcesContent": ["import { json, type RequestHandler } from \"@sveltejs/kit\";\nimport {\n\thandleCommit,\n\thandleCommitBatch,\n\thandlePromoteWorkingPreview,\n\thandleRotateCatalogs,\n\thandleRotatePreflight\n} from \"./commit.ts\";\nimport {\n\tconfigureTranslationHelper,\n\tloadAngyConfigFromRoot,\n\tnormalizeTranslationHelperConfig,\n\ttype TranslationHelperConfig\n} from \"./config.ts\";\nimport { handleContext } from \"./context.ts\";\nimport { handleSuggestions } from \"./suggestions.ts\";\n\nexport async function handleTranslationRequest(\n\trequest: Request,\n\turl: URL,\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig>; dev?: boolean }\n) {\n\tconst devOnly = options?.devOnly ?? true;\n\tconst runtimeDev =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\ttypeof import.meta.env !== \"undefined\" &&\n\t\timport.meta.env.DEV === true;\n\tconst isDev = options?.dev ?? runtimeDev;\n\n\tif (devOnly && !isDev) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Disabled outside dev\"\n\t\t\t},\n\t\t\t{ status: 404 }\n\t\t);\n\t}\n\n\tif (options?.config) {\n\t\tconfigureTranslationHelper(normalizeTranslationHelperConfig(process.cwd(), options.config));\n\t} else {\n\t\tawait loadAngyConfigFromRoot(process.cwd());\n\t}\n\n\tconst intent = url.searchParams.get(\"intent\") ?? \"commit\";\n\n\tif (intent === \"commit-batch\") {\n\t\treturn handleCommitBatch(request);\n\t}\n\n\tif (intent === \"context\") {\n\t\treturn handleContext(request);\n\t}\n\n\tif (intent === \"suggestions\") {\n\t\treturn handleSuggestions(request);\n\t}\n\n\tif (intent === \"rotate-catalogs\") {\n\t\treturn handleRotateCatalogs(request);\n\t}\n\n\tif (intent === \"rotate-preflight\") {\n\t\treturn handleRotatePreflight();\n\t}\n\n\tif (intent === \"promote-working-preview\") {\n\t\treturn handlePromoteWorkingPreview();\n\t}\n\n\treturn handleCommit(request);\n}\n\nfunction createAngyHandler(\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig> }\n): RequestHandler {\n\treturn async ({ request, url }) => handleTranslationRequest(request, url, options);\n}\n\nexport const handler = createAngyHandler();\n\nexport {\n\tdefineAngyConfig,\n\ttype AngyConfigInput,\n\ttype SuggestionProvider,\n\ttype SuggestionProviderInput,\n\ttype SuggestionRequestItem,\n\ttype SuggestionResponseItem\n} from \"./config.ts\";\nexport {\n\tCatalogIntegrityError,\n\tcollectCatalogIntegrityIssues,\n\tcollectRotationImpact,\n\tgetRotationPreflight,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\n", "import { json } from \"@sveltejs/kit\";\nimport { basename } from \"node:path\";\nimport {\n\tCatalogIntegrityError,\n\tgetRotationPreflight,\n\tensureWorkingCatalog,\n\tpromoteWorkingDraftToRuntime,\n\treadCatalogPair,\n\treadParsedCatalog,\n\tremoveFuzzyFlag,\n\trotateCatalogs,\n\truntimeTranslations,\n\twriteWorkingCatalog\n} from \"./catalog.ts\";\nimport type { CommitBatchItem, PoTranslationEntry } from \"./types.ts\";\nimport { getTranslationHelperConfig, inferLocaleFromCatalogPath } from \"./config.ts\";\n\r\nfunction runtimeKey(msgid: string, msgctxt: string | null) {\r\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\r\n}\r\n\r\nexport async function writeTranslationToWorkingCatalog(\n\tresolvedMsgid: string,\n\tresolvedMsgctxt: string | null,\n\ttranslationValue: string\n) {\n\tawait ensureWorkingCatalog();\n\n\tawait readCatalogPair({ ensureWorking: true });\n\n\tconst [baseParsed, workingParsed] = await Promise.all([readParsedCatalog(\"base\"), readParsedCatalog(\"working\")]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\treturn { ok: false as const, error: \"Unable to load catalogs\" };\n\t}\n\r\n\tconst baseTranslations = baseParsed.translations ?? {};\r\n\tconst workingTranslations = workingParsed.translations ?? {};\r\n\r\n\tconst ctxKey = resolvedMsgctxt ?? \"\";\r\n\tconst baseGroup = baseTranslations[ctxKey];\r\n\tif (!baseGroup) {\r\n\t\treturn { ok: false as const, error: \"Context group not found in base catalog\" };\r\n\t}\r\n\r\n\tconst baseEntry = baseGroup[resolvedMsgid] as PoTranslationEntry | undefined;\r\n\tif (!baseEntry) {\r\n\t\treturn { ok: false as const, error: \"Target msgid not found in base catalog\" };\r\n\t}\r\n\r\n\tconst workingGroup = (workingTranslations[ctxKey] ??= {});\r\n\tconst entry = ((workingGroup[resolvedMsgid] as PoTranslationEntry | undefined) ??=\r\n\t\tcloneEntryForWorking(baseEntry, resolvedMsgid, resolvedMsgctxt));\r\n\r\n\tentry.msgstr = [translationValue];\r\n\tentry.comments ??= {};\r\n\tentry.comments.flag = removeFuzzyFlag(entry.comments.flag);\r\n\r\n\tawait writeWorkingCatalog(workingParsed);\r\n\r\n\truntimeTranslations.set(runtimeKey(resolvedMsgid, resolvedMsgctxt), translationValue);\n\n\tconst workingCatalogName = inferLocaleFromCatalogPath(getTranslationHelperConfig().workingPoPath) ?? \"working\";\n\n\treturn {\n\t\tok: true as const,\n\t\tmsgid: resolvedMsgid,\n\t\tmsgctxt: resolvedMsgctxt,\n\t\tworkingCatalog: workingCatalogName\n\t};\n}\n\r\nfunction cloneEntryForWorking(\r\n\tbaseEntry: PoTranslationEntry,\r\n\tmsgid: string,\r\n\tmsgctxt: string | null\r\n): PoTranslationEntry {\r\n\treturn {\r\n\t\tmsgid,\r\n\t\tmsgctxt: msgctxt ?? undefined,\r\n\t\tmsgid_plural: baseEntry.msgid_plural,\r\n\t\tmsgstr: Array.isArray(baseEntry.msgstr) ? [...baseEntry.msgstr] : [\"\"],\r\n\t\tobsolete: Boolean(baseEntry.obsolete),\r\n\t\tcomments: baseEntry.comments\r\n\t\t\t? {\r\n\t\t\t\t\treference: baseEntry.comments.reference,\r\n\t\t\t\t\textracted: baseEntry.comments.extracted,\r\n\t\t\t\t\tflag: baseEntry.comments.flag,\r\n\t\t\t\t\tprevious: baseEntry.comments.previous\r\n\t\t\t\t}\r\n\t\t\t: undefined\r\n\t};\r\n}\r\n\r\nexport async function handleCommitBatch(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst items = Array.isArray(data?.items) ? (data.items as CommitBatchItem[]) : [];\n\r\n\tif (!items.length) {\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"No items to commit\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\n\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tconst results: Array<{ msgid: string; msgctxt: string | null; ok: boolean; error?: string }> = [];\n\r\n\tfor (const item of items) {\r\n\t\tconst resolvedMsgid = item.resolvedMsgid?.trim();\r\n\t\tconst resolvedMsgctxt =\r\n\t\t\titem.resolvedMsgctxt && item.resolvedMsgctxt.trim().length > 0\r\n\t\t\t\t? item.resolvedMsgctxt.trim()\r\n\t\t\t\t: null;\r\n\t\tconst translationValue = item.translationValue?.trim();\r\n\r\n\t\tif (!resolvedMsgid || !translationValue) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid ?? \"\",\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst result = await writeTranslationToWorkingCatalog(\r\n\t\t\tresolvedMsgid,\r\n\t\t\tresolvedMsgctxt,\r\n\t\t\ttranslationValue\r\n\t\t);\r\n\r\n\t\tif (!result.ok) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid,\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tresults.push({\r\n\t\t\tmsgid: resolvedMsgid,\r\n\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\tok: true\r\n\t\t});\r\n\t}\r\n\r\n\tconst failed = results.filter((item) => !item.ok);\r\n\tif (failed.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Some translations failed to commit\",\r\n\t\t\t\tresults\r\n\t\t\t},\r\n\t\t\t{ status: 207 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: `Committed ${results.length} translations to Angy draft working catalog`,\n\t\tresults\n\t});\n}\n\r\nexport async function handleCommit(request: Request) {\n\tconst data = await request.formData();\r\n\tconst resolvedMsgid = data.get(\"resolvedMsgid\")?.toString().trim();\r\n\tconst resolvedMsgctxtRaw = data.get(\"resolvedMsgctxt\")?.toString();\r\n\tconst translationValue = data.get(\"translationValue\")?.toString().trim();\r\n\tconst resolvedMsgctxt =\r\n\t\tresolvedMsgctxtRaw && resolvedMsgctxtRaw.trim().length > 0\r\n\t\t\t? resolvedMsgctxtRaw.trim()\r\n\t\t\t: null;\r\n\r\n\tif (!resolvedMsgid || !translationValue) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst result = await writeTranslationToWorkingCatalog(\n\t\tresolvedMsgid,\n\t\tresolvedMsgctxt,\n\t\ttranslationValue\n\t);\n\r\n\tif (!result.ok) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Translation written to Angy draft working catalog\",\n\t\tmsgid: result.msgid,\n\t\tmsgctxt: result.msgctxt,\n\t\tworkingCatalog: result.workingCatalog\n\t});\n}\n\nexport async function handlePromoteWorkingPreview() {\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t\tawait promoteWorkingDraftToRuntime();\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: `Promoted Angy draft into ${basename(getTranslationHelperConfig().workingPoPath)}`\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotatePreflight() {\n\ttry {\n\t\tconst preflight = await getRotationPreflight();\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tsafe: preflight.safe,\n\t\t\tstatus: preflight.status,\n\t\t\taffected: preflight.affected\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotateCatalogs(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst confirmDestructive = data?.confirmDestructive === true;\n\tconst preflight = await getRotationPreflight();\n\n\tif (!preflight.safe && !confirmDestructive) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Catalog rotation would overwrite working-only translations. Confirm rotation explicitly to continue.\",\n\t\t\t\tcode: \"rotation_confirmation_required\",\n\t\t\t\tstatus: preflight.status,\n\t\t\t\taffected: preflight.affected\n\t\t\t},\n\t\t\t{ status: 409 }\n\t\t);\n\t}\n\n\tconst result = await rotateCatalogs({ allowOutOfSync: confirmDestructive });\n\n\tif (!result.ok) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: result.error,\n\t\t\t\tstatus: \"status\" in result ? result.status : undefined,\n\t\t\t\taffected: \"affected\" in result ? result.affected : undefined\n\t\t\t},\n\t\t\t{ status: \"status\" in result && result.status === \"out_of_sync\" ? 409 : 400 }\n\t\t);\n\t}\n\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Catalogs rotated\",\n\t\tbasePoPath: result.basePoPath,\n\t\tworkingPoPath: result.workingPoPath,\n\t\tbaseBackupPath: result.baseBackupPath,\n\t\tworkingBackupPath: result.workingBackupPath\n\t});\n}\n", "import { constants as fsConstants } from \"node:fs\";\nimport { access, copyFile, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join, basename } from \"node:path\";\nimport gettextParser from \"gettext-parser\";\nimport Fuse from \"fuse.js\";\nimport type {\n\tCatalogIntegrityIssue,\n\tNormalizedEntry,\n\tPoTranslationEntry,\n\tRotationImpactItem,\n\tTranslationOrigin,\n\tTranslationStatus\n} from \"./types.ts\";\nimport { getTranslationHelperConfig, suspendWorkingCatalogWatch } from \"./config.ts\";\n\nexport const runtimeTranslations = new Map<string, string>();\n\nexport class CatalogIntegrityError extends Error {\n\tissues: CatalogIntegrityIssue[];\n\n\tconstructor(message: string, issues: CatalogIntegrityIssue[]) {\n\t\tsuper(message);\n\t\tthis.name = \"CatalogIntegrityError\";\n\t\tthis.issues = issues;\n\t}\n}\n\ntype CatalogPair = {\n\tbaseEntries: NormalizedEntry[];\n\tworkingEntries: NormalizedEntry[];\n\tbaseMap: Map<string, NormalizedEntry>;\n\tworkingMap: Map<string, NormalizedEntry>;\n\trotationImpact: RotationImpactItem[];\n};\n\nexport function catalogEntryKey(msgid: string, msgctxt: string | null) {\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\n}\n\r\nexport function normalizeWhitespace(value: string): string {\r\n\treturn value.replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nexport function normalizeForLookup(value: string): string {\r\n\treturn normalizeWhitespace(value)\r\n\t\t.replace(/<\\/?[a-z][^>]*>/gi, \"<x>\")\r\n\t\t.replace(/<\\/?\\d+>/g, \"<x>\")\r\n\t\t.replace(/\\{\\{?\\s*[^}]+\\s*\\}?\\}/g, \"{0}\")\r\n\t\t.replace(/\\{\\d+\\}/g, \"{0}\")\r\n\t\t.replace(/[\"\"'`\u00C2\u00B4]/g, \"'\")\r\n\t\t.toLowerCase();\r\n}\r\n\r\nexport function tokenizeForLookup(value: string): string[] {\r\n\treturn normalizeForLookup(value)\r\n\t\t.split(/[\\s,.;:!?()[\\]{}]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nexport function buildLookupVariants(raw: string): string[] {\r\n\tconst normalized = normalizeForLookup(raw);\r\n\tconst variants = new Set<string>([normalized]);\r\n\r\n\tvariants.add(normalized.replace(/<x>/g, \"<0>\"));\r\n\tvariants.add(normalized.replace(/<x>/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\tvariants.add(normalized.replace(/\\{0\\}/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\r\n\treturn [...variants].filter(Boolean);\r\n}\r\n\r\nexport function flattenPoEntries(parsed: any, translationOrigin: TranslationOrigin): NormalizedEntry[] {\r\n\tconst entries: NormalizedEntry[] = [];\r\n\tconst translations = parsed?.translations ?? {};\r\n\r\n\tfor (const [ctxKey, group] of Object.entries(translations)) {\r\n\t\tfor (const [msgidKey, raw] of Object.entries(group as Record<string, unknown>)) {\r\n\t\t\tif (!msgidKey) continue;\r\n\r\n\t\t\tconst entry = raw as PoTranslationEntry;\r\n\t\t\tconst msgid = entry.msgid ?? msgidKey;\r\n\t\t\tconst msgctxt = entry.msgctxt ?? (ctxKey === \"\" ? null : ctxKey);\r\n\t\t\tconst msgidPlural = entry.msgid_plural ?? null;\r\n\t\t\tconst msgstr = Array.isArray(entry.msgstr) ? entry.msgstr.filter(Boolean) : [];\r\n\t\t\tconst references = entry.comments?.reference\r\n\t\t\t\t? entry.comments.reference.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst extractedComments = entry.comments?.extracted\r\n\t\t\t\t? entry.comments.extracted.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst flags = entry.comments?.flag\r\n\t\t\t\t? entry.comments.flag.split(\",\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst previous = entry.comments?.previous\r\n\t\t\t\t? entry.comments.previous.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst translated = entry.msgstr?.some((item) => item.trim().length > 0) ?? false;\r\n\t\t\tconst fuzzy = entry.comments?.flag?.includes(\"fuzzy\");\r\n\r\n\t\t\tentries.push({\r\n\t\t\t\tmsgid,\r\n\t\t\t\tmsgctxt,\r\n\t\t\t\tmsgidPlural,\r\n\t\t\t\tmsgstr,\r\n\t\t\t\treferences,\r\n\t\t\t\textractedComments,\r\n\t\t\t\tflags,\r\n\t\t\t\tprevious,\r\n\t\t\t\tobsolete: Boolean(entry.obsolete),\r\n\t\t\t\tsearchText: normalizeForLookup(msgid),\r\n\t\t\t\tsearchTokens: tokenizeForLookup(msgid),\r\n\t\t\t\tisFuzzy: fuzzy,\r\n\t\t\t\thasTranslation: translated,\r\n\t\t\t\ttranslationOrigin\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\treturn entries;\r\n}\r\n\r\nexport function createFuse(entries: NormalizedEntry[]) {\r\n\treturn new Fuse(entries, {\r\n\t\tincludeScore: true,\r\n\t\tignoreLocation: true,\r\n\t\tthreshold: 0.34,\r\n\t\tminMatchCharLength: 2,\r\n\t\tkeys: [\r\n\t\t\t{ name: \"msgid\", weight: 0.7 },\r\n\t\t\t{ name: \"searchText\", weight: 0.2 },\r\n\t\t\t{ name: \"references\", weight: 0.1 }\r\n\t\t]\r\n\t});\r\n}\r\n\r\nexport function buildEntryMap(entries: NormalizedEntry[]) {\n\treturn new Map(entries.map((entry) => [catalogEntryKey(entry.msgid, entry.msgctxt), entry]));\n}\n\nexport function getEntryValue(entry?: Pick<NormalizedEntry, \"msgstr\"> | null) {\n\tif (!entry?.msgstr?.length) return \"\";\n\treturn entry.msgstr.find((item) => item.trim().length > 0)?.trim() ?? \"\";\n}\n\nexport function applyWorkingState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n): NormalizedEntry {\n\tif (!workingEntry) {\r\n\t\treturn baseEntry;\r\n\t}\r\n\r\n\treturn {\r\n\t\t...baseEntry,\r\n\t\tmsgstr: workingEntry.msgstr,\r\n\t\tflags: workingEntry.flags,\r\n\t\tprevious: workingEntry.previous,\r\n\t\tobsolete: workingEntry.obsolete,\r\n\t\thasTranslation: workingEntry.hasTranslation,\r\n\t\tisFuzzy: workingEntry.isFuzzy,\r\n\t\ttranslationOrigin: workingEntry.translationOrigin\r\n\t};\r\n}\r\n\r\nexport async function fileExists(path: string) {\r\n\ttry {\r\n\t\tawait access(path, fsConstants.F_OK);\r\n\t\treturn true;\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport async function readCatIndex(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tlet path = \"\";\n\n\tif (catalog === \"base\") {\n\t\tpath = basePoPath;\n\t} else {\n\t\tpath = await getEffectiveWorkingPoPath();\n\t\tconst exists = await fileExists(path);\n\t\tif (!exists) return null;\n\t}\n\r\n\tconst raw = await readFile(path);\r\n\tconst parsed = gettextParser.po.parse(raw);\r\n\tconst entries = flattenPoEntries(parsed, catalog);\r\n\r\n\treturn {\r\n\t\tentries,\r\n\t\tfuse: createFuse(entries)\r\n\t};\r\n}\r\n\r\nexport async function readParsedCatalog(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tconst path = catalog === \"base\" ? basePoPath : await getEffectiveWorkingPoPath();\n\r\n\tif (catalog === \"working\") {\r\n\t\tconst exists = await fileExists(path);\r\n\t\tif (!exists) return null;\r\n\t}\r\n\r\n\tconst raw = await readFile(path);\r\n\treturn gettextParser.po.parse(raw);\r\n}\r\n\r\nexport async function ensureWorkingCatalog() {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst exists = await fileExists(workingPoPath);\n\tif (!exists) {\n\t\tawait copyFile(basePoPath, workingPoPath);\n\t}\n}\n\nexport function getWorkingDraftPoPath() {\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst extension = extname(workingPoPath);\n\tconst stem = workingPoPath.slice(0, workingPoPath.length - extension.length);\n\treturn `${stem}.angy-draft${extension}`;\n}\n\nasync function getEffectiveWorkingPoPath() {\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tif (await fileExists(draftPoPath)) {\n\t\treturn draftPoPath;\n\t}\n\n\treturn getTranslationHelperConfig().workingPoPath;\n}\n\nexport async function ensureWorkingDraftCatalog() {\n\tawait ensureWorkingCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst exists = await fileExists(draftPoPath);\n\tif (!exists) {\n\t\tawait copyFile(workingPoPath, draftPoPath);\n\t}\n}\n\r\nexport function removeFuzzyFlag(flagString?: string) {\r\n\tif (!flagString) return \"\";\r\n\r\n\treturn flagString\r\n\t\t.split(\",\")\r\n\t\t.map((item) => item.trim())\r\n\t\t.filter(Boolean)\r\n\t\t.filter((item) => item !== \"fuzzy\")\r\n\t\t.join(\", \");\r\n}\r\n\r\nexport async function writeWorkingCatalog(parsed: any) {\n\tawait ensureWorkingDraftCatalog();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst compiled = gettextParser.po.compile(parsed);\n\tawait writeFile(draftPoPath, compiled);\n}\n\nexport async function promoteWorkingDraftToRuntime() {\n\tawait ensureWorkingDraftCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst publishPath = `${workingPoPath}.angy-publish`;\n\tconst compiled = await readFile(draftPoPath);\n\n\tsuspendWorkingCatalogWatch(workingPoPath);\n\tawait writeFile(publishPath, compiled);\n\tawait rm(workingPoPath, { force: true });\n\tawait rename(publishPath, workingPoPath);\n}\n\nexport function collectCatalogIntegrityIssues(\n\tbaseEntries: NormalizedEntry[],\n\tworkingEntries: NormalizedEntry[]\n) {\n\tconst issues: CatalogIntegrityIssue[] = [];\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst key = catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt);\n\t\tconst workingEntry = workingMap.get(key);\n\t\tif (!workingEntry) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\treason: \"missing_in_working\"\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (baseValue && !workingValue) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"missing_working_translation\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\tbaseValue\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const workingEntry of workingEntries) {\n\t\tconst key = catalogEntryKey(workingEntry.msgid, workingEntry.msgctxt);\n\t\tif (!baseMap.has(key)) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: workingEntry.msgid,\n\t\t\t\tmsgctxt: workingEntry.msgctxt,\n\t\t\t\treason: \"missing_in_base\"\n\t\t\t});\n\t\t}\n\t}\n\n\treturn issues;\n}\n\nexport function assertCatalogIntegrity(baseEntries: NormalizedEntry[], workingEntries: NormalizedEntry[]) {\n\tconst issues = collectCatalogIntegrityIssues(baseEntries, workingEntries);\n\tif (issues.length) {\n\t\tthrow new CatalogIntegrityError(\n\t\t\t\"Working catalog is out of sync with the base catalog. Regenerate or replace the working catalog before using Angy.\",\n\t\t\tissues\n\t\t);\n\t}\n}\n\nexport function collectRotationImpact(\n\tbaseEntries: NormalizedEntry[],\n\tworkingMap: Map<string, NormalizedEntry>\n) {\n\tconst impacted: RotationImpactItem[] = [];\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst workingEntry = workingMap.get(catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt));\n\t\tif (!workingEntry) continue;\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (!workingValue) continue;\n\t\tif (baseValue === workingValue) continue;\n\n\t\timpacted.push({\n\t\t\tmsgid: baseEntry.msgid,\n\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\tbaseValue,\n\t\t\tworkingValue\n\t\t});\n\t}\n\n\treturn impacted;\n}\n\nexport function resolveTranslationState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst baseValue = getEntryValue(baseEntry);\n\tconst workingValue = getEntryValue(workingEntry);\n\tconst activeEntry = workingEntry && workingValue ? workingEntry : baseEntry;\n\tconst fuzzy = Boolean(activeEntry.isFuzzy);\n\tlet translationOrigin: TranslationOrigin = \"base\";\n\tlet translationStatus: TranslationStatus = \"none\";\n\tlet effectiveEntry = baseEntry;\n\n\tif (workingEntry && workingValue) {\n\t\teffectiveEntry = workingEntry;\n\t\tif (!baseValue || workingValue !== baseValue) {\n\t\t\ttranslationOrigin = \"working\";\n\t\t\ttranslationStatus = \"working\";\n\t\t} else {\n\t\t\ttranslationOrigin = \"base\";\n\t\t\ttranslationStatus = \"base\";\n\t\t}\n\t} else if (baseValue) {\n\t\teffectiveEntry = baseEntry;\n\t\ttranslationOrigin = \"base\";\n\t\ttranslationStatus = \"base\";\n\t}\n\n\tif (fuzzy) {\n\t\ttranslationStatus = \"fuzzy\";\n\t}\n\n\treturn {\n\t\teffectiveEntry,\n\t\tbaseValue,\n\t\tworkingValue,\n\t\ttranslationOrigin,\n\t\ttranslationStatus,\n\t\thasTranslation: Boolean(getEntryValue(effectiveEntry)),\n\t\tisFuzzy: fuzzy\n\t};\n}\n\nexport async function readCatalogPair(options?: { ensureWorking?: boolean }): Promise<CatalogPair> {\n\tif (options?.ensureWorking) {\n\t\tawait ensureWorkingCatalog();\n\t}\n\n\tconst [baseParsed, workingParsed] = await Promise.all([\n\t\treadParsedCatalog(\"base\"),\n\t\treadParsedCatalog(\"working\")\n\t]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\tthrow new Error(\"Unable to load base and working catalogs.\");\n\t}\n\n\tconst baseEntries = flattenPoEntries(baseParsed, \"base\");\n\tconst workingEntries = flattenPoEntries(workingParsed, \"working\");\n\tassertCatalogIntegrity(baseEntries, workingEntries);\n\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\tconst rotationImpact = collectRotationImpact(baseEntries, workingMap);\n\n\treturn {\n\t\tbaseEntries,\n\t\tworkingEntries,\n\t\tbaseMap,\n\t\tworkingMap,\n\t\trotationImpact\n\t};\n}\n\nexport async function getRotationPreflight() {\n\tconst { rotationImpact } = await readCatalogPair({ ensureWorking: true });\n\n\treturn {\n\t\tsafe: true as const,\n\t\tstatus: \"ok\" as const,\n\t\taffected: rotationImpact\n\t};\n}\n\nfunction timestampForBackup() {\n\treturn new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction buildBackupPath(path: string, label: string) {\n\tconst directory = dirname(path);\n\tconst extension = extname(path);\n\tconst stem = basename(path, extension);\n\treturn join(directory, `${stem}.${label}.${timestampForBackup()}${extension}`);\n}\n\nexport async function rotateCatalogs(options?: { allowOutOfSync?: boolean }) {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst effectiveWorkingPoPath = await getEffectiveWorkingPoPath();\n\tconst workingExists = await fileExists(effectiveWorkingPoPath);\n\n\tif (!workingExists) {\n\t\treturn { ok: false as const, error: \"Working catalog does not exist\" };\n\t}\n\n\tconst baseBackupPath = buildBackupPath(basePoPath, \"base-backup\");\n\tconst workingBackupPath = buildBackupPath(workingPoPath, \"working-backup\");\n\n\tawait copyFile(basePoPath, baseBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, workingBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, basePoPath);\n\tawait copyFile(basePoPath, workingPoPath);\n\tawait copyFile(workingPoPath, draftPoPath);\n\n\treturn {\n\t\tok: true as const,\n\t\tbasePoPath,\n\t\tworkingPoPath,\n\t\tbaseBackupPath,\n\t\tworkingBackupPath\n\t};\n}\n", "import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, extname, isAbsolute, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { pathToFileURL } from \"node:url\";\nimport { transform } from \"esbuild\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst CONFIG_FILENAMES = [\n\t\"angy.config.ts\",\n\t\"angy.config.js\",\n\t\"angy.config.mjs\",\n\t\"angy.config.cjs\"\n];\n\nexport type TranslationHelperConfig = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath: string;\n\tapiKey: string;\n\tsystemMessage: string;\n\tsuggestionModel: string;\n\twatchIgnore: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport type SuggestionRequestItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n};\n\nexport type SuggestionResponseItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n\tsuggestion: string;\n};\n\nexport type SuggestionProviderInput = {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tsourceLocale: string;\n\ttargetLocale: string;\n\tsystemMessage: string;\n\tmodel: string;\n\tapiKey: string | undefined;\n};\n\nexport type SuggestionProvider = (\n\tinput: SuggestionProviderInput\n) => Promise<SuggestionResponseItem[]>;\n\nexport type TranslationHelperUserConfig = Partial<TranslationHelperConfig>;\n\nexport type AngyConfigInput = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath?: string;\n\tapiKey?: string;\n\tsystemMessage?: string;\n\tsuggestionModel?: string;\n\twatchIgnore?: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport function buildDefaultSystemMessage(sourceLocale: string, targetLocale: string) {\n\treturn `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;\n}\n\nfunction assertNonEmptyString(value: unknown, key: string) {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`[angy] ${key} is required and must be a non-empty string.`);\n\t}\n}\n\nfunction validateRoutePath(routePath: unknown) {\n\tif (routePath == null || routePath === \"\") return;\n\tif (typeof routePath !== \"string\" || !routePath.startsWith(\"/\")) {\n\t\tthrow new Error(`[angy] routePath must be an absolute path starting with \"/\".`);\n\t}\n}\n\nfunction validateWatchIgnore(watchIgnore: unknown) {\n\tif (watchIgnore == null) return;\n\tif (!Array.isArray(watchIgnore) || watchIgnore.some((item) => typeof item !== \"string\")) {\n\t\tthrow new Error(`[angy] watchIgnore must be an array of strings.`);\n\t}\n}\n\nfunction validateSuggestionProvider(suggestionProvider: unknown) {\n\tif (suggestionProvider == null) return;\n\tif (typeof suggestionProvider !== \"function\") {\n\t\tthrow new Error(`[angy] suggestionProvider must be a function.`);\n\t}\n}\n\nconst config: TranslationHelperConfig = {\n\tbasePoPath: resolve(__dirname, \"../../locales/en.po\"),\n\tworkingPoPath: resolve(__dirname, \"../../locales/en-working.po\"),\n\tsourceLocale: \"sv\",\n\ttargetLocale: \"en\",\n\troutePath: \"/api/translations\",\n\tapiKey: \"\",\n\tsystemMessage: buildDefaultSystemMessage(\"sv\", \"en\"),\n\tsuggestionModel: \"gpt-4.1-mini\",\n\twatchIgnore: [\"**/en-working.po\"]\n};\n\nlet loadedConfigRoot: string | null = null;\nconst workingCatalogWatchControllers = new Set<(path: string, delayMs?: number) => void>();\n\nexport function configureTranslationHelper(next: Partial<TranslationHelperConfig>) {\n\tObject.assign(config, next);\n}\n\nexport function getTranslationHelperConfig() {\n\treturn config;\n}\n\nfunction normalizeFsPath(path: string) {\n\treturn normalize(path).replace(/\\\\/g, \"/\");\n}\n\nexport function inferLocaleFromCatalogPath(path: string) {\n\tconst fileName = basename(path);\n\tif (!fileName) return null;\n\treturn fileName.slice(0, fileName.length - extname(fileName).length) || null;\n}\n\nfunction resolveLocaleAlias(\n\tvalue: string,\n\tpaths: { basePoPath: string; workingPoPath: string }\n) {\n\tif (value === \"working\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.workingPoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from workingPoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\tif (value === \"base\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.basePoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from basePoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\treturn value;\n}\n\nfunction validateLocaleAliasUsage(sourceLocale: string, targetLocale: string) {\n\tif (sourceLocale === \"working\") {\n\t\tthrow new Error('[angy] sourceLocale cannot be \"working\". Use an explicit source locale.');\n\t}\n\n\tif (targetLocale === \"base\") {\n\t\tthrow new Error('[angy] targetLocale cannot be \"base\". Use an explicit target locale or \"working\".');\n\t}\n}\n\nfunction validateCatalogPathSemantics(next: TranslationHelperConfig) {\n\tconst baseLocale = inferLocaleFromCatalogPath(next.basePoPath);\n\tconst workingLocale = inferLocaleFromCatalogPath(next.workingPoPath);\n\tconst expectedWorkingLocale = `${next.targetLocale}-working`;\n\n\tif (baseLocale !== next.targetLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] basePoPath must point to the ${next.targetLocale}.po catalog. Received \"${baseLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n\n\tif (workingLocale !== expectedWorkingLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] workingPoPath must point to the ${expectedWorkingLocale}.po catalog. Received \"${workingLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n}\n\nexport function registerWorkingCatalogWatchController(\n\tcontroller: (path: string, delayMs?: number) => void\n) {\n\tworkingCatalogWatchControllers.add(controller);\n\treturn () => {\n\t\tworkingCatalogWatchControllers.delete(controller);\n\t};\n}\n\nexport function suspendWorkingCatalogWatch(path: string, delayMs = 800) {\n\tconst normalizedPath = normalizeFsPath(path);\n\tfor (const controller of workingCatalogWatchControllers) {\n\t\tcontroller(normalizedPath, delayMs);\n\t}\n}\n\nexport function normalizeTranslationHelperConfig(\n\troot: string,\n\tnext: TranslationHelperUserConfig\n): TranslationHelperUserConfig {\n\tconst normalized = { ...next };\n\n\tif (normalized.basePoPath && !isAbsolute(normalized.basePoPath)) {\n\t\tnormalized.basePoPath = resolve(root, normalized.basePoPath);\n\t}\n\n\tif (normalized.workingPoPath && !isAbsolute(normalized.workingPoPath)) {\n\t\tnormalized.workingPoPath = resolve(root, normalized.workingPoPath);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolveConfiguredLocaleAliases(\n\tnext: TranslationHelperConfig\n): TranslationHelperConfig {\n\tconst rawSourceLocale = next.sourceLocale;\n\tconst rawTargetLocale = next.targetLocale;\n\tconst resolvedSourceLocale = resolveLocaleAlias(rawSourceLocale, next);\n\tconst resolvedTargetLocale = resolveLocaleAlias(rawTargetLocale, next);\n\tconst usesDefaultSystemMessage =\n\t\tnext.systemMessage === buildDefaultSystemMessage(rawSourceLocale, rawTargetLocale);\n\n\tconst resolved = {\n\t\t...next,\n\t\tsourceLocale: resolvedSourceLocale,\n\t\ttargetLocale: resolvedTargetLocale,\n\t\tsystemMessage: usesDefaultSystemMessage\n\t\t\t? buildDefaultSystemMessage(resolvedSourceLocale, resolvedTargetLocale)\n\t\t\t: next.systemMessage\n\t};\n\n\tvalidateCatalogPathSemantics(resolved);\n\treturn resolved;\n}\n\nexport function completeAngyConfig(input: AngyConfigInput): TranslationHelperConfig {\n\tassertNonEmptyString(input.basePoPath, \"basePoPath\");\n\tassertNonEmptyString(input.workingPoPath, \"workingPoPath\");\n\tassertNonEmptyString(input.sourceLocale, \"sourceLocale\");\n\tassertNonEmptyString(input.targetLocale, \"targetLocale\");\n\tvalidateLocaleAliasUsage(input.sourceLocale, input.targetLocale);\n\tif (typeof input.apiKey !== \"string\" && typeof input.apiKey !== \"undefined\") {\n\t\tthrow new Error(`[angy] apiKey must be a string when provided. Use an empty string to disable suggestions.`);\n\t}\n\tvalidateRoutePath(input.routePath);\n\tvalidateWatchIgnore(input.watchIgnore);\n\tvalidateSuggestionProvider(input.suggestionProvider);\n\n\treturn {\n\t\tbasePoPath: input.basePoPath,\n\t\tworkingPoPath: input.workingPoPath,\n\t\tsourceLocale: input.sourceLocale,\n\t\ttargetLocale: input.targetLocale,\n\t\troutePath: input.routePath ?? \"/api/translations\",\n\t\tapiKey: input.apiKey ?? \"\",\n\t\tsystemMessage:\n\t\t\tinput.systemMessage ??\n\t\t\tbuildDefaultSystemMessage(input.sourceLocale, input.targetLocale),\n\t\tsuggestionModel: input.suggestionModel ?? \"gpt-4.1-mini\",\n\t\twatchIgnore: input.watchIgnore ?? [\"**/en-working.po\"],\n\t\tsuggestionProvider: input.suggestionProvider\n\t};\n}\n\nexport function defineAngyConfig(config: AngyConfigInput) {\n\treturn resolveConfiguredLocaleAliases(completeAngyConfig(config));\n}\n\nasync function fileExists(path: string) {\n\ttry {\n\t\tawait readFile(path);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function loadTsConfigModule(path: string) {\n\tconst source = await readFile(path, \"utf8\");\n\tconst transformed = await transform(source, {\n\t\tloader: \"ts\",\n\t\tformat: \"esm\",\n\t\ttarget: \"es2022\"\n\t});\n\tconst tempPath = `${path}.angy.tmp.mjs`;\n\tawait writeFile(tempPath, transformed.code, \"utf8\");\n\n\ttry {\n\t\treturn await import(/* @vite-ignore */ `${pathToFileURL(tempPath).href}?t=${Date.now()}`);\n\t} finally {\n\t\tawait unlink(tempPath).catch(() => undefined);\n\t}\n}\n\nasync function loadJsConfigModule(path: string) {\n\treturn import(/* @vite-ignore */ pathToFileURL(path).href);\n}\n\nexport async function loadAngyConfigFromRoot(root: string) {\n\tif (loadedConfigRoot === root) {\n\t\treturn config;\n\t}\n\n\tfor (const filename of CONFIG_FILENAMES) {\n\t\tconst fullPath = `${root}/${filename}`.replace(/\\\\/g, \"/\");\n\t\tif (!(await fileExists(fullPath))) continue;\n\n\t\tconst module =\n\t\t\tfilename.endsWith(\".ts\")\n\t\t\t\t? await loadTsConfigModule(fullPath)\n\t\t\t\t: await loadJsConfigModule(fullPath);\n\t\tconst raw = (module.default ?? module.config ?? {}) as TranslationHelperUserConfig;\n\t\tconst next = resolveConfiguredLocaleAliases(\n\t\t\tnormalizeTranslationHelperConfig(root, completeAngyConfig(raw as AngyConfigInput))\n\t\t);\n\t\tconfigureTranslationHelper(next);\n\t\tloadedConfigRoot = root;\n\t\treturn config;\n\t}\n\n\tloadedConfigRoot = root;\n\treturn config;\n}\n", "import { json } from \"@sveltejs/kit\";\nimport {\n\tbuildLookupVariants,\n\tcatalogEntryKey,\n\tcreateFuse,\n\tCatalogIntegrityError,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nimport type { NormalizedEntry } from \"./types.ts\";\n\r\nconst DIRECT_MATCH_LIMIT = 5;\nconst MAX_ALTERNATIVES = 300;\nconst TRANSLATED_TARGET = Math.floor(MAX_ALTERNATIVES * 0.2);\nconst UNTRANSLATED_TARGET = MAX_ALTERNATIVES - TRANSLATED_TARGET;\nconst UNTRANSLATED_RANK_BONUS = 0.03;\n\r\ntype Candidate = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\ntype ScoredEntry = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\nexport function entryKey(msgid: string, msgctxt: string | null) {\r\n\treturn catalogEntryKey(msgid, msgctxt);\r\n}\r\n\r\nfunction normalizeCurrentPath(currentPath: string) {\r\n\ttry {\r\n\t\treturn new URL(currentPath, \"http://localhost\").pathname;\r\n\t} catch {\r\n\t\treturn currentPath;\r\n\t}\r\n}\r\n\r\nfunction inferPageReference(currentPath: string) {\r\n\tconst normalizedPath = normalizeCurrentPath(currentPath);\r\n\tif (normalizedPath === \"/\") {\r\n\t\treturn \"src/routes/+page.svelte\";\r\n\t}\r\n\r\n\tconst trimmedPath = normalizedPath.replace(/\\/+$/, \"\");\r\n\treturn `src/routes${trimmedPath}/+page.svelte`;\r\n}\r\n\r\nfunction routeLooksRelevant(routeReference: string, refs: string[], extractedComments: string[]) {\r\n\tif (!routeReference) return false;\r\n\r\n\tconst haystack = [...refs, ...extractedComments].join(\" \").toLowerCase();\r\n\treturn haystack.includes(routeReference.toLowerCase());\r\n}\r\n\r\nfunction dedupeCandidates(candidates: Candidate[]) {\r\n\tconst deduped = new Map<string, Candidate>();\r\n\r\n\tfor (const item of candidates) {\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tconst existing = deduped.get(id);\r\n\t\tif (!existing || item.score < existing.score) {\r\n\t\t\tdeduped.set(id, item);\r\n\t\t}\r\n\t}\r\n\r\n\treturn [...deduped.values()];\r\n}\r\n\r\nfunction rankDirectMatches(candidates: Candidate[], routeReference: string) {\n\treturn [...candidates].sort((left, right) => {\n\t\tconst leftRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tleft.entry.references,\r\n\t\t\tleft.entry.extractedComments\r\n\t\t)\r\n\t\t\t? -0.08\r\n\t\t\t: 0;\r\n\t\tconst rightRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tright.entry.references,\n\t\t\tright.entry.extractedComments\n\t\t)\n\t\t\t? -0.08\n\t\t\t: 0;\n\t\tconst leftUntranslated = !left.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\t\tconst rightUntranslated = !right.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\n\t\treturn left.score + leftRoute + leftUntranslated - (right.score + rightRoute + rightUntranslated);\n\t});\n}\n\r\nfunction getReferenceTokens(reference: string) {\r\n\treturn reference\r\n\t\t.toLowerCase()\r\n\t\t.split(/[\\/\\[\\].:_-]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nfunction getReferenceSimilarity(reference: string, routeReference: string) {\r\n\tconst referenceTokens = new Set(getReferenceTokens(reference));\r\n\tconst routeTokens = getReferenceTokens(routeReference);\r\n\r\n\tif (!routeTokens.length || !referenceTokens.size) return 0;\r\n\r\n\tlet matches = 0;\r\n\tfor (const token of routeTokens) {\r\n\t\tif (referenceTokens.has(token)) {\r\n\t\t\tmatches += 1;\r\n\t\t}\r\n\t}\r\n\r\n\treturn matches / routeTokens.length;\r\n}\r\n\r\nfunction collectSharedReferenceAlternatives(\n\tentries: NormalizedEntry[],\n\tbestEntry: NormalizedEntry,\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\r\n\tconst sharedReferences = new Set(bestEntry.references.map((reference) => reference.toLowerCase()));\r\n\r\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceMatches = entry.references.filter((reference) =>\r\n\t\t\t\tsharedReferences.has(reference.toLowerCase())\r\n\t\t\t);\r\n\t\t\tconst routeMatch = entry.references.some((reference) =>\r\n\t\t\t\treference.toLowerCase().includes(routeReference.toLowerCase())\r\n\t\t\t);\r\n\r\n\t\t\tif (!referenceMatches.length && !routeMatch) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\r\n\t\t\tconst referenceSimilarity = Math.max(\n\t\t\t\t0,\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\n\t\t\t);\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.35 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore:\n\t\t\t\t\treferenceMatches.length * 100 +\n\t\t\t\t\t(routeMatch ? 25 : 0) +\n\t\t\t\t\treferenceSimilarity +\n\t\t\t\t\tuntranslatedBonus,\n\t\t\t\tvariant: \"shared-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectRouteFillAlternatives(\n\tentries: NormalizedEntry[],\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceSimilarity = Math.max(\r\n\t\t\t\t0,\r\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\r\n\t\t\t);\r\n\r\n\t\t\tif (referenceSimilarity <= 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.2 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore: referenceSimilarity + untranslatedBonus,\n\t\t\t\tvariant: \"route-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectTranslatedCohesionAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"translated-cohesion\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectUntranslatedSimilarityAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => !item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"untranslated-similarity\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction buildAlternativePool(\r\n\ttopDirectAlternatives: Candidate[],\r\n\tsharedReferenceAlternatives: ScoredEntry[],\r\n\trouteFillAlternatives: ScoredEntry[],\r\n\ttranslatedCohesionAlternatives: ScoredEntry[],\r\n\tuntranslatedSimilarityAlternatives: ScoredEntry[]\r\n) {\r\n\tconst alternatives: ScoredEntry[] = [...topDirectAlternatives];\r\n\tconst selectedKeys = new Set<string>(\r\n\t\ttopDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t);\r\n\r\n\tlet translatedCount = topDirectAlternatives.filter((item) => item.entry.hasTranslation).length;\r\n\tlet untranslatedCount = topDirectAlternatives.length - translatedCount;\r\n\r\n\tconst tryAdd = (item: ScoredEntry, honorQuota = true) => {\r\n\t\tif (alternatives.length >= MAX_ALTERNATIVES) return false;\r\n\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tif (selectedKeys.has(id)) return false;\r\n\r\n\t\tif (honorQuota) {\r\n\t\t\tif (item.entry.hasTranslation && translatedCount >= TRANSLATED_TARGET) return false;\r\n\t\t\tif (!item.entry.hasTranslation && untranslatedCount >= UNTRANSLATED_TARGET) return false;\r\n\t\t}\r\n\r\n\t\tselectedKeys.add(id);\r\n\t\talternatives.push(item);\r\n\r\n\t\tif (item.entry.hasTranslation) {\r\n\t\t\ttranslatedCount += 1;\r\n\t\t} else {\r\n\t\t\tuntranslatedCount += 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t};\r\n\r\n\tconst prioritizedSources = [\r\n\t\tsharedReferenceAlternatives,\r\n\t\trouteFillAlternatives,\r\n\t\tuntranslatedSimilarityAlternatives,\r\n\t\ttranslatedCohesionAlternatives\r\n\t];\r\n\r\n\tfor (const source of prioritizedSources) {\r\n\t\tfor (const item of source) {\r\n\t\t\ttryAdd(item, true);\r\n\t\t}\r\n\t}\r\n\r\n\tif (alternatives.length < MAX_ALTERNATIVES) {\r\n\t\tfor (const source of prioritizedSources) {\r\n\t\t\tfor (const item of source) {\r\n\t\t\t\ttryAdd(item, false);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn alternatives.slice(0, MAX_ALTERNATIVES);\r\n}\r\n\r\nexport async function findTranslationContext(\n\tkey: string,\n\tcurrentPath: string\n) {\n\tconst catalogPair = await readCatalogPair({ ensureWorking: true });\n\tconst effectiveEntries = catalogPair.baseEntries.map((entry) => {\n\t\tconst state = resolveTranslationState(entry, catalogPair.workingMap.get(entryKey(entry.msgid, entry.msgctxt)));\n\t\treturn {\n\t\t\t...state.effectiveEntry,\n\t\t\thasTranslation: state.hasTranslation,\n\t\t\tisFuzzy: state.isFuzzy,\n\t\t\ttranslationOrigin: state.translationOrigin\n\t\t};\n\t});\n\tconst effectiveIndex = {\n\t\tentries: effectiveEntries,\n\t\tfuse: createFuse(effectiveEntries)\n\t};\n\n\tconst routeReference = inferPageReference(currentPath);\n\tconst directMatches = rankDirectMatches(\n\t\tdedupeCandidates(\n\t\t\tbuildLookupVariants(key).flatMap((variant) =>\n\t\t\t\teffectiveIndex.fuse.search(variant, { limit: 180 }).map((hit) => ({\n\t\t\t\t\tentry: hit.item,\n\t\t\t\t\tscore: hit.score ?? 1,\n\t\t\t\t\tvariant\n\t\t\t\t}))\n\t\t\t)\n\t\t),\r\n\t\trouteReference\r\n\t);\r\n\r\n\tconst best = directMatches[0];\r\n\tif (!best || best.score > 0.42) {\n\t\treturn null;\n\t}\n\n\tconst effectiveDirectMatches = directMatches;\n\tconst bestEffective = effectiveDirectMatches[0];\n\tconst topDirectAlternatives = effectiveDirectMatches.slice(1, DIRECT_MATCH_LIMIT);\n\tconst excludedKeys = new Set<string>([\r\n\t\tentryKey(best.entry.msgid, best.entry.msgctxt),\r\n\t\t...topDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t]);\r\n\r\n\tconst sharedReferenceAlternatives = collectSharedReferenceAlternatives(\r\n\t\teffectiveEntries,\r\n\t\tbestEffective.entry,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst routeFillAlternatives = collectRouteFillAlternatives(\r\n\t\teffectiveEntries,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst translatedCohesionAlternatives = collectTranslatedCohesionAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst untranslatedSimilarityAlternatives = collectUntranslatedSimilarityAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\treturn {\n\t\tbest,\n\t\talternatives: buildAlternativePool(\n\t\t\ttopDirectAlternatives,\r\n\t\t\tsharedReferenceAlternatives,\r\n\t\t\trouteFillAlternatives,\r\n\t\t\ttranslatedCohesionAlternatives,\r\n\t\t\tuntranslatedSimilarityAlternatives\n\t\t),\n\t\trouteReference,\n\t\tbaseMap: catalogPair.baseMap,\n\t\tworkingMap: catalogPair.workingMap,\n\t\tcatalogState: {\n\t\t\tstatus: \"ok\" as const,\n\t\t\taffectedCount: catalogPair.rotationImpact.length\n\t\t}\n\t};\n}\n\nexport function toPublicEntry(\n\tentry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tmsgidPlural: entry.msgidPlural,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\treferences: entry.references,\n\t\textractedComments: entry.extractedComments,\n\t\tflags: state.effectiveEntry.flags,\n\t\tprevious: state.effectiveEntry.previous,\n\t\tobsolete: state.effectiveEntry.obsolete,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\nexport function toPublicAlternative(\n\tentry: NormalizedEntry,\n\tscore: number,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tscore,\n\t\treferences: entry.references,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\r\nexport async function handleContext(request: Request) {\r\n\tconst data = await request.formData();\r\n\tconst key = data.get(\"translationKey\")?.toString().trim();\r\n\tconst currentPath = data.get(\"currentPath\")?.toString().trim();\r\n\r\n\tif (!key || !currentPath) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"translationKey and currentPath are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tlet baseFound;\n\ttry {\n\t\tbaseFound = await findTranslationContext(key, currentPath);\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tif (!baseFound) {\n\t\treturn json(\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Translation key context not found\"\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst bestBase =\n\t\tbaseFound.baseMap.get(entryKey(baseFound.best.entry.msgid, baseFound.best.entry.msgctxt)) ??\n\t\tbaseFound.best.entry;\n\tconst bestWorking = baseFound.workingMap.get(entryKey(bestBase.msgid, bestBase.msgctxt));\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmatch: {\n\t\t\tscore: baseFound.best.score,\n\t\t\tvia: baseFound.best.variant\n\t\t},\n\t\tcatalogState: baseFound.catalogState,\n\t\tentry: toPublicEntry(bestBase, bestWorking),\n\t\talternatives: baseFound.alternatives.map((item) => {\n\t\t\tconst baseAlt =\n\t\t\t\tbaseFound.baseMap.get(entryKey(item.entry.msgid, item.entry.msgctxt)) ?? item.entry;\n\t\t\tconst workingAlt = baseFound.workingMap.get(entryKey(baseAlt.msgid, baseAlt.msgctxt));\n\t\t\treturn toPublicAlternative(baseAlt, item.score, workingAlt);\n\t\t})\n\t});\n}\n", "import { json } from \"@sveltejs/kit\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\nimport type { SuggestionRequestItem, SuggestionResponseItem } from \"./config.ts\";\nimport { getTranslationHelperConfig } from \"./config.ts\";\n\r\nfunction getEntriesForSuggestions(contextResult: TranslationContextResult) {\r\n\treturn [contextResult.entry, ...contextResult.alternatives];\r\n}\r\n\r\nfunction getTranslatedExamples(contextResult: TranslationContextResult) {\r\n\treturn getEntriesForSuggestions(contextResult)\r\n\t\t.filter((entry) => entry.hasTranslation && entry.msgstr?.[0])\r\n\t\t.slice(0, 30)\r\n\t\t.map((entry) => ({\r\n\t\t\tmsgid: entry.msgid,\r\n\t\t\tmsgctxt: entry.msgctxt,\r\n\t\t\ttranslation: entry.msgstr[0]\r\n\t\t}));\r\n}\r\n\r\nfunction buildUserMessage(\r\n\tcontextResult: TranslationContextResult,\r\n\titems: SuggestionRequestItem[]\r\n) {\r\n\treturn JSON.stringify(\r\n\t\t{\r\n\t\t\ttask: \"Return translation suggestions for the untranslated UI strings.\",\r\n\t\t\toutput_format: {\r\n\t\t\t\titems: [\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmsgid: \"source string\",\r\n\t\t\t\t\t\tmsgctxt: \"optional context or null\",\r\n\t\t\t\t\t\tsuggestion: \"translated suggestion\"\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\ttranslated_examples: getTranslatedExamples(contextResult),\r\n\t\t\tuntranslated_items: items\r\n\t\t},\r\n\t\tnull,\r\n\t\t2\r\n\t);\r\n}\r\n\r\nfunction parseSuggestions(responseText: string): SuggestionResponseItem[] {\r\n\ttry {\r\n\t\tconst parsed = JSON.parse(responseText);\r\n\t\tconst items = Array.isArray(parsed?.items) ? parsed.items : [];\r\n\t\treturn items.filter(\r\n\t\t\t(item): item is SuggestionResponseItem =>\r\n\t\t\t\ttypeof item?.msgid === \"string\" &&\r\n\t\t\t\t(item.msgctxt === null || typeof item.msgctxt === \"string\") &&\r\n\t\t\t\ttypeof item?.suggestion === \"string\"\r\n\t\t);\r\n\t} catch {\r\n\t\treturn [];\r\n\t}\r\n}\r\n\r\nexport async function handleSuggestions(request: Request) {\n\tconst {\n\t\tapiKey,\n\t\tsuggestionModel,\n\t\tsystemMessage,\n\t\tsourceLocale,\n\t\ttargetLocale,\n\t\tsuggestionProvider\n\t} = getTranslationHelperConfig();\n\n\tconst data = await request.json().catch(() => null);\n\tconst context = data?.context as TranslationContextResult | undefined;\n\tconst items = Array.isArray(data?.items) ? (data.items as SuggestionRequestItem[]) : [];\n\r\n\tif (!context || !items.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"context and items are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\n\t\t);\n\t}\n\n\tif (suggestionProvider) {\n\t\tconst providedItems = await suggestionProvider({\n\t\t\tcontext,\n\t\t\titems,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\tsystemMessage,\n\t\t\tmodel: suggestionModel,\n\t\t\tapiKey\n\t\t});\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: providedItems\n\t\t});\n\t}\n\n\tif (!apiKey.trim()) {\n\t\tconsole.warn(\"[angy] Suggestions disabled because apiKey is empty.\");\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tdisabled: true,\n\t\t\titems: []\n\t\t});\n\t}\n\n\ttry {\n\t\tconsole.info(\"[angy] Suggestion request starting.\", {\n\t\t\tmodel: suggestionModel,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\titemCount: items.length,\n\t\t\thasApiKey: Boolean(apiKey.trim())\n\t\t});\n\n\t\tconst response = await fetch(\"https://api.openai.com/v1/responses\", {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"content-type\": \"application/json\"\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: suggestionModel,\n\t\t\t\treasoning: {\n\t\t\t\t\teffort: \"medium\"\n\t\t\t\t},\n\t\t\t\tinput: [\n\t\t\t\t\t{\n\t\t\t\t\t\trole: \"system\",\n\t\t\t\t\t\tcontent: [{ type: \"input_text\", text: systemMessage }]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"input_text\", text: buildUserMessage(context, items) }]\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\ttext: {\n\t\t\t\t\tformat: {\n\t\t\t\t\t\ttype: \"json_schema\",\n\t\t\t\t\t\tname: \"translation_suggestions\",\n\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmsgid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\tmsgctxt: { type: [\"string\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\tsuggestion: { type: \"string\" }\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\trequired: [\"msgid\", \"msgctxt\", \"suggestion\"]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"items\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\tconsole.error(\"[angy] Suggestion request failed.\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\tbody: errorBody\n\t\t\t});\n\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Suggestion request failed: ${response.status}`\n\t\t\t\t},\n\t\t\t\t{ status: 502 }\n\t\t\t);\n\t\t}\n\n\t\tconst responseJson = await response.json();\n\tconst responseText =\n\t\tresponseJson?.output_text ??\n\t\tresponseJson?.output\n\t\t\t?.flatMap((item: any) => item?.content ?? [])\n\t\t\t.find((part: any) => part?.text)?.text ??\n\t\t\"\";\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: parseSuggestions(responseText)\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(\"[angy] Suggestion request threw.\", error);\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Suggestion request failed before reaching the model\"\n\t\t\t},\n\t\t\t{ status: 502 }\n\t\t);\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { json, type RequestHandler } from \"@sveltejs/kit\";\nimport {\n\thandleCommit,\n\thandleCommitBatch,\n\thandlePromoteWorkingPreview,\n\thandleRotateCatalogs,\n\thandleRotatePreflight\n} from \"./commit.ts\";\nimport {\n\tconfigureTranslationHelper,\n\tloadAngyConfigFromRoot,\n\tnormalizeTranslationHelperConfig,\n\ttype TranslationHelperConfig\n} from \"./config.ts\";\nimport { handleContext } from \"./context.ts\";\nimport { handleSuggestions } from \"./suggestions.ts\";\n\nexport async function handleTranslationRequest(\n\trequest: Request,\n\turl: URL,\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig>; dev?: boolean }\n) {\n\tconst devOnly = options?.devOnly ?? true;\n\tconst runtimeDev =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\ttypeof import.meta.env !== \"undefined\" &&\n\t\timport.meta.env.DEV === true;\n\tconst isDev = options?.dev ?? runtimeDev;\n\n\tif (devOnly && !isDev) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Disabled outside dev\"\n\t\t\t},\n\t\t\t{ status: 404 }\n\t\t);\n\t}\n\n\tif (options?.config) {\n\t\tconfigureTranslationHelper(normalizeTranslationHelperConfig(process.cwd(), options.config));\n\t} else {\n\t\tawait loadAngyConfigFromRoot(process.cwd());\n\t}\n\n\tconst intent = url.searchParams.get(\"intent\") ?? \"commit\";\n\n\tif (intent === \"commit-batch\") {\n\t\treturn handleCommitBatch(request);\n\t}\n\n\tif (intent === \"context\") {\n\t\treturn handleContext(request);\n\t}\n\n\tif (intent === \"suggestions\") {\n\t\treturn handleSuggestions(request);\n\t}\n\n\tif (intent === \"rotate-catalogs\") {\n\t\treturn handleRotateCatalogs(request);\n\t}\n\n\tif (intent === \"rotate-preflight\") {\n\t\treturn handleRotatePreflight();\n\t}\n\n\tif (intent === \"promote-working-preview\") {\n\t\treturn handlePromoteWorkingPreview();\n\t}\n\n\treturn handleCommit(request);\n}\n\nfunction createAngyHandler(\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig> }\n): RequestHandler {\n\treturn async ({ request, url }) => handleTranslationRequest(request, url, options);\n}\n\nexport const handler = createAngyHandler();\n\nexport {\n\tdefineAngyConfig,\n\tnormalizeSuggestionModelConfig,\n\ttype AngyConfigInput,\n\ttype SuggestionModelConfig,\n\ttype SuggestionProvider,\n\ttype SuggestionProviderInput,\n\ttype SuggestionRequestItem,\n\ttype SuggestionResponseItem\n} from \"./config.ts\";\nexport {\n\tCatalogIntegrityError,\n\tcollectCatalogIntegrityIssues,\n\tcollectRotationImpact,\n\tgetRotationPreflight,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nexport { buildSuggestionRequestBody } from \"./suggestions.ts\";\n", "import { json } from \"@sveltejs/kit\";\nimport { basename } from \"node:path\";\nimport {\n\tCatalogIntegrityError,\n\tgetRotationPreflight,\n\tensureWorkingCatalog,\n\tpromoteWorkingDraftToRuntime,\n\treadCatalogPair,\n\treadParsedCatalog,\n\tremoveFuzzyFlag,\n\trotateCatalogs,\n\truntimeTranslations,\n\twriteWorkingCatalog\n} from \"./catalog.ts\";\nimport type { CommitBatchItem, PoTranslationEntry } from \"./types.ts\";\nimport { getTranslationHelperConfig, inferLocaleFromCatalogPath } from \"./config.ts\";\n\r\nfunction runtimeKey(msgid: string, msgctxt: string | null) {\r\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\r\n}\r\n\r\nexport async function writeTranslationToWorkingCatalog(\n\tresolvedMsgid: string,\n\tresolvedMsgctxt: string | null,\n\ttranslationValue: string\n) {\n\tawait ensureWorkingCatalog();\n\n\tawait readCatalogPair({ ensureWorking: true });\n\n\tconst [baseParsed, workingParsed] = await Promise.all([readParsedCatalog(\"base\"), readParsedCatalog(\"working\")]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\treturn { ok: false as const, error: \"Unable to load catalogs\" };\n\t}\n\r\n\tconst baseTranslations = baseParsed.translations ?? {};\r\n\tconst workingTranslations = workingParsed.translations ?? {};\r\n\r\n\tconst ctxKey = resolvedMsgctxt ?? \"\";\r\n\tconst baseGroup = baseTranslations[ctxKey];\r\n\tif (!baseGroup) {\r\n\t\treturn { ok: false as const, error: \"Context group not found in base catalog\" };\r\n\t}\r\n\r\n\tconst baseEntry = baseGroup[resolvedMsgid] as PoTranslationEntry | undefined;\r\n\tif (!baseEntry) {\r\n\t\treturn { ok: false as const, error: \"Target msgid not found in base catalog\" };\r\n\t}\r\n\r\n\tconst workingGroup = (workingTranslations[ctxKey] ??= {});\r\n\tconst entry = ((workingGroup[resolvedMsgid] as PoTranslationEntry | undefined) ??=\r\n\t\tcloneEntryForWorking(baseEntry, resolvedMsgid, resolvedMsgctxt));\r\n\r\n\tentry.msgstr = [translationValue];\r\n\tentry.comments ??= {};\r\n\tentry.comments.flag = removeFuzzyFlag(entry.comments.flag);\r\n\r\n\tawait writeWorkingCatalog(workingParsed);\r\n\r\n\truntimeTranslations.set(runtimeKey(resolvedMsgid, resolvedMsgctxt), translationValue);\n\n\tconst workingCatalogName = inferLocaleFromCatalogPath(getTranslationHelperConfig().workingPoPath) ?? \"working\";\n\n\treturn {\n\t\tok: true as const,\n\t\tmsgid: resolvedMsgid,\n\t\tmsgctxt: resolvedMsgctxt,\n\t\tworkingCatalog: workingCatalogName\n\t};\n}\n\r\nfunction cloneEntryForWorking(\r\n\tbaseEntry: PoTranslationEntry,\r\n\tmsgid: string,\r\n\tmsgctxt: string | null\r\n): PoTranslationEntry {\r\n\treturn {\r\n\t\tmsgid,\r\n\t\tmsgctxt: msgctxt ?? undefined,\r\n\t\tmsgid_plural: baseEntry.msgid_plural,\r\n\t\tmsgstr: Array.isArray(baseEntry.msgstr) ? [...baseEntry.msgstr] : [\"\"],\r\n\t\tobsolete: Boolean(baseEntry.obsolete),\r\n\t\tcomments: baseEntry.comments\r\n\t\t\t? {\r\n\t\t\t\t\treference: baseEntry.comments.reference,\r\n\t\t\t\t\textracted: baseEntry.comments.extracted,\r\n\t\t\t\t\tflag: baseEntry.comments.flag,\r\n\t\t\t\t\tprevious: baseEntry.comments.previous\r\n\t\t\t\t}\r\n\t\t\t: undefined\r\n\t};\r\n}\r\n\r\nexport async function handleCommitBatch(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst items = Array.isArray(data?.items) ? (data.items as CommitBatchItem[]) : [];\n\r\n\tif (!items.length) {\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"No items to commit\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\n\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tconst results: Array<{ msgid: string; msgctxt: string | null; ok: boolean; error?: string }> = [];\n\r\n\tfor (const item of items) {\r\n\t\tconst resolvedMsgid = item.resolvedMsgid?.trim();\r\n\t\tconst resolvedMsgctxt =\r\n\t\t\titem.resolvedMsgctxt && item.resolvedMsgctxt.trim().length > 0\r\n\t\t\t\t? item.resolvedMsgctxt.trim()\r\n\t\t\t\t: null;\r\n\t\tconst translationValue = item.translationValue?.trim();\r\n\r\n\t\tif (!resolvedMsgid || !translationValue) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid ?? \"\",\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst result = await writeTranslationToWorkingCatalog(\r\n\t\t\tresolvedMsgid,\r\n\t\t\tresolvedMsgctxt,\r\n\t\t\ttranslationValue\r\n\t\t);\r\n\r\n\t\tif (!result.ok) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid,\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tresults.push({\r\n\t\t\tmsgid: resolvedMsgid,\r\n\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\tok: true\r\n\t\t});\r\n\t}\r\n\r\n\tconst failed = results.filter((item) => !item.ok);\r\n\tif (failed.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Some translations failed to commit\",\r\n\t\t\t\tresults\r\n\t\t\t},\r\n\t\t\t{ status: 207 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: `Committed ${results.length} translations to Angy draft working catalog`,\n\t\tresults\n\t});\n}\n\r\nexport async function handleCommit(request: Request) {\n\tconst data = await request.formData();\r\n\tconst resolvedMsgid = data.get(\"resolvedMsgid\")?.toString().trim();\r\n\tconst resolvedMsgctxtRaw = data.get(\"resolvedMsgctxt\")?.toString();\r\n\tconst translationValue = data.get(\"translationValue\")?.toString().trim();\r\n\tconst resolvedMsgctxt =\r\n\t\tresolvedMsgctxtRaw && resolvedMsgctxtRaw.trim().length > 0\r\n\t\t\t? resolvedMsgctxtRaw.trim()\r\n\t\t\t: null;\r\n\r\n\tif (!resolvedMsgid || !translationValue) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst result = await writeTranslationToWorkingCatalog(\n\t\tresolvedMsgid,\n\t\tresolvedMsgctxt,\n\t\ttranslationValue\n\t);\n\r\n\tif (!result.ok) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Translation written to Angy draft working catalog\",\n\t\tmsgid: result.msgid,\n\t\tmsgctxt: result.msgctxt,\n\t\tworkingCatalog: result.workingCatalog\n\t});\n}\n\nexport async function handlePromoteWorkingPreview() {\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t\tawait promoteWorkingDraftToRuntime();\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: `Promoted Angy draft into ${basename(getTranslationHelperConfig().workingPoPath)}`\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotatePreflight() {\n\ttry {\n\t\tconst preflight = await getRotationPreflight();\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tsafe: preflight.safe,\n\t\t\tstatus: preflight.status,\n\t\t\taffected: preflight.affected\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotateCatalogs(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst confirmDestructive = data?.confirmDestructive === true;\n\tconst preflight = await getRotationPreflight();\n\n\tif (!preflight.safe && !confirmDestructive) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Catalog rotation would overwrite working-only translations. Confirm rotation explicitly to continue.\",\n\t\t\t\tcode: \"rotation_confirmation_required\",\n\t\t\t\tstatus: preflight.status,\n\t\t\t\taffected: preflight.affected\n\t\t\t},\n\t\t\t{ status: 409 }\n\t\t);\n\t}\n\n\tconst result = await rotateCatalogs({ allowOutOfSync: confirmDestructive });\n\n\tif (!result.ok) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: result.error,\n\t\t\t\tstatus: \"status\" in result ? result.status : undefined,\n\t\t\t\taffected: \"affected\" in result ? result.affected : undefined\n\t\t\t},\n\t\t\t{ status: \"status\" in result && result.status === \"out_of_sync\" ? 409 : 400 }\n\t\t);\n\t}\n\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Catalogs rotated\",\n\t\tbasePoPath: result.basePoPath,\n\t\tworkingPoPath: result.workingPoPath,\n\t\tbaseBackupPath: result.baseBackupPath,\n\t\tworkingBackupPath: result.workingBackupPath\n\t});\n}\n", "import { constants as fsConstants } from \"node:fs\";\nimport { access, copyFile, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join, basename } from \"node:path\";\nimport gettextParser from \"gettext-parser\";\nimport Fuse from \"fuse.js\";\nimport type {\n\tCatalogIntegrityIssue,\n\tNormalizedEntry,\n\tPoTranslationEntry,\n\tRotationImpactItem,\n\tTranslationOrigin,\n\tTranslationStatus\n} from \"./types.ts\";\nimport { getTranslationHelperConfig, suspendWorkingCatalogWatch } from \"./config.ts\";\n\nexport const runtimeTranslations = new Map<string, string>();\n\nexport class CatalogIntegrityError extends Error {\n\tissues: CatalogIntegrityIssue[];\n\n\tconstructor(message: string, issues: CatalogIntegrityIssue[]) {\n\t\tsuper(message);\n\t\tthis.name = \"CatalogIntegrityError\";\n\t\tthis.issues = issues;\n\t}\n}\n\ntype CatalogPair = {\n\tbaseEntries: NormalizedEntry[];\n\tworkingEntries: NormalizedEntry[];\n\tbaseMap: Map<string, NormalizedEntry>;\n\tworkingMap: Map<string, NormalizedEntry>;\n\trotationImpact: RotationImpactItem[];\n};\n\nexport function catalogEntryKey(msgid: string, msgctxt: string | null) {\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\n}\n\r\nexport function normalizeWhitespace(value: string): string {\r\n\treturn value.replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nexport function normalizeForLookup(value: string): string {\r\n\treturn normalizeWhitespace(value)\r\n\t\t.replace(/<\\/?[a-z][^>]*>/gi, \"<x>\")\r\n\t\t.replace(/<\\/?\\d+>/g, \"<x>\")\r\n\t\t.replace(/\\{\\{?\\s*[^}]+\\s*\\}?\\}/g, \"{0}\")\r\n\t\t.replace(/\\{\\d+\\}/g, \"{0}\")\r\n\t\t.replace(/[\"\"'`\u00C2\u00B4]/g, \"'\")\r\n\t\t.toLowerCase();\r\n}\r\n\r\nexport function tokenizeForLookup(value: string): string[] {\r\n\treturn normalizeForLookup(value)\r\n\t\t.split(/[\\s,.;:!?()[\\]{}]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nexport function buildLookupVariants(raw: string): string[] {\r\n\tconst normalized = normalizeForLookup(raw);\r\n\tconst variants = new Set<string>([normalized]);\r\n\r\n\tvariants.add(normalized.replace(/<x>/g, \"<0>\"));\r\n\tvariants.add(normalized.replace(/<x>/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\tvariants.add(normalized.replace(/\\{0\\}/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\r\n\treturn [...variants].filter(Boolean);\r\n}\r\n\r\nexport function flattenPoEntries(parsed: any, translationOrigin: TranslationOrigin): NormalizedEntry[] {\r\n\tconst entries: NormalizedEntry[] = [];\r\n\tconst translations = parsed?.translations ?? {};\r\n\r\n\tfor (const [ctxKey, group] of Object.entries(translations)) {\r\n\t\tfor (const [msgidKey, raw] of Object.entries(group as Record<string, unknown>)) {\r\n\t\t\tif (!msgidKey) continue;\r\n\r\n\t\t\tconst entry = raw as PoTranslationEntry;\r\n\t\t\tconst msgid = entry.msgid ?? msgidKey;\r\n\t\t\tconst msgctxt = entry.msgctxt ?? (ctxKey === \"\" ? null : ctxKey);\r\n\t\t\tconst msgidPlural = entry.msgid_plural ?? null;\r\n\t\t\tconst msgstr = Array.isArray(entry.msgstr) ? entry.msgstr.filter(Boolean) : [];\r\n\t\t\tconst references = entry.comments?.reference\r\n\t\t\t\t? entry.comments.reference.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst extractedComments = entry.comments?.extracted\r\n\t\t\t\t? entry.comments.extracted.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst flags = entry.comments?.flag\r\n\t\t\t\t? entry.comments.flag.split(\",\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst previous = entry.comments?.previous\r\n\t\t\t\t? entry.comments.previous.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst translated = entry.msgstr?.some((item) => item.trim().length > 0) ?? false;\r\n\t\t\tconst fuzzy = entry.comments?.flag?.includes(\"fuzzy\");\r\n\r\n\t\t\tentries.push({\r\n\t\t\t\tmsgid,\r\n\t\t\t\tmsgctxt,\r\n\t\t\t\tmsgidPlural,\r\n\t\t\t\tmsgstr,\r\n\t\t\t\treferences,\r\n\t\t\t\textractedComments,\r\n\t\t\t\tflags,\r\n\t\t\t\tprevious,\r\n\t\t\t\tobsolete: Boolean(entry.obsolete),\r\n\t\t\t\tsearchText: normalizeForLookup(msgid),\r\n\t\t\t\tsearchTokens: tokenizeForLookup(msgid),\r\n\t\t\t\tisFuzzy: fuzzy,\r\n\t\t\t\thasTranslation: translated,\r\n\t\t\t\ttranslationOrigin\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\treturn entries;\r\n}\r\n\r\nexport function createFuse(entries: NormalizedEntry[]) {\r\n\treturn new Fuse(entries, {\r\n\t\tincludeScore: true,\r\n\t\tignoreLocation: true,\r\n\t\tthreshold: 0.34,\r\n\t\tminMatchCharLength: 2,\r\n\t\tkeys: [\r\n\t\t\t{ name: \"msgid\", weight: 0.7 },\r\n\t\t\t{ name: \"searchText\", weight: 0.2 },\r\n\t\t\t{ name: \"references\", weight: 0.1 }\r\n\t\t]\r\n\t});\r\n}\r\n\r\nexport function buildEntryMap(entries: NormalizedEntry[]) {\n\treturn new Map(entries.map((entry) => [catalogEntryKey(entry.msgid, entry.msgctxt), entry]));\n}\n\nexport function getEntryValue(entry?: Pick<NormalizedEntry, \"msgstr\"> | null) {\n\tif (!entry?.msgstr?.length) return \"\";\n\treturn entry.msgstr.find((item) => item.trim().length > 0)?.trim() ?? \"\";\n}\n\nexport function applyWorkingState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n): NormalizedEntry {\n\tif (!workingEntry) {\r\n\t\treturn baseEntry;\r\n\t}\r\n\r\n\treturn {\r\n\t\t...baseEntry,\r\n\t\tmsgstr: workingEntry.msgstr,\r\n\t\tflags: workingEntry.flags,\r\n\t\tprevious: workingEntry.previous,\r\n\t\tobsolete: workingEntry.obsolete,\r\n\t\thasTranslation: workingEntry.hasTranslation,\r\n\t\tisFuzzy: workingEntry.isFuzzy,\r\n\t\ttranslationOrigin: workingEntry.translationOrigin\r\n\t};\r\n}\r\n\r\nexport async function fileExists(path: string) {\r\n\ttry {\r\n\t\tawait access(path, fsConstants.F_OK);\r\n\t\treturn true;\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport async function readCatIndex(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tlet path = \"\";\n\n\tif (catalog === \"base\") {\n\t\tpath = basePoPath;\n\t} else {\n\t\tpath = await getEffectiveWorkingPoPath();\n\t\tconst exists = await fileExists(path);\n\t\tif (!exists) return null;\n\t}\n\r\n\tconst raw = await readFile(path);\r\n\tconst parsed = gettextParser.po.parse(raw);\r\n\tconst entries = flattenPoEntries(parsed, catalog);\r\n\r\n\treturn {\r\n\t\tentries,\r\n\t\tfuse: createFuse(entries)\r\n\t};\r\n}\r\n\r\nexport async function readParsedCatalog(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tconst path = catalog === \"base\" ? basePoPath : await getEffectiveWorkingPoPath();\n\r\n\tif (catalog === \"working\") {\r\n\t\tconst exists = await fileExists(path);\r\n\t\tif (!exists) return null;\r\n\t}\r\n\r\n\tconst raw = await readFile(path);\r\n\treturn gettextParser.po.parse(raw);\r\n}\r\n\r\nexport async function ensureWorkingCatalog() {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst exists = await fileExists(workingPoPath);\n\tif (!exists) {\n\t\tawait copyFile(basePoPath, workingPoPath);\n\t}\n}\n\nexport function getWorkingDraftPoPath() {\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst extension = extname(workingPoPath);\n\tconst stem = workingPoPath.slice(0, workingPoPath.length - extension.length);\n\treturn `${stem}.angy-draft${extension}`;\n}\n\nasync function getEffectiveWorkingPoPath() {\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tif (await fileExists(draftPoPath)) {\n\t\treturn draftPoPath;\n\t}\n\n\treturn getTranslationHelperConfig().workingPoPath;\n}\n\nexport async function ensureWorkingDraftCatalog() {\n\tawait ensureWorkingCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst exists = await fileExists(draftPoPath);\n\tif (!exists) {\n\t\tawait copyFile(workingPoPath, draftPoPath);\n\t}\n}\n\r\nexport function removeFuzzyFlag(flagString?: string) {\r\n\tif (!flagString) return \"\";\r\n\r\n\treturn flagString\r\n\t\t.split(\",\")\r\n\t\t.map((item) => item.trim())\r\n\t\t.filter(Boolean)\r\n\t\t.filter((item) => item !== \"fuzzy\")\r\n\t\t.join(\", \");\r\n}\r\n\r\nexport async function writeWorkingCatalog(parsed: any) {\n\tawait ensureWorkingDraftCatalog();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst compiled = gettextParser.po.compile(parsed);\n\tawait writeFile(draftPoPath, compiled);\n}\n\nexport async function promoteWorkingDraftToRuntime() {\n\tawait ensureWorkingDraftCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst publishPath = `${workingPoPath}.angy-publish`;\n\tconst compiled = await readFile(draftPoPath);\n\n\tsuspendWorkingCatalogWatch(workingPoPath);\n\tawait writeFile(publishPath, compiled);\n\tawait rm(workingPoPath, { force: true });\n\tawait rename(publishPath, workingPoPath);\n}\n\nexport function collectCatalogIntegrityIssues(\n\tbaseEntries: NormalizedEntry[],\n\tworkingEntries: NormalizedEntry[]\n) {\n\tconst issues: CatalogIntegrityIssue[] = [];\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst key = catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt);\n\t\tconst workingEntry = workingMap.get(key);\n\t\tif (!workingEntry) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\treason: \"missing_in_working\"\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (baseValue && !workingValue) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"missing_working_translation\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\tbaseValue\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const workingEntry of workingEntries) {\n\t\tconst key = catalogEntryKey(workingEntry.msgid, workingEntry.msgctxt);\n\t\tif (!baseMap.has(key)) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: workingEntry.msgid,\n\t\t\t\tmsgctxt: workingEntry.msgctxt,\n\t\t\t\treason: \"missing_in_base\"\n\t\t\t});\n\t\t}\n\t}\n\n\treturn issues;\n}\n\nexport function assertCatalogIntegrity(baseEntries: NormalizedEntry[], workingEntries: NormalizedEntry[]) {\n\tconst issues = collectCatalogIntegrityIssues(baseEntries, workingEntries);\n\tif (issues.length) {\n\t\tthrow new CatalogIntegrityError(\n\t\t\t\"Working catalog is out of sync with the base catalog. Regenerate or replace the working catalog before using Angy.\",\n\t\t\tissues\n\t\t);\n\t}\n}\n\nexport function collectRotationImpact(\n\tbaseEntries: NormalizedEntry[],\n\tworkingMap: Map<string, NormalizedEntry>\n) {\n\tconst impacted: RotationImpactItem[] = [];\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst workingEntry = workingMap.get(catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt));\n\t\tif (!workingEntry) continue;\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (!workingValue) continue;\n\t\tif (baseValue === workingValue) continue;\n\n\t\timpacted.push({\n\t\t\tmsgid: baseEntry.msgid,\n\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\tbaseValue,\n\t\t\tworkingValue\n\t\t});\n\t}\n\n\treturn impacted;\n}\n\nexport function resolveTranslationState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst baseValue = getEntryValue(baseEntry);\n\tconst workingValue = getEntryValue(workingEntry);\n\tconst activeEntry = workingEntry && workingValue ? workingEntry : baseEntry;\n\tconst fuzzy = Boolean(activeEntry.isFuzzy);\n\tlet translationOrigin: TranslationOrigin = \"base\";\n\tlet translationStatus: TranslationStatus = \"none\";\n\tlet effectiveEntry = baseEntry;\n\n\tif (workingEntry && workingValue) {\n\t\teffectiveEntry = workingEntry;\n\t\tif (!baseValue || workingValue !== baseValue) {\n\t\t\ttranslationOrigin = \"working\";\n\t\t\ttranslationStatus = \"working\";\n\t\t} else {\n\t\t\ttranslationOrigin = \"base\";\n\t\t\ttranslationStatus = \"base\";\n\t\t}\n\t} else if (baseValue) {\n\t\teffectiveEntry = baseEntry;\n\t\ttranslationOrigin = \"base\";\n\t\ttranslationStatus = \"base\";\n\t}\n\n\tif (fuzzy) {\n\t\ttranslationStatus = \"fuzzy\";\n\t}\n\n\treturn {\n\t\teffectiveEntry,\n\t\tbaseValue,\n\t\tworkingValue,\n\t\ttranslationOrigin,\n\t\ttranslationStatus,\n\t\thasTranslation: Boolean(getEntryValue(effectiveEntry)),\n\t\tisFuzzy: fuzzy\n\t};\n}\n\nexport async function readCatalogPair(options?: { ensureWorking?: boolean }): Promise<CatalogPair> {\n\tif (options?.ensureWorking) {\n\t\tawait ensureWorkingCatalog();\n\t}\n\n\tconst [baseParsed, workingParsed] = await Promise.all([\n\t\treadParsedCatalog(\"base\"),\n\t\treadParsedCatalog(\"working\")\n\t]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\tthrow new Error(\"Unable to load base and working catalogs.\");\n\t}\n\n\tconst baseEntries = flattenPoEntries(baseParsed, \"base\");\n\tconst workingEntries = flattenPoEntries(workingParsed, \"working\");\n\tassertCatalogIntegrity(baseEntries, workingEntries);\n\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\tconst rotationImpact = collectRotationImpact(baseEntries, workingMap);\n\n\treturn {\n\t\tbaseEntries,\n\t\tworkingEntries,\n\t\tbaseMap,\n\t\tworkingMap,\n\t\trotationImpact\n\t};\n}\n\nexport async function getRotationPreflight() {\n\tconst { rotationImpact } = await readCatalogPair({ ensureWorking: true });\n\n\treturn {\n\t\tsafe: true as const,\n\t\tstatus: \"ok\" as const,\n\t\taffected: rotationImpact\n\t};\n}\n\nfunction timestampForBackup() {\n\treturn new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction buildBackupPath(path: string, label: string) {\n\tconst directory = dirname(path);\n\tconst extension = extname(path);\n\tconst stem = basename(path, extension);\n\treturn join(directory, `${stem}.${label}.${timestampForBackup()}${extension}`);\n}\n\nexport async function rotateCatalogs(options?: { allowOutOfSync?: boolean }) {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst effectiveWorkingPoPath = await getEffectiveWorkingPoPath();\n\tconst workingExists = await fileExists(effectiveWorkingPoPath);\n\n\tif (!workingExists) {\n\t\treturn { ok: false as const, error: \"Working catalog does not exist\" };\n\t}\n\n\tconst baseBackupPath = buildBackupPath(basePoPath, \"base-backup\");\n\tconst workingBackupPath = buildBackupPath(workingPoPath, \"working-backup\");\n\n\tawait copyFile(basePoPath, baseBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, workingBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, basePoPath);\n\tawait copyFile(basePoPath, workingPoPath);\n\tawait copyFile(workingPoPath, draftPoPath);\n\n\treturn {\n\t\tok: true as const,\n\t\tbasePoPath,\n\t\tworkingPoPath,\n\t\tbaseBackupPath,\n\t\tworkingBackupPath\n\t};\n}\n", "import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, extname, isAbsolute, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { pathToFileURL } from \"node:url\";\nimport { transform } from \"esbuild\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst CONFIG_FILENAMES = [\n\t\"angy.config.ts\",\n\t\"angy.config.js\",\n\t\"angy.config.mjs\",\n\t\"angy.config.cjs\"\n];\n\nexport type TranslationHelperConfig = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath: string;\n\tapiKey: string;\n\tsystemMessage: string;\n\tsuggestionModel: SuggestionModelConfig;\n\twatchIgnore: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport type SuggestionRequestItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n};\n\nexport type SuggestionResponseItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n\tsuggestion: string;\n};\n\nexport type SuggestionProviderInput = {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tsourceLocale: string;\n\ttargetLocale: string;\n\tsystemMessage: string;\n\tsuggestionModel: SuggestionModelConfig;\n\tapiKey: string | undefined;\n};\n\nexport type SuggestionProvider = (\n\tinput: SuggestionProviderInput\n) => Promise<SuggestionResponseItem[]>;\n\nexport type TranslationHelperUserConfig = Partial<TranslationHelperConfig>;\n\nconst NON_REASONING_SUGGESTION_MODELS = [\n\t\"gpt-4.1\",\n\t\"gpt-4.1-mini\",\n\t\"gpt-4.1-nano\"\n] as const;\nconst GPT54_REASONING_EFFORTS = [\"none\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\nconst GPT51_REASONING_EFFORTS = [\"none\", \"low\", \"medium\", \"high\"] as const;\nconst GPT5_REASONING_EFFORTS = [\"minimal\", \"low\", \"medium\", \"high\"] as const;\nconst GPT5_PRO_REASONING_EFFORTS = [\"high\"] as const;\nconst GPT5X_PRO_REASONING_EFFORTS = [\"medium\", \"high\", \"xhigh\"] as const;\n\ntype NonReasoningSuggestionModelName = (typeof NON_REASONING_SUGGESTION_MODELS)[number];\nexport type SuggestionReasoningEffort =\n\t| (typeof GPT54_REASONING_EFFORTS)[number]\n\t| (typeof GPT51_REASONING_EFFORTS)[number]\n\t| (typeof GPT5_REASONING_EFFORTS)[number];\n\ntype NonReasoningSuggestionModel = {\n\tmodel: NonReasoningSuggestionModelName;\n\treasoning?: null;\n};\n\ntype SuggestionModelWithReasoning<\n\tTModel extends string,\n\tTEffort extends readonly string[]\n> = {\n\tmodel: TModel;\n\treasoning?: TEffort[number];\n};\n\nexport type SuggestionModelConfig =\n\t| NonReasoningSuggestionModel\n\t| SuggestionModelWithReasoning<\"gpt-5.4\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-mini\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-nano\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.2\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.1\", typeof GPT51_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5\", typeof GPT5_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5-pro\", typeof GPT5_PRO_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.2-pro\", typeof GPT5X_PRO_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-pro\", typeof GPT5X_PRO_REASONING_EFFORTS>;\n\nconst REASONING_EFFORTS_BY_MODEL = {\n\t\"gpt-5.4\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.4-mini\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.4-nano\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.2\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.1\": GPT51_REASONING_EFFORTS,\n\t\"gpt-5\": GPT5_REASONING_EFFORTS,\n\t\"gpt-5-pro\": GPT5_PRO_REASONING_EFFORTS,\n\t\"gpt-5.2-pro\": GPT5X_PRO_REASONING_EFFORTS,\n\t\"gpt-5.4-pro\": GPT5X_PRO_REASONING_EFFORTS\n} as const;\n\nconst DEFAULT_SUGGESTION_MODEL: SuggestionModelConfig = {\n\tmodel: \"gpt-4.1-mini\"\n};\n\nexport type AngyConfigInput = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath?: string;\n\tapiKey?: string;\n\tsystemMessage?: string;\n\tsuggestionModel?: SuggestionModelConfig;\n\twatchIgnore?: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport function buildDefaultSystemMessage(sourceLocale: string, targetLocale: string) {\n\treturn `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;\n}\n\nfunction assertNonEmptyString(value: unknown, key: string) {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`[angy] ${key} is required and must be a non-empty string.`);\n\t}\n}\n\nfunction validateRoutePath(routePath: unknown) {\n\tif (routePath == null || routePath === \"\") return;\n\tif (typeof routePath !== \"string\" || !routePath.startsWith(\"/\")) {\n\t\tthrow new Error(`[angy] routePath must be an absolute path starting with \"/\".`);\n\t}\n}\n\nfunction validateWatchIgnore(watchIgnore: unknown) {\n\tif (watchIgnore == null) return;\n\tif (!Array.isArray(watchIgnore) || watchIgnore.some((item) => typeof item !== \"string\")) {\n\t\tthrow new Error(`[angy] watchIgnore must be an array of strings.`);\n\t}\n}\n\nfunction validateSuggestionProvider(suggestionProvider: unknown) {\n\tif (suggestionProvider == null) return;\n\tif (typeof suggestionProvider !== \"function\") {\n\t\tthrow new Error(`[angy] suggestionProvider must be a function.`);\n\t}\n}\n\nexport function isNonReasoningSuggestionModel(\n\tmodel: string\n): model is NonReasoningSuggestionModelName {\n\treturn NON_REASONING_SUGGESTION_MODELS.includes(model as NonReasoningSuggestionModelName);\n}\n\nfunction isSupportedSuggestionModel(model: string): model is SuggestionModelConfig[\"model\"] {\n\treturn (\n\t\tisNonReasoningSuggestionModel(model) ||\n\t\tObject.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model)\n\t);\n}\n\nexport function normalizeSuggestionModelConfig(\n\tinput: SuggestionModelConfig | undefined\n): SuggestionModelConfig {\n\tconst suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;\n\n\tif (typeof suggestionModel !== \"object\" || suggestionModel == null || Array.isArray(suggestionModel)) {\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel must be an object like { model: \"gpt-4.1-mini\" }.`\n\t\t);\n\t}\n\n\tif (typeof suggestionModel.model !== \"string\" || !suggestionModel.model.trim()) {\n\t\tthrow new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);\n\t}\n\n\tif (!isSupportedSuggestionModel(suggestionModel.model)) {\n\t\tthrow new Error(`[angy] Unsupported suggestionModel.model \"${suggestionModel.model}\".`);\n\t}\n\n\tif (isNonReasoningSuggestionModel(suggestionModel.model)) {\n\t\tif (\n\t\t\t!(\"reasoning\" in suggestionModel) ||\n\t\t\tsuggestionModel.reasoning === undefined ||\n\t\t\tsuggestionModel.reasoning === null\n\t\t) {\n\t\t\treturn { model: suggestionModel.model, reasoning: null };\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`\n\t\t);\n\t}\n\n\tconst allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];\n\tconst reasoning = suggestionModel.reasoning;\n\n\tif (reasoning == null) {\n\t\tif (suggestionModel.model === \"gpt-5-pro\") {\n\t\t\treturn { model: suggestionModel.model, reasoning: \"high\" };\n\t\t}\n\n\t\treturn { model: suggestionModel.model };\n\t}\n\n\tif (!allowedReasoning.includes(reasoning as (typeof allowedReasoning)[number])) {\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel.reasoning \"${reasoning}\" is not supported for ${suggestionModel.model}.`\n\t\t);\n\t}\n\n\treturn {\n\t\tmodel: suggestionModel.model,\n\t\treasoning\n\t} as SuggestionModelConfig;\n}\n\nconst config: TranslationHelperConfig = {\n\tbasePoPath: resolve(__dirname, \"../../locales/en.po\"),\n\tworkingPoPath: resolve(__dirname, \"../../locales/en-working.po\"),\n\tsourceLocale: \"sv\",\n\ttargetLocale: \"en\",\n\troutePath: \"/api/translations\",\n\tapiKey: \"\",\n\tsystemMessage: buildDefaultSystemMessage(\"sv\", \"en\"),\n\tsuggestionModel: DEFAULT_SUGGESTION_MODEL,\n\twatchIgnore: [\"**/en-working.po\"]\n};\n\nlet loadedConfigRoot: string | null = null;\nconst workingCatalogWatchControllers = new Set<(path: string, delayMs?: number) => void>();\n\nexport function configureTranslationHelper(next: Partial<TranslationHelperConfig>) {\n\tObject.assign(config, next);\n}\n\nexport function getTranslationHelperConfig() {\n\treturn config;\n}\n\nfunction normalizeFsPath(path: string) {\n\treturn normalize(path).replace(/\\\\/g, \"/\");\n}\n\nexport function inferLocaleFromCatalogPath(path: string) {\n\tconst fileName = basename(path);\n\tif (!fileName) return null;\n\treturn fileName.slice(0, fileName.length - extname(fileName).length) || null;\n}\n\nfunction resolveLocaleAlias(\n\tvalue: string,\n\tpaths: { basePoPath: string; workingPoPath: string }\n) {\n\tif (value === \"working\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.workingPoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from workingPoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\tif (value === \"base\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.basePoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from basePoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\treturn value;\n}\n\nfunction validateLocaleAliasUsage(sourceLocale: string, targetLocale: string) {\n\tif (sourceLocale === \"working\") {\n\t\tthrow new Error('[angy] sourceLocale cannot be \"working\". Use an explicit source locale.');\n\t}\n\n\tif (targetLocale === \"base\") {\n\t\tthrow new Error('[angy] targetLocale cannot be \"base\". Use an explicit target locale or \"working\".');\n\t}\n}\n\nfunction validateCatalogPathSemantics(next: TranslationHelperConfig) {\n\tconst baseLocale = inferLocaleFromCatalogPath(next.basePoPath);\n\tconst workingLocale = inferLocaleFromCatalogPath(next.workingPoPath);\n\tconst expectedWorkingLocale = `${next.targetLocale}-working`;\n\n\tif (baseLocale !== next.targetLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] basePoPath must point to the ${next.targetLocale}.po catalog. Received \"${baseLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n\n\tif (workingLocale !== expectedWorkingLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] workingPoPath must point to the ${expectedWorkingLocale}.po catalog. Received \"${workingLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n}\n\nexport function registerWorkingCatalogWatchController(\n\tcontroller: (path: string, delayMs?: number) => void\n) {\n\tworkingCatalogWatchControllers.add(controller);\n\treturn () => {\n\t\tworkingCatalogWatchControllers.delete(controller);\n\t};\n}\n\nexport function suspendWorkingCatalogWatch(path: string, delayMs = 800) {\n\tconst normalizedPath = normalizeFsPath(path);\n\tfor (const controller of workingCatalogWatchControllers) {\n\t\tcontroller(normalizedPath, delayMs);\n\t}\n}\n\nexport function normalizeTranslationHelperConfig(\n\troot: string,\n\tnext: TranslationHelperUserConfig\n): TranslationHelperUserConfig {\n\tconst normalized = { ...next };\n\n\tif (normalized.basePoPath && !isAbsolute(normalized.basePoPath)) {\n\t\tnormalized.basePoPath = resolve(root, normalized.basePoPath);\n\t}\n\n\tif (normalized.workingPoPath && !isAbsolute(normalized.workingPoPath)) {\n\t\tnormalized.workingPoPath = resolve(root, normalized.workingPoPath);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolveConfiguredLocaleAliases(\n\tnext: TranslationHelperConfig\n): TranslationHelperConfig {\n\tconst rawSourceLocale = next.sourceLocale;\n\tconst rawTargetLocale = next.targetLocale;\n\tconst resolvedSourceLocale = resolveLocaleAlias(rawSourceLocale, next);\n\tconst resolvedTargetLocale = resolveLocaleAlias(rawTargetLocale, next);\n\tconst usesDefaultSystemMessage =\n\t\tnext.systemMessage === buildDefaultSystemMessage(rawSourceLocale, rawTargetLocale);\n\n\tconst resolved = {\n\t\t...next,\n\t\tsourceLocale: resolvedSourceLocale,\n\t\ttargetLocale: resolvedTargetLocale,\n\t\tsystemMessage: usesDefaultSystemMessage\n\t\t\t? buildDefaultSystemMessage(resolvedSourceLocale, resolvedTargetLocale)\n\t\t\t: next.systemMessage\n\t};\n\n\tvalidateCatalogPathSemantics(resolved);\n\treturn resolved;\n}\n\nexport function completeAngyConfig(input: AngyConfigInput): TranslationHelperConfig {\n\tassertNonEmptyString(input.basePoPath, \"basePoPath\");\n\tassertNonEmptyString(input.workingPoPath, \"workingPoPath\");\n\tassertNonEmptyString(input.sourceLocale, \"sourceLocale\");\n\tassertNonEmptyString(input.targetLocale, \"targetLocale\");\n\tvalidateLocaleAliasUsage(input.sourceLocale, input.targetLocale);\n\tif (typeof input.apiKey !== \"string\" && typeof input.apiKey !== \"undefined\") {\n\t\tthrow new Error(`[angy] apiKey must be a string when provided. Use an empty string to disable suggestions.`);\n\t}\n\tvalidateRoutePath(input.routePath);\n\tvalidateWatchIgnore(input.watchIgnore);\n\tvalidateSuggestionProvider(input.suggestionProvider);\n\n\treturn {\n\t\tbasePoPath: input.basePoPath,\n\t\tworkingPoPath: input.workingPoPath,\n\t\tsourceLocale: input.sourceLocale,\n\t\ttargetLocale: input.targetLocale,\n\t\troutePath: input.routePath ?? \"/api/translations\",\n\t\tapiKey: input.apiKey ?? \"\",\n\t\tsystemMessage:\n\t\t\tinput.systemMessage ??\n\t\t\tbuildDefaultSystemMessage(input.sourceLocale, input.targetLocale),\n\t\tsuggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),\n\t\twatchIgnore: input.watchIgnore ?? [\"**/en-working.po\"],\n\t\tsuggestionProvider: input.suggestionProvider\n\t};\n}\n\nexport function defineAngyConfig(config: AngyConfigInput) {\n\treturn resolveConfiguredLocaleAliases(completeAngyConfig(config));\n}\n\nasync function fileExists(path: string) {\n\ttry {\n\t\tawait readFile(path);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function loadTsConfigModule(path: string) {\n\tconst source = await readFile(path, \"utf8\");\n\tconst transformed = await transform(source, {\n\t\tloader: \"ts\",\n\t\tformat: \"esm\",\n\t\ttarget: \"es2022\"\n\t});\n\tconst tempPath = `${path}.angy.tmp.mjs`;\n\tawait writeFile(tempPath, transformed.code, \"utf8\");\n\n\ttry {\n\t\treturn await import(/* @vite-ignore */ `${pathToFileURL(tempPath).href}?t=${Date.now()}`);\n\t} finally {\n\t\tawait unlink(tempPath).catch(() => undefined);\n\t}\n}\n\nasync function loadJsConfigModule(path: string) {\n\treturn import(/* @vite-ignore */ pathToFileURL(path).href);\n}\n\nexport async function loadAngyConfigFromRoot(root: string) {\n\tif (loadedConfigRoot === root) {\n\t\treturn config;\n\t}\n\n\tfor (const filename of CONFIG_FILENAMES) {\n\t\tconst fullPath = `${root}/${filename}`.replace(/\\\\/g, \"/\");\n\t\tif (!(await fileExists(fullPath))) continue;\n\n\t\tconst module =\n\t\t\tfilename.endsWith(\".ts\")\n\t\t\t\t? await loadTsConfigModule(fullPath)\n\t\t\t\t: await loadJsConfigModule(fullPath);\n\t\tconst raw = (module.default ?? module.config ?? {}) as TranslationHelperUserConfig;\n\t\tconst next = resolveConfiguredLocaleAliases(\n\t\t\tnormalizeTranslationHelperConfig(root, completeAngyConfig(raw as AngyConfigInput))\n\t\t);\n\t\tconfigureTranslationHelper(next);\n\t\tloadedConfigRoot = root;\n\t\treturn config;\n\t}\n\n\tloadedConfigRoot = root;\n\treturn config;\n}\n", "import { json } from \"@sveltejs/kit\";\nimport {\n\tbuildLookupVariants,\n\tcatalogEntryKey,\n\tcreateFuse,\n\tCatalogIntegrityError,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nimport type { NormalizedEntry } from \"./types.ts\";\n\r\nconst DIRECT_MATCH_LIMIT = 5;\nconst MAX_ALTERNATIVES = 300;\nconst TRANSLATED_TARGET = Math.floor(MAX_ALTERNATIVES * 0.2);\nconst UNTRANSLATED_TARGET = MAX_ALTERNATIVES - TRANSLATED_TARGET;\nconst UNTRANSLATED_RANK_BONUS = 0.03;\n\r\ntype Candidate = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\ntype ScoredEntry = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\nexport function entryKey(msgid: string, msgctxt: string | null) {\r\n\treturn catalogEntryKey(msgid, msgctxt);\r\n}\r\n\r\nfunction normalizeCurrentPath(currentPath: string) {\r\n\ttry {\r\n\t\treturn new URL(currentPath, \"http://localhost\").pathname;\r\n\t} catch {\r\n\t\treturn currentPath;\r\n\t}\r\n}\r\n\r\nfunction inferPageReference(currentPath: string) {\r\n\tconst normalizedPath = normalizeCurrentPath(currentPath);\r\n\tif (normalizedPath === \"/\") {\r\n\t\treturn \"src/routes/+page.svelte\";\r\n\t}\r\n\r\n\tconst trimmedPath = normalizedPath.replace(/\\/+$/, \"\");\r\n\treturn `src/routes${trimmedPath}/+page.svelte`;\r\n}\r\n\r\nfunction routeLooksRelevant(routeReference: string, refs: string[], extractedComments: string[]) {\r\n\tif (!routeReference) return false;\r\n\r\n\tconst haystack = [...refs, ...extractedComments].join(\" \").toLowerCase();\r\n\treturn haystack.includes(routeReference.toLowerCase());\r\n}\r\n\r\nfunction dedupeCandidates(candidates: Candidate[]) {\r\n\tconst deduped = new Map<string, Candidate>();\r\n\r\n\tfor (const item of candidates) {\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tconst existing = deduped.get(id);\r\n\t\tif (!existing || item.score < existing.score) {\r\n\t\t\tdeduped.set(id, item);\r\n\t\t}\r\n\t}\r\n\r\n\treturn [...deduped.values()];\r\n}\r\n\r\nfunction rankDirectMatches(candidates: Candidate[], routeReference: string) {\n\treturn [...candidates].sort((left, right) => {\n\t\tconst leftRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tleft.entry.references,\r\n\t\t\tleft.entry.extractedComments\r\n\t\t)\r\n\t\t\t? -0.08\r\n\t\t\t: 0;\r\n\t\tconst rightRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tright.entry.references,\n\t\t\tright.entry.extractedComments\n\t\t)\n\t\t\t? -0.08\n\t\t\t: 0;\n\t\tconst leftUntranslated = !left.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\t\tconst rightUntranslated = !right.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\n\t\treturn left.score + leftRoute + leftUntranslated - (right.score + rightRoute + rightUntranslated);\n\t});\n}\n\r\nfunction getReferenceTokens(reference: string) {\r\n\treturn reference\r\n\t\t.toLowerCase()\r\n\t\t.split(/[\\/\\[\\].:_-]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nfunction getReferenceSimilarity(reference: string, routeReference: string) {\r\n\tconst referenceTokens = new Set(getReferenceTokens(reference));\r\n\tconst routeTokens = getReferenceTokens(routeReference);\r\n\r\n\tif (!routeTokens.length || !referenceTokens.size) return 0;\r\n\r\n\tlet matches = 0;\r\n\tfor (const token of routeTokens) {\r\n\t\tif (referenceTokens.has(token)) {\r\n\t\t\tmatches += 1;\r\n\t\t}\r\n\t}\r\n\r\n\treturn matches / routeTokens.length;\r\n}\r\n\r\nfunction collectSharedReferenceAlternatives(\n\tentries: NormalizedEntry[],\n\tbestEntry: NormalizedEntry,\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\r\n\tconst sharedReferences = new Set(bestEntry.references.map((reference) => reference.toLowerCase()));\r\n\r\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceMatches = entry.references.filter((reference) =>\r\n\t\t\t\tsharedReferences.has(reference.toLowerCase())\r\n\t\t\t);\r\n\t\t\tconst routeMatch = entry.references.some((reference) =>\r\n\t\t\t\treference.toLowerCase().includes(routeReference.toLowerCase())\r\n\t\t\t);\r\n\r\n\t\t\tif (!referenceMatches.length && !routeMatch) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\r\n\t\t\tconst referenceSimilarity = Math.max(\n\t\t\t\t0,\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\n\t\t\t);\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.35 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore:\n\t\t\t\t\treferenceMatches.length * 100 +\n\t\t\t\t\t(routeMatch ? 25 : 0) +\n\t\t\t\t\treferenceSimilarity +\n\t\t\t\t\tuntranslatedBonus,\n\t\t\t\tvariant: \"shared-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectRouteFillAlternatives(\n\tentries: NormalizedEntry[],\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceSimilarity = Math.max(\r\n\t\t\t\t0,\r\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\r\n\t\t\t);\r\n\r\n\t\t\tif (referenceSimilarity <= 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.2 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore: referenceSimilarity + untranslatedBonus,\n\t\t\t\tvariant: \"route-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectTranslatedCohesionAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"translated-cohesion\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectUntranslatedSimilarityAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => !item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"untranslated-similarity\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction buildAlternativePool(\r\n\ttopDirectAlternatives: Candidate[],\r\n\tsharedReferenceAlternatives: ScoredEntry[],\r\n\trouteFillAlternatives: ScoredEntry[],\r\n\ttranslatedCohesionAlternatives: ScoredEntry[],\r\n\tuntranslatedSimilarityAlternatives: ScoredEntry[]\r\n) {\r\n\tconst alternatives: ScoredEntry[] = [...topDirectAlternatives];\r\n\tconst selectedKeys = new Set<string>(\r\n\t\ttopDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t);\r\n\r\n\tlet translatedCount = topDirectAlternatives.filter((item) => item.entry.hasTranslation).length;\r\n\tlet untranslatedCount = topDirectAlternatives.length - translatedCount;\r\n\r\n\tconst tryAdd = (item: ScoredEntry, honorQuota = true) => {\r\n\t\tif (alternatives.length >= MAX_ALTERNATIVES) return false;\r\n\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tif (selectedKeys.has(id)) return false;\r\n\r\n\t\tif (honorQuota) {\r\n\t\t\tif (item.entry.hasTranslation && translatedCount >= TRANSLATED_TARGET) return false;\r\n\t\t\tif (!item.entry.hasTranslation && untranslatedCount >= UNTRANSLATED_TARGET) return false;\r\n\t\t}\r\n\r\n\t\tselectedKeys.add(id);\r\n\t\talternatives.push(item);\r\n\r\n\t\tif (item.entry.hasTranslation) {\r\n\t\t\ttranslatedCount += 1;\r\n\t\t} else {\r\n\t\t\tuntranslatedCount += 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t};\r\n\r\n\tconst prioritizedSources = [\r\n\t\tsharedReferenceAlternatives,\r\n\t\trouteFillAlternatives,\r\n\t\tuntranslatedSimilarityAlternatives,\r\n\t\ttranslatedCohesionAlternatives\r\n\t];\r\n\r\n\tfor (const source of prioritizedSources) {\r\n\t\tfor (const item of source) {\r\n\t\t\ttryAdd(item, true);\r\n\t\t}\r\n\t}\r\n\r\n\tif (alternatives.length < MAX_ALTERNATIVES) {\r\n\t\tfor (const source of prioritizedSources) {\r\n\t\t\tfor (const item of source) {\r\n\t\t\t\ttryAdd(item, false);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn alternatives.slice(0, MAX_ALTERNATIVES);\r\n}\r\n\r\nexport async function findTranslationContext(\n\tkey: string,\n\tcurrentPath: string\n) {\n\tconst catalogPair = await readCatalogPair({ ensureWorking: true });\n\tconst effectiveEntries = catalogPair.baseEntries.map((entry) => {\n\t\tconst state = resolveTranslationState(entry, catalogPair.workingMap.get(entryKey(entry.msgid, entry.msgctxt)));\n\t\treturn {\n\t\t\t...state.effectiveEntry,\n\t\t\thasTranslation: state.hasTranslation,\n\t\t\tisFuzzy: state.isFuzzy,\n\t\t\ttranslationOrigin: state.translationOrigin\n\t\t};\n\t});\n\tconst effectiveIndex = {\n\t\tentries: effectiveEntries,\n\t\tfuse: createFuse(effectiveEntries)\n\t};\n\n\tconst routeReference = inferPageReference(currentPath);\n\tconst directMatches = rankDirectMatches(\n\t\tdedupeCandidates(\n\t\t\tbuildLookupVariants(key).flatMap((variant) =>\n\t\t\t\teffectiveIndex.fuse.search(variant, { limit: 180 }).map((hit) => ({\n\t\t\t\t\tentry: hit.item,\n\t\t\t\t\tscore: hit.score ?? 1,\n\t\t\t\t\tvariant\n\t\t\t\t}))\n\t\t\t)\n\t\t),\r\n\t\trouteReference\r\n\t);\r\n\r\n\tconst best = directMatches[0];\r\n\tif (!best || best.score > 0.42) {\n\t\treturn null;\n\t}\n\n\tconst effectiveDirectMatches = directMatches;\n\tconst bestEffective = effectiveDirectMatches[0];\n\tconst topDirectAlternatives = effectiveDirectMatches.slice(1, DIRECT_MATCH_LIMIT);\n\tconst excludedKeys = new Set<string>([\r\n\t\tentryKey(best.entry.msgid, best.entry.msgctxt),\r\n\t\t...topDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t]);\r\n\r\n\tconst sharedReferenceAlternatives = collectSharedReferenceAlternatives(\r\n\t\teffectiveEntries,\r\n\t\tbestEffective.entry,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst routeFillAlternatives = collectRouteFillAlternatives(\r\n\t\teffectiveEntries,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst translatedCohesionAlternatives = collectTranslatedCohesionAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst untranslatedSimilarityAlternatives = collectUntranslatedSimilarityAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\treturn {\n\t\tbest,\n\t\talternatives: buildAlternativePool(\n\t\t\ttopDirectAlternatives,\r\n\t\t\tsharedReferenceAlternatives,\r\n\t\t\trouteFillAlternatives,\r\n\t\t\ttranslatedCohesionAlternatives,\r\n\t\t\tuntranslatedSimilarityAlternatives\n\t\t),\n\t\trouteReference,\n\t\tbaseMap: catalogPair.baseMap,\n\t\tworkingMap: catalogPair.workingMap,\n\t\tcatalogState: {\n\t\t\tstatus: \"ok\" as const,\n\t\t\taffectedCount: catalogPair.rotationImpact.length\n\t\t}\n\t};\n}\n\nexport function toPublicEntry(\n\tentry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tmsgidPlural: entry.msgidPlural,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\treferences: entry.references,\n\t\textractedComments: entry.extractedComments,\n\t\tflags: state.effectiveEntry.flags,\n\t\tprevious: state.effectiveEntry.previous,\n\t\tobsolete: state.effectiveEntry.obsolete,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\nexport function toPublicAlternative(\n\tentry: NormalizedEntry,\n\tscore: number,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tscore,\n\t\treferences: entry.references,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\r\nexport async function handleContext(request: Request) {\r\n\tconst data = await request.formData();\r\n\tconst key = data.get(\"translationKey\")?.toString().trim();\r\n\tconst currentPath = data.get(\"currentPath\")?.toString().trim();\r\n\r\n\tif (!key || !currentPath) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"translationKey and currentPath are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tlet baseFound;\n\ttry {\n\t\tbaseFound = await findTranslationContext(key, currentPath);\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tif (!baseFound) {\n\t\treturn json(\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Translation key context not found\"\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst bestBase =\n\t\tbaseFound.baseMap.get(entryKey(baseFound.best.entry.msgid, baseFound.best.entry.msgctxt)) ??\n\t\tbaseFound.best.entry;\n\tconst bestWorking = baseFound.workingMap.get(entryKey(bestBase.msgid, bestBase.msgctxt));\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmatch: {\n\t\t\tscore: baseFound.best.score,\n\t\t\tvia: baseFound.best.variant\n\t\t},\n\t\tcatalogState: baseFound.catalogState,\n\t\tentry: toPublicEntry(bestBase, bestWorking),\n\t\talternatives: baseFound.alternatives.map((item) => {\n\t\t\tconst baseAlt =\n\t\t\t\tbaseFound.baseMap.get(entryKey(item.entry.msgid, item.entry.msgctxt)) ?? item.entry;\n\t\t\tconst workingAlt = baseFound.workingMap.get(entryKey(baseAlt.msgid, baseAlt.msgctxt));\n\t\t\treturn toPublicAlternative(baseAlt, item.score, workingAlt);\n\t\t})\n\t});\n}\n", "import { json } from \"@sveltejs/kit\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\nimport type {\n\tSuggestionModelConfig,\n\tSuggestionRequestItem,\n\tSuggestionResponseItem\n} from \"./config.ts\";\nimport { getTranslationHelperConfig } from \"./config.ts\";\n\r\nfunction getEntriesForSuggestions(contextResult: TranslationContextResult) {\r\n\treturn [contextResult.entry, ...contextResult.alternatives];\r\n}\r\n\r\nfunction getTranslatedExamples(contextResult: TranslationContextResult) {\r\n\treturn getEntriesForSuggestions(contextResult)\r\n\t\t.filter((entry) => entry.hasTranslation && entry.msgstr?.[0])\r\n\t\t.slice(0, 30)\r\n\t\t.map((entry) => ({\r\n\t\t\tmsgid: entry.msgid,\r\n\t\t\tmsgctxt: entry.msgctxt,\r\n\t\t\ttranslation: entry.msgstr[0]\r\n\t\t}));\r\n}\r\n\r\nfunction buildUserMessage(\r\n\tcontextResult: TranslationContextResult,\r\n\titems: SuggestionRequestItem[]\r\n) {\r\n\treturn JSON.stringify(\r\n\t\t{\r\n\t\t\ttask: \"Return translation suggestions for the untranslated UI strings.\",\r\n\t\t\toutput_format: {\r\n\t\t\t\titems: [\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmsgid: \"source string\",\r\n\t\t\t\t\t\tmsgctxt: \"optional context or null\",\r\n\t\t\t\t\t\tsuggestion: \"translated suggestion\"\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\ttranslated_examples: getTranslatedExamples(contextResult),\r\n\t\t\tuntranslated_items: items\r\n\t\t},\r\n\t\tnull,\r\n\t\t2\r\n\t);\r\n}\r\n\r\nfunction parseSuggestions(responseText: string): SuggestionResponseItem[] {\n\ttry {\r\n\t\tconst parsed = JSON.parse(responseText);\r\n\t\tconst items = Array.isArray(parsed?.items) ? parsed.items : [];\r\n\t\treturn items.filter(\r\n\t\t\t(item): item is SuggestionResponseItem =>\r\n\t\t\t\ttypeof item?.msgid === \"string\" &&\r\n\t\t\t\t(item.msgctxt === null || typeof item.msgctxt === \"string\") &&\r\n\t\t\t\ttypeof item?.suggestion === \"string\"\r\n\t\t);\r\n\t} catch {\r\n\t\treturn [];\r\n\t}\n}\n\nexport function buildSuggestionRequestBody({\n\tcontext,\n\titems,\n\tmodelConfig,\n\tsystemMessage\n}: {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tmodelConfig: SuggestionModelConfig;\n\tsystemMessage: string;\n}) {\n\tconst body: Record<string, unknown> = {\n\t\tmodel: modelConfig.model,\n\t\tinput: [\n\t\t\t{\n\t\t\t\trole: \"system\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: systemMessage }]\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: buildUserMessage(context, items) }]\n\t\t\t}\n\t\t],\n\t\ttext: {\n\t\t\tformat: {\n\t\t\t\ttype: \"json_schema\",\n\t\t\t\tname: \"translation_suggestions\",\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tmsgid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\tmsgctxt: { type: [\"string\", \"null\"] },\n\t\t\t\t\t\t\t\t\tsuggestion: { type: \"string\" }\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"msgid\", \"msgctxt\", \"suggestion\"]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"items\"]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tif (modelConfig.reasoning && modelConfig.reasoning !== \"none\") {\n\t\tbody.reasoning = {\n\t\t\teffort: modelConfig.reasoning\n\t\t};\n\t}\n\n\treturn body;\n}\n\nexport async function handleSuggestions(request: Request) {\n\tconst {\n\t\tapiKey,\n\t\tsuggestionModel,\n\t\tsystemMessage,\n\t\tsourceLocale,\n\t\ttargetLocale,\n\t\tsuggestionProvider\n\t} = getTranslationHelperConfig();\n\n\tconst data = await request.json().catch(() => null);\n\tconst context = data?.context as TranslationContextResult | undefined;\n\tconst items = Array.isArray(data?.items) ? (data.items as SuggestionRequestItem[]) : [];\n\r\n\tif (!context || !items.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"context and items are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\n\t\t);\n\t}\n\n\tif (suggestionProvider) {\n\t\tconst providedItems = await suggestionProvider({\n\t\t\tcontext,\n\t\t\titems,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\tsystemMessage,\n\t\t\tsuggestionModel,\n\t\t\tapiKey\n\t\t});\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: providedItems\n\t\t});\n\t}\n\n\tif (!apiKey.trim()) {\n\t\tconsole.warn(\"[angy] Suggestions disabled because apiKey is empty.\");\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tdisabled: true,\n\t\t\titems: []\n\t\t});\n\t}\n\n\ttry {\n\t\tconsole.info(\"[angy] Suggestion request starting.\", {\n\t\t\tmodel: suggestionModel.model,\n\t\t\treasoning: suggestionModel.reasoning ?? null,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\titemCount: items.length,\n\t\t\thasApiKey: Boolean(apiKey.trim())\n\t\t});\n\n\t\tconst response = await fetch(\"https://api.openai.com/v1/responses\", {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"content-type\": \"application/json\"\n\t\t\t},\n\t\t\tbody: JSON.stringify(\n\t\t\t\tbuildSuggestionRequestBody({\n\t\t\t\t\tcontext,\n\t\t\t\t\titems,\n\t\t\t\t\tmodelConfig: suggestionModel,\n\t\t\t\t\tsystemMessage\n\t\t\t\t})\n\t\t\t)\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\tconsole.error(\"[angy] Suggestion request failed.\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\tbody: errorBody\n\t\t\t});\n\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Suggestion request failed: ${response.status}`\n\t\t\t\t},\n\t\t\t\t{ status: 502 }\n\t\t\t);\n\t\t}\n\n\t\tconst responseJson = await response.json();\n\tconst responseText =\n\t\tresponseJson?.output_text ??\n\t\tresponseJson?.output\n\t\t\t?.flatMap((item: any) => item?.content ?? [])\n\t\t\t.find((part: any) => part?.text)?.text ??\n\t\t\"\";\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: parseSuggestions(responseText)\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(\"[angy] Suggestion request threw.\", error);\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Suggestion request failed before reaching the model\"\n\t\t\t},\n\t\t\t{ status: 502 }\n\t\t);\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["json", "basename", "readFile", "writeFile", "dirname", "extname", "basename", "config", "fileExists", "fileExists", "readFile", "extname", "writeFile", "dirname", "basename", "basename", "json", "json", "json", "json", "json"]
|
|
7
7
|
}
|