@walkinissue/angy 0.2.17 → 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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # Angy
2
2
 
3
- Dev-only SvelteKit translation helper for in-app PO workflow.
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,69 +10,36 @@ Dev-only SvelteKit translation helper for in-app PO workflow.
8
10
  npm install @walkinissue/angy
9
11
  ```
10
12
 
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
13
+ ## What it does
14
+
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
18
+ - request AI suggestions for untranslated strings
19
+ - rotate locales for visual QA
20
+
21
+ ## Quick flow
22
+
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.
28
+
29
+ ## Docs site
30
+
31
+ - [Showcase and docs](https://walkingissue.github.io/Angy/)
32
+ - [Release notes](./docs/changelog/0.2.19.md)
64
33
 
65
34
  ## Integration
66
35
 
67
- Use the plugin for shared config, then mount the component and define the route yourself.
36
+ Use the plugin for dev glue, mount the component, and expose the server handler.
68
37
 
69
38
  ```ts
70
39
  // vite.config.ts
71
40
  import { defineConfig } from "vite";
72
41
  import { sveltekit } from "@sveltejs/kit/vite";
73
- import { angy } from "@walkingissue/angy/plugin";
42
+ import { angy } from "@walkinissue/angy/plugin";
74
43
 
75
44
  export default defineConfig({
76
45
  plugins: [angy(), sveltekit()]
@@ -79,7 +48,7 @@ export default defineConfig({
79
48
 
80
49
  ```ts
81
50
  // angy.config.ts
82
- import { defineAngyConfig } from "@walkingissue/angy/server";
51
+ import { defineAngyConfig } from "@walkinissue/angy/server";
83
52
 
84
53
  export default defineAngyConfig({
85
54
  basePoPath: "./src/locales/en.po",
@@ -88,7 +57,7 @@ export default defineAngyConfig({
88
57
  targetLocale: "en",
89
58
  routePath: "/api/translations",
90
59
  apiKey: "",
91
- watchIgnore: ["**/locales/en-working.po"]
60
+ suggestionModel: { model: "gpt-4.1-mini" }
92
61
  });
93
62
  ```
94
63
 
@@ -96,151 +65,65 @@ export default defineAngyConfig({
96
65
  <!-- src/routes/+layout.svelte -->
97
66
  <script lang="ts">
98
67
  import { dev } from "$app/environment";
99
- import { Angy } from "@walkingissue/angy";
68
+ import { Angy } from "@walkinissue/angy";
69
+
70
+ let { children } = $props();
100
71
  </script>
101
72
 
102
73
  {#if dev}
103
74
  <Angy />
104
75
  {/if}
105
76
 
106
- <slot />
77
+ {@render children?.()}
107
78
  ```
108
79
 
109
80
  ```ts
110
81
  // src/routes/api/translations/+server.ts
111
- export { handler as POST } from "@walkingissue/angy/server";
82
+ export { handler as POST } from "@walkinissue/angy/server";
112
83
  ```
113
84
 
114
85
  ## Catalog model
115
86
 
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`
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
162
92
 
163
93
  ## Config
164
94
 
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
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.
109
+
110
+ ## Frequent issues
111
+
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.
116
+ - Rotation shows a warning modal
117
+ - That is a preview of what rotation will promote into base.
118
+ - Angy refuses to operate with a catalog integrity error
119
+ - Regenerate or replace the working catalog.
120
+
121
+ ## Docs
122
+
123
+ - [Release notes](./docs/changelog/0.2.19.md)
124
+ - [Roadmap](./docs/roadmap.md)
125
+ - [Wuchale runtime wiring](./docs/wuchale-runtime.md)
126
+
127
+ ## License
128
+
129
+ MIT