@walkinissue/angy 0.2.17

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # Angy
2
+
3
+ Dev-only SvelteKit translation helper for in-app PO workflow.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @walkinissue/angy
9
+ ```
10
+
11
+ ## What it is
12
+
13
+ - A QA widget for in-app translation work
14
+ - A PO catalog lookup and commit layer
15
+ - A suggestion pipeline for untranslated strings
16
+ - A SvelteKit-friendly integration shape
17
+
18
+ ## Packages used
19
+
20
+ - `svelte`
21
+ UI for the helper widget
22
+ - `@sveltejs/kit`
23
+ Route handler shape and dev/server integration
24
+ - `wuchale`
25
+ Runtime i18n layer and locale switching
26
+ - `gettext-parser`
27
+ Read and write `.po` catalogs
28
+ - `fuse.js`
29
+ Fuzzy lookup for key discovery
30
+ - `esbuild`
31
+ Load `angy.config.ts` in dev and server contexts
32
+ - OpenAI API
33
+ Optional translation suggestions
34
+
35
+ ## What problem it solves
36
+
37
+ - Large apps are painful to internationalize late
38
+ - Many strings have weak or missing context
39
+ - Repeated copy appears in many components
40
+ - PO workflows are slow when you must search manually
41
+ - Existing i18n libraries solve extraction/runtime, not translator workflow
42
+ - Teams need a way to select text in the app, inspect context, and commit safely
43
+
44
+ ## Why it exists
45
+
46
+ - Plain catalog editing was too slow
47
+ - Context from extraction alone was not enough
48
+ - Similar strings caused ambiguity
49
+ - Repeated strings needed reference-aware lookup
50
+ - Suggestion workflows needed to stay inside the app
51
+ - A dev-only helper reduced friction enough to keep the migration moving
52
+
53
+ ## Workflow
54
+
55
+ - Developer runs app in dev
56
+ - Developer mounts `<Angy />` in layout
57
+ - App exports `POST` from the package handler
58
+ - User selects text or uses the helper trigger in the UI
59
+ - Client asks server for PO context and alternatives
60
+ - Server matches best key, expands related entries, and returns alternatives
61
+ - User edits, stages, tabs through unresolved strings, and commits
62
+ - Suggestions can be requested for untranslated strings
63
+ - Commits are written to the working catalog
64
+
65
+ ## Integration
66
+
67
+ Use the plugin for shared config, then mount the component and define the route yourself.
68
+
69
+ ```ts
70
+ // vite.config.ts
71
+ import { defineConfig } from "vite";
72
+ import { sveltekit } from "@sveltejs/kit/vite";
73
+ import { angy } from "@walkingissue/angy/plugin";
74
+
75
+ export default defineConfig({
76
+ plugins: [angy(), sveltekit()]
77
+ });
78
+ ```
79
+
80
+ ```ts
81
+ // angy.config.ts
82
+ import { defineAngyConfig } from "@walkingissue/angy/server";
83
+
84
+ export default defineAngyConfig({
85
+ basePoPath: "./src/locales/en.po",
86
+ workingPoPath: "./src/locales/en-working.po",
87
+ sourceLocale: "sv",
88
+ targetLocale: "en",
89
+ routePath: "/api/translations",
90
+ apiKey: "",
91
+ watchIgnore: ["**/locales/en-working.po"]
92
+ });
93
+ ```
94
+
95
+ ```svelte
96
+ <!-- src/routes/+layout.svelte -->
97
+ <script lang="ts">
98
+ import { dev } from "$app/environment";
99
+ import { Angy } from "@walkingissue/angy";
100
+ </script>
101
+
102
+ {#if dev}
103
+ <Angy />
104
+ {/if}
105
+
106
+ <slot />
107
+ ```
108
+
109
+ ```ts
110
+ // src/routes/api/translations/+server.ts
111
+ export { handler as POST } from "@walkingissue/angy/server";
112
+ ```
113
+
114
+ ## Catalog model
115
+
116
+ - `en.po` is the base catalog
117
+ - `en-working.po` is the mutable working catalog
118
+ - Base catalog is the source of truth for valid keys
119
+ - Working catalog is the source of truth for current translation state
120
+ - Lookup reads working state first
121
+ - Commit validates against base, then writes to working
122
+
123
+ ## Discovery algorithm
124
+
125
+ - User sends selected text and current route path
126
+ - Server creates lookup variants from the selected text
127
+ - Fuzzy search runs against the effective working view of the catalog
128
+ - Best match must clear a score threshold
129
+ - Top 4 similar direct matches are kept
130
+ - Server infers a page reference like `src/routes/.../+page.svelte`
131
+ - It then pulls entries that share PO references with the best match
132
+ - It then pulls entries whose references look similar to the current route
133
+ - If space remains, it fills with similar unresolved strings
134
+ - It also keeps a smaller translated slice for cohesion
135
+
136
+ ## Why the lookup works like this
137
+
138
+ - Direct fuzzy match alone is not enough
139
+ - Repeated UI copy often exists in several components
140
+ - Shared PO references are strong local context
141
+ - Route similarity helps reconstruct page-level context
142
+ - A translated slice helps keep wording consistent
143
+ - An untranslated-heavy pool focuses effort where work remains
144
+
145
+ ## Retrieval targets
146
+
147
+ - Up to 300 alternatives
148
+ - Roughly 80% untranslated
149
+ - Roughly 20% already translated
150
+ - Enough translated context for tone and syntax
151
+ - Enough untranslated context for efficient batch work
152
+
153
+ ## Suggestions
154
+
155
+ - Suggestions are dev-only
156
+ - Server-side request to OpenAI
157
+ - Prompt aims to preserve placeholders, markup, casing, and product terminology
158
+ - Existing translations are used as style anchors
159
+ - Suggestions are cached client-side to avoid repeat requests
160
+ - Built-in suggestions are disabled when `apiKey` is empty
161
+ - Consumers can replace the suggestion pipeline with `suggestionProvider`
162
+
163
+ ## Config
164
+
165
+ | Key | Required | Default | Notes |
166
+ | --- | --- | --- | --- |
167
+ | `basePoPath` | Yes | None | Path to base catalog |
168
+ | `workingPoPath` | Yes | None | Path to working catalog |
169
+ | `sourceLocale` | Yes | None | Source language for suggestions. Can be set to `"base"` to infer from `basePoPath` |
170
+ | `targetLocale` | Yes | None | Target language for suggestions. Can be set to `"working"` to infer from `workingPoPath` |
171
+ | `routePath` | No | `/api/translations` | Route used by the helper client and consumer server handler |
172
+ | `apiKey` | Yes | None | Used by built-in suggestion pipeline. If empty, suggestions are disabled |
173
+ | `systemMessage` | No | Built from locales | Default AI system prompt |
174
+ | `suggestionModel` | No | `gpt-4.1-mini` | Cheap default to avoid cost surprises |
175
+ | `watchIgnore` | No | `["**/en-working.po"]` | Extra Vite watch ignore patterns |
176
+ | `suggestionProvider` | No | None | Custom suggestion pipeline hook |
177
+
178
+ ## Route config
179
+
180
+ - The server route path is defined by the consumer app
181
+ - The default route path is `/api/translations`
182
+ - `routePath` lives in `angy.config.ts`
183
+ - The `Angy` `endpoint` prop is optional
184
+ - Use the prop only if you want to override `routePath` per instance
185
+
186
+ ## Custom suggestion provider
187
+
188
+ Use `suggestionProvider` if you want your own AI pipeline.
189
+
190
+ Input:
191
+
192
+ - `context`
193
+ - `items`
194
+ - `sourceLocale`
195
+ - `targetLocale`
196
+ - `systemMessage`
197
+ - `model`
198
+ - `apiKey`
199
+
200
+ Return:
201
+
202
+ - `Array<{ msgid: string; msgctxt: string | null; suggestion: string }>`
203
+
204
+ Example:
205
+
206
+ ```ts
207
+ import { defineAngyConfig, type SuggestionProvider } from "@walkingissue/angy/server";
208
+
209
+ const suggestionProvider: SuggestionProvider = async ({ items }) => {
210
+ return items.map((item) => ({
211
+ msgid: item.msgid,
212
+ msgctxt: item.msgctxt,
213
+ suggestion: `TODO: ${item.msgid}`
214
+ }));
215
+ };
216
+
217
+ export default defineAngyConfig({
218
+ basePoPath: "./src/locales/en.po",
219
+ workingPoPath: "./src/locales/en-working.po",
220
+ sourceLocale: "sv",
221
+ targetLocale: "en",
222
+ routePath: "/api/translations",
223
+ apiKey: "",
224
+ suggestionProvider
225
+ });
226
+ ```
227
+
228
+ ## Exports
229
+
230
+ - `Angy` from `@walkingissue/angy`
231
+ - `handler` and `defineAngyConfig` from `@walkingissue/angy/server`
232
+ - `angy` from `@walkingissue/angy/plugin`
233
+
234
+ ## Notes
235
+
236
+ - The default `handler` returns a `404` outside dev.
237
+ - The package still has fallback internal paths, but consumers should define their own explicit catalog paths.
238
+ - Override paths, locales, `routePath`, `apiKey`, `systemMessage`, `suggestionModel`, `watchIgnore`, and `suggestionProvider` through `angy.config.ts`.
239
+ - The default export surface is intentionally small: plugin, component, config helper, and server handler.
240
+
241
+ ## Future work
242
+
243
+ - Handle server paths for single config and single app
244
+ - Support multiple catalogs
245
+ - Support multiple translations from a single source locale
246
+ - This seems trivially implemented with the current setup