prosebook 0.3.0 → 0.5.0
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 +357 -26
- package/dist/bind-BIZNv8nU.js +62 -0
- package/dist/bind-BIZNv8nU.js.map +1 -0
- package/dist/bind-ZS86jHXk.d.ts +31 -0
- package/dist/bind-ZS86jHXk.d.ts.map +1 -0
- package/dist/bun.d.ts +12 -3
- package/dist/bun.d.ts.map +1 -1
- package/dist/bun.js +77 -26
- package/dist/bun.js.map +1 -1
- package/dist/{bundle-KD-DiiFa.d.ts → bundle-CLHY_97D.d.ts} +3 -3
- package/dist/bundle-CLHY_97D.d.ts.map +1 -0
- package/dist/bundle-Ut6Y2X6N.js +217 -0
- package/dist/bundle-Ut6Y2X6N.js.map +1 -0
- package/dist/cli.js +55 -12
- package/dist/cli.js.map +1 -1
- package/dist/{config-BPSmoYel.js → config-7T4hX_f7.js} +13 -5
- package/dist/{config-BPSmoYel.js.map → config-7T4hX_f7.js.map} +1 -1
- package/dist/{config-DdMkKt_N.d.ts → config-CzaJJQ60.d.ts} +19 -5
- package/dist/{config-DdMkKt_N.d.ts.map → config-CzaJJQ60.d.ts.map} +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/{copy-B3V_QRkT.d.ts → copy-BXn0aYQn.d.ts} +37 -3
- package/dist/copy-BXn0aYQn.d.ts.map +1 -0
- package/dist/copy-Bv6S-Hr2.js +235 -0
- package/dist/copy-Bv6S-Hr2.js.map +1 -0
- package/dist/endpoints-Dhgowovz.js +217 -0
- package/dist/endpoints-Dhgowovz.js.map +1 -0
- package/dist/generate-1BeQAd1O.d.ts +72 -0
- package/dist/generate-1BeQAd1O.d.ts.map +1 -0
- package/dist/{generate-9pR3i5Bj.js → generate-CKv86Iki.js} +1724 -627
- package/dist/generate-CKv86Iki.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/next-loader.d.ts +9 -2
- package/dist/next-loader.d.ts.map +1 -1
- package/dist/next-loader.js +12 -3
- package/dist/next-loader.js.map +1 -1
- package/dist/next.d.ts +11 -3
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +94 -21
- package/dist/next.js.map +1 -1
- package/dist/preact/index.d.ts +34 -0
- package/dist/preact/index.d.ts.map +1 -0
- package/dist/preact/index.js +72 -0
- package/dist/preact/index.js.map +1 -0
- package/dist/react/index.d.ts +5 -14
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +9 -33
- package/dist/react/index.js.map +1 -1
- package/dist/react/server.d.ts +1 -1
- package/dist/react/server.js +1 -1
- package/dist/{sarif-CWyTAQ0y.js → sarif-BnwYC78_.js} +8 -98
- package/dist/sarif-BnwYC78_.js.map +1 -0
- package/dist/{scan-Bx0_yDSi.js → scan-C0ap4Mpp.js} +145 -20
- package/dist/scan-C0ap4Mpp.js.map +1 -0
- package/dist/solid/index.d.ts +29 -0
- package/dist/solid/index.d.ts.map +1 -0
- package/dist/solid/index.js +48 -0
- package/dist/solid/index.js.map +1 -0
- package/dist/svelte/Text.svelte +13 -0
- package/dist/svelte/Text.svelte.d.ts +20 -0
- package/dist/svelte/index.d.ts +31 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +35 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/tools.d.ts +44 -65
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +5 -5
- package/dist/vite.d.ts +13 -3
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +82 -25
- package/dist/vite.js.map +1 -1
- package/dist/vue/index.d.ts +43 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +76 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +63 -7
- package/src/bun.ts +77 -20
- package/src/cli.ts +62 -8
- package/src/index.ts +1 -1
- package/src/next-loader.ts +17 -5
- package/src/next.ts +120 -16
- package/src/preact/index.ts +91 -0
- package/src/react/index.tsx +12 -42
- package/src/runtime/bind.ts +96 -0
- package/src/runtime/copy.ts +148 -24
- package/src/solid/index.ts +67 -0
- package/src/svelte/Text.svelte +13 -0
- package/src/svelte/Text.svelte.d.ts +20 -0
- package/src/svelte/index.ts +65 -0
- package/src/tools/bundle.ts +143 -2
- package/src/tools/catalog.ts +10 -1
- package/src/tools/chunks.ts +142 -0
- package/src/tools/config.ts +25 -7
- package/src/tools/entry.ts +19 -20
- package/src/tools/exchange.ts +275 -0
- package/src/tools/extract.ts +2 -2
- package/src/tools/generate.ts +7 -1
- package/src/tools/index.ts +2 -1
- package/src/tools/native/android.ts +186 -0
- package/src/tools/native/apple.ts +201 -0
- package/src/tools/native/date.ts +219 -0
- package/src/tools/native/index.ts +17 -5
- package/src/tools/native/kotlin.ts +47 -6
- package/src/tools/native/model.ts +67 -29
- package/src/tools/native/printf.ts +137 -0
- package/src/tools/native/rust.ts +86 -5
- package/src/tools/native/swift.ts +62 -6
- package/src/tools/scan.ts +146 -25
- package/src/tools/todo.ts +6 -1
- package/src/vite.ts +121 -28
- package/src/vue/index.ts +117 -0
- package/dist/bundle-DboItAre.js +0 -111
- package/dist/bundle-DboItAre.js.map +0 -1
- package/dist/bundle-KD-DiiFa.d.ts.map +0 -1
- package/dist/compile-xF3F4oIp.d.ts +0 -16
- package/dist/compile-xF3F4oIp.d.ts.map +0 -1
- package/dist/copy-B3V_QRkT.d.ts.map +0 -1
- package/dist/copy-Bo_FIoLy.js +0 -154
- package/dist/copy-Bo_FIoLy.js.map +0 -1
- package/dist/endpoints-CnUfCkIk.js +0 -96
- package/dist/endpoints-CnUfCkIk.js.map +0 -1
- package/dist/generate-9pR3i5Bj.js.map +0 -1
- package/dist/sarif-CWyTAQ0y.js.map +0 -1
- package/dist/scan-Bx0_yDSi.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# prosebook
|
|
2
2
|
|
|
3
|
-
Every string your app shows, in one typed catalog. For React and any JavaScript.
|
|
3
|
+
Every string your app shows, in one typed catalog. For React, Preact, Vue, Svelte, Solid and any JavaScript.
|
|
4
4
|
|
|
5
5
|
[Homepage](https://gkurt.com/prosebook/) · [Try it on the page](https://gkurt.com/prosebook/#in-context)
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ const { t } = useCopy();
|
|
|
38
38
|
string, periods, sentence case, banned filler, and `{count} files` outside a plural. Plugins add your
|
|
39
39
|
own rules, scoped by kind, surface and locale, and translations are held to their source.
|
|
40
40
|
- **Native too.** The same catalog generates Rust, Swift and Kotlin for a tray, a menu bar or a CLI,
|
|
41
|
-
with plurals and
|
|
41
|
+
with plurals, numbers and dates that say what the browser says, and the string resources Android and Apple read.
|
|
42
42
|
|
|
43
43
|
## Quick start
|
|
44
44
|
|
|
@@ -155,6 +155,121 @@ function Cart({ count }: { count: number }) {
|
|
|
155
155
|
}
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### In Preact
|
|
159
|
+
|
|
160
|
+
`prosebook/preact` is the React binding for Preact, written on `preact/hooks`, so it needs no
|
|
161
|
+
`preact/compat`: `CopyProvider`, `useCopy`, `useLocale` and `Text`, used as above.
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
import { CopyProvider, Text, useCopy } from 'prosebook/preact';
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### In Vue
|
|
168
|
+
|
|
169
|
+
Install the copy as a plugin, or give a subtree its own with `provideCopy(copy)` in `setup`. What
|
|
170
|
+
`useCopy()` returns reads through a ref, so a template or `computed` that reads a message runs again
|
|
171
|
+
when the locale changes. `rich` returns a `VNode`; in a template, `<Text>` renders a message and takes
|
|
172
|
+
each tag as a slot of the same name, which gets what the tag wraps as `chunks`:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
// main.ts
|
|
176
|
+
import { copyPlugin } from 'prosebook/vue';
|
|
177
|
+
|
|
178
|
+
createApp(App)
|
|
179
|
+
.use(copyPlugin(copy, { tags: { link: (chunks) => h('a', { href: '/terms' }, chunks) } }))
|
|
180
|
+
.mount('#app');
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```vue
|
|
184
|
+
<script setup lang="ts">
|
|
185
|
+
import { Text, useCopy } from 'prosebook/vue';
|
|
186
|
+
import { ref } from 'vue';
|
|
187
|
+
|
|
188
|
+
const copy = useCopy();
|
|
189
|
+
const count = ref(2);
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<template>
|
|
193
|
+
<h2>{{ copy.t('cart.title') }}</h2>
|
|
194
|
+
<Text id="cart.items" :values="{ count }" />
|
|
195
|
+
<Text id="cart.terms">
|
|
196
|
+
<template #link="{ chunks }"
|
|
197
|
+
><RouterLink to="/terms"><component :is="chunks" /></RouterLink
|
|
198
|
+
></template>
|
|
199
|
+
</Text>
|
|
200
|
+
<button @click="copy.setLocale('de')">Deutsch</button>
|
|
201
|
+
</template>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Templates are typed with `vue-tsc`, which runs on TypeScript 6 until TypeScript 7 ships its API.
|
|
205
|
+
|
|
206
|
+
### In Svelte
|
|
207
|
+
|
|
208
|
+
`setCopy(copy)` in a component gives it and everything below it the copy; `getCopy()` reads it. Its
|
|
209
|
+
reads are reactive, so markup, `$derived` and effects that read a message run again when the locale
|
|
210
|
+
changes. Tags render through snippets: give `<Text>` one of the same name, or `setCopy` one for every
|
|
211
|
+
message. A snippet gets what its tag wraps as `chunks`, which `<Text {chunks} />` renders:
|
|
212
|
+
|
|
213
|
+
```svelte
|
|
214
|
+
<!-- App.svelte -->
|
|
215
|
+
<script lang="ts">
|
|
216
|
+
import { setCopy, Text, type RichPart } from 'prosebook/svelte';
|
|
217
|
+
|
|
218
|
+
let { copy } = $props();
|
|
219
|
+
setCopy(copy, { tags: { link } });
|
|
220
|
+
</script>
|
|
221
|
+
|
|
222
|
+
{#snippet link(chunks: RichPart[])}<a href="/terms"><Text {chunks} /></a>{/snippet}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```svelte
|
|
226
|
+
<!-- Cart.svelte -->
|
|
227
|
+
<script lang="ts">
|
|
228
|
+
import { getCopy, Text } from 'prosebook/svelte';
|
|
229
|
+
|
|
230
|
+
const copy = getCopy();
|
|
231
|
+
let count = $state(2);
|
|
232
|
+
</script>
|
|
233
|
+
|
|
234
|
+
<h2>{copy.t('cart.title')}</h2>
|
|
235
|
+
<Text id="cart.items" values={{ count }} />
|
|
236
|
+
<Text id="cart.terms" />
|
|
237
|
+
<button onclick={() => copy.setLocale('de')}>Deutsch</button>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`Text` ships as a `.svelte` file for your Svelte compiler to build, so it needs Svelte 5 and its Vite
|
|
241
|
+
plugin (or SvelteKit). `svelte-check` types it, and runs on TypeScript 6 for now.
|
|
242
|
+
|
|
243
|
+
### In Solid
|
|
244
|
+
|
|
245
|
+
Each read tracks a signal the copy sets, so what reads a message updates in place when the locale
|
|
246
|
+
changes, and components never run again. `rich` returns your tag renderers' elements beside the text.
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { CopyProvider, Text, useCopy } from 'prosebook/solid';
|
|
250
|
+
|
|
251
|
+
render(
|
|
252
|
+
() => (
|
|
253
|
+
<CopyProvider copy={copy} tags={{ link: (chunks) => <a href="/terms">{chunks}</a> }}>
|
|
254
|
+
<Cart />
|
|
255
|
+
</CopyProvider>
|
|
256
|
+
),
|
|
257
|
+
root,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
function Cart() {
|
|
261
|
+
const copy = useCopy();
|
|
262
|
+
const [count, setCount] = createSignal(2);
|
|
263
|
+
return (
|
|
264
|
+
<>
|
|
265
|
+
<h2>{copy.t('cart.title')}</h2>
|
|
266
|
+
<Text id="cart.items" values={{ count: count() }} />
|
|
267
|
+
<p>{copy.rich('cart.terms')}</p>
|
|
268
|
+
</>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
158
273
|
### In plain JavaScript
|
|
159
274
|
|
|
160
275
|
```js
|
|
@@ -238,7 +353,7 @@ function Ready({ copy, children }: { copy: Copy; children: ReactNode }) {
|
|
|
238
353
|
}
|
|
239
354
|
```
|
|
240
355
|
|
|
241
|
-
### Outside the browser: Rust, Swift and
|
|
356
|
+
### Outside the browser: Rust, Swift, Kotlin, Android and Apple
|
|
242
357
|
|
|
243
358
|
The parts of an app the browser doesn't draw, a tray, a menu bar, a CLI, an Android shell, can read
|
|
244
359
|
the same catalog. `generate` writes one file per target in `native`, holding every locale:
|
|
@@ -283,17 +398,126 @@ A message with no placeholders is a `&'static str` in Rust and a property in Swi
|
|
|
283
398
|
with placeholders is a function taking them in order, a string or a number of any integer or float type. Each member's doc comment
|
|
284
399
|
is the source text, its context and what each argument holds.
|
|
285
400
|
|
|
401
|
+
A date is a `Date` in Swift and a `java.time.Instant` in Kotlin, shown in the device's time zone. Rust's
|
|
402
|
+
standard library has no time zones, so its file brings a `DateTime` holding the clock's fields, made
|
|
403
|
+
from a moment and the offset it is shown at:
|
|
404
|
+
|
|
405
|
+
```rust
|
|
406
|
+
let printed = copy::DateTime::from_unix(seconds, offset); // offset in seconds east of UTC
|
|
407
|
+
copy.printed(printed); // "Printed Jan 5, 2026 at 2:05 PM."
|
|
408
|
+
```
|
|
409
|
+
|
|
286
410
|
The files need nothing beside them: each brings the plural rules of its locales, compiled from CLDR's
|
|
287
|
-
(the data `Intl.PluralRules` is built on), and its number formats, taken apart from `Intl`
|
|
288
|
-
generated: the digits, the separators, how integers group, the text around a currency or a
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
411
|
+
(the data `Intl.PluralRules` is built on), and its number and date formats, taken apart from `Intl`
|
|
412
|
+
when it is generated: the digits, the separators, how integers group, the text around a currency or a
|
|
413
|
+
percent; the order of a date's fields, the month and weekday names, AM and PM or the locale's periods
|
|
414
|
+
of the day, and its clock. Each format is checked against `Intl` before any code is written, so native
|
|
415
|
+
code writes what the browser does, Arabic digits and Indian grouping included. A key with no
|
|
416
|
+
translation falls back through its parents to the source as the runtime's do, resolved when the file
|
|
417
|
+
is written.
|
|
418
|
+
|
|
419
|
+
What can't be written that way fails `generate`, with the fix: a tag, compact notation, significant
|
|
420
|
+
digits, a unit or currency name that changes with the count (`unitDisplay: 'long'`), a date with its
|
|
421
|
+
time zone's name (`{when, time, full}`) or a time zone of its own, a locale that counts in another
|
|
422
|
+
calendar than the Gregorian (Thai's Buddhist, Persian), and an MF2 option read from an argument. Leave
|
|
423
|
+
such keys out of the target's `keys`.
|
|
424
|
+
|
|
425
|
+
#### Android resources
|
|
426
|
+
|
|
427
|
+
An Android app needs string resources even with the Kotlin target: its manifest, launcher shortcuts,
|
|
428
|
+
an accessibility service's description and every other XML resource name strings as `@string/…`, and
|
|
429
|
+
only a resource answers that. The `android` target writes them, with `out` naming the `res` folder:
|
|
292
430
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
431
|
+
```ts
|
|
432
|
+
native: [{ language: 'android', out: 'android/app/src/main/res', keys: ['android'] }],
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
`values/strings.xml` holds the source locale, and each other locale gets a folder named as Android
|
|
436
|
+
names it (`values-de`, `values-pt-rBR`, `values-b+sr+Latn` for a script), holding what that locale
|
|
437
|
+
shows, every fallback resolved. `name` renames the file, `strings` by default, so it can sit beside
|
|
438
|
+
strings you write yourself. A resource is named for its key, less the prefix, with `.` and `-` as
|
|
439
|
+
`_`: `android.sign-in.denied` is `R.string.sign_in_denied`. Above each is a comment with its context
|
|
440
|
+
and what each argument holds.
|
|
441
|
+
|
|
442
|
+
```xml
|
|
443
|
+
<!-- Counts the cart. count: how many items the cart holds. -->
|
|
444
|
+
<plurals name="cart_items">
|
|
445
|
+
<item quantity="one">%1$d item in the cart.</item>
|
|
446
|
+
<item quantity="other">%1$d items in the cart.</item>
|
|
447
|
+
</plurals>
|
|
448
|
+
<!-- Greets the person signed in. firstName: the person's first name. -->
|
|
449
|
+
<string name="hello">Hello, %1$s!</string>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Android fills placeholders in with `String.format` and picks a plural's arm itself, so a resource
|
|
453
|
+
holds less than the other targets: text, plain `{name}` placeholders, written `%1$s` by their place in
|
|
454
|
+
the source so a translation can reorder them, and one plural, which becomes `<plurals>` with its count
|
|
455
|
+
as `%1$d` (the text around it goes into each arm). Call it as Android does,
|
|
456
|
+
`resources.getQuantityString(R.plurals.cart_items, count, count)`. `String.format` isn't `Intl`, so
|
|
457
|
+
nothing here is checked against it: `%d` writes 1234 without grouping. Android picks the arm by the
|
|
458
|
+
device's language, so a plural a locale doesn't translate picks among the source's arms by that
|
|
459
|
+
language's rules. Escaping is done for you: quotes, apostrophes, `\`, `&`, `<`, a newline, a leading
|
|
460
|
+
`@` or `?`, `%` where the string is formatted, and spaces Android would collapse or trim.
|
|
461
|
+
|
|
462
|
+
What a resource can't hold fails `generate`, with the fix: a select, a date, a formatted number, a
|
|
463
|
+
second plural, an ordinal, an offset, an exact arm such as `=0` (Android's `zero` is a category, which
|
|
464
|
+
English never picks), a selector other than a bare `:number` or `:integer`, and a tag.
|
|
465
|
+
|
|
466
|
+
#### Apple string tables
|
|
467
|
+
|
|
468
|
+
An Apple app has the same gap: macOS and iOS read the permission prompts, the app's name and other
|
|
469
|
+
Info.plist strings from the bundle, never from Swift. The `apple` target writes `.strings` tables, a
|
|
470
|
+
`<locale>.lproj/<name>.strings` per locale under `out`, each holding what that locale shows. They are
|
|
471
|
+
plain `.strings` files, not a String Catalog, which only Xcode compiles: an app bundled by Tauri or a
|
|
472
|
+
script copies the `.lproj` folders into `Contents/Resources` as they are.
|
|
473
|
+
|
|
474
|
+
With `plist`, the table is `InfoPlist` and translates that Info.plist. A catalog key can't be written
|
|
475
|
+
`NSCameraUsageDescription`, so its words spell the plist key: `macos.ns-camera-usage-description` is
|
|
476
|
+
`NSCameraUsageDescription`. The key must be in the plist, since the system reads only the keys there,
|
|
477
|
+
and the plist's own text must be the source's, since it shows where no `.lproj` matches. Either way
|
|
478
|
+
round, `generate` fails with the fix:
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
native: [{ language: 'apple', out: 'src-tauri/Resources', keys: ['macos'], plist: 'src-tauri/Info.plist' }],
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
```c
|
|
485
|
+
/* Asks for the camera. */
|
|
486
|
+
"NSCameraUsageDescription" = "Corner Shop scans a product's barcode with the camera.";
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Without `plist`, the table is `Localizable`, for what the system looks up by key, such as a push
|
|
490
|
+
notification's `loc-key`. An entry is named for its key less the prefix, as written (`tray.quit` is
|
|
491
|
+
`"quit"` under `['tray']`), and placeholders are written `%1$@` by their place in the source, filled
|
|
492
|
+
in by `String(format:)`, not `Intl`. A table holds text and plain `{name}` placeholders.
|
|
493
|
+
|
|
494
|
+
A message with a plural goes into a `.stringsdict` of the same name beside the table, where the
|
|
495
|
+
system looks first. Each plural is a variable in its format, so the text around it stays where it is
|
|
496
|
+
and a message can hold two; its count is `%N$lld`, for an `Int`, and the system picks the arm by its
|
|
497
|
+
own CLDR rules:
|
|
498
|
+
|
|
499
|
+
```xml
|
|
500
|
+
<key>items</key>
|
|
501
|
+
<dict>
|
|
502
|
+
<key>NSStringLocalizedFormatKey</key>
|
|
503
|
+
<string>%1$@ has %2$#@count@ in the cart.</string>
|
|
504
|
+
<key>count</key>
|
|
505
|
+
<dict>
|
|
506
|
+
<key>NSStringFormatSpecTypeKey</key>
|
|
507
|
+
<string>NSStringPluralRuleType</string>
|
|
508
|
+
<key>NSStringFormatValueTypeKey</key>
|
|
509
|
+
<string>lld</string>
|
|
510
|
+
<key>one</key>
|
|
511
|
+
<string>%2$lld item</string>
|
|
512
|
+
<key>other</key>
|
|
513
|
+
<string>%2$lld items</string>
|
|
514
|
+
</dict>
|
|
515
|
+
</dict>
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
A plural there is cardinal, on a bare count, with no offset and no exact arm such as `=0`, as on
|
|
519
|
+
Android; a select, a date, a formatted number, a tag and a plural inside another fail `generate`, and
|
|
520
|
+
an Info.plist string takes no placeholders at all. Show those from the Swift target.
|
|
297
521
|
|
|
298
522
|
## The catalog
|
|
299
523
|
|
|
@@ -321,6 +545,10 @@ catalog, so an editor with the YAML language server completes and explains every
|
|
|
321
545
|
# yaml-language-server: $schema=./catalog.schema.json
|
|
322
546
|
```
|
|
323
547
|
|
|
548
|
+
That line is how the editor finds the schema, so `generate` starts each catalog file with it, and
|
|
549
|
+
`generate --check` (and `check`) fails on a file without it. A file that names a schema of its own on
|
|
550
|
+
its first line keeps it.
|
|
551
|
+
|
|
324
552
|
### Messages
|
|
325
553
|
|
|
326
554
|
ICU MessageFormat, checked at build time:
|
|
@@ -487,6 +715,26 @@ translations are then linted, as a person's would be. An agent working in the re
|
|
|
487
715
|
of this: it can read `prosebook todo de`, write the translation files itself, and run
|
|
488
716
|
`prosebook check`.
|
|
489
717
|
|
|
718
|
+
**Translation tools.** `prosebook export` writes the same entries for a translation tool, as XLIFF
|
|
719
|
+
2.0 or JSON by the file's extension (`--format xliff|json` on stdout), and `import` takes the tool's
|
|
720
|
+
file back as it takes a reply:
|
|
721
|
+
|
|
722
|
+
```bash
|
|
723
|
+
prosebook export de de.xlf # what todo lists; --all for every entry
|
|
724
|
+
prosebook export en en.json # the source, every entry, for a tool that translates JSON
|
|
725
|
+
prosebook import de de.xlf # or de.json, flat or nested by key
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
In XLIFF, each entry is a unit named for its key, in a file per area, with its context, kind, surface,
|
|
729
|
+
what each placeholder holds and, for a stale one, the word diff as notes. A message travels as its
|
|
730
|
+
ICU or MF2 text, which tools that take these formats read, not as XLIFF's inline codes: a target that
|
|
731
|
+
comes back with a `<ph>` is left out with the fix. A missing translation goes out `initial`, a stale
|
|
732
|
+
one `initial` with its translation, and a current one `translated`. A stale translation given back
|
|
733
|
+
unchanged is accepted only once the tool has moved it on from `initial`, since only then has someone
|
|
734
|
+
looked at it. Each unit carries its source as exported, so a translation of a source that has changed
|
|
735
|
+
since is left out until it is exported again. JSON is `key: text` in the locale, empty where it has
|
|
736
|
+
none, and carries no context: give the tool the XLIFF where it reads both.
|
|
737
|
+
|
|
490
738
|
**Pseudo-locale.** `pseudo: 'en-XA'` generates `⟦Ýóóúúŕ çááŕţ⟧` from the source. Switch to it and
|
|
491
739
|
any plain text on screen is hardcoded, a missing bracket is clipped text, and brackets inside
|
|
492
740
|
brackets are a sentence spliced from fragments.
|
|
@@ -734,10 +982,92 @@ A message the build leaves out shows as its key, so by default a build fails on
|
|
|
734
982
|
it can't see: `t(item.key)`, ``rich(`${area}.title`)``, `<Text id={id} />`. Write the key at the read,
|
|
735
983
|
or, where every key it reads is written elsewhere (in an array, say) or listed in `keep`, mark the
|
|
736
984
|
line with `prosebook-ignore`. Reads are known by name: `t` and `rich` alone or as methods, the names
|
|
737
|
-
they're destructured to (`const { rich: tRich } = copy`) in any module, and `<Text>` from
|
|
738
|
-
|
|
985
|
+
they're destructured to (`const { rich: tRich } = copy`) in any module, and `<Text>` from any of the
|
|
986
|
+
bindings. In `.vue` and `.svelte` files the scripts and the expressions in the markup are read alike
|
|
987
|
+
(`{{ t(key) }}`, `:id="key"`, `{copy.t(key)}`, `<Text {id} />`), and `<!-- prosebook-ignore -->` marks
|
|
988
|
+
a line there. `prune: 'loose'` prunes without the check, and `prune: false` ships everything.
|
|
739
989
|
`prune` and `keep` are options of each integration.
|
|
740
990
|
|
|
991
|
+
## Splitting
|
|
992
|
+
|
|
993
|
+
Pruning drops what no code reads; splitting ships the rest with the code that reads it. With
|
|
994
|
+
`prosebook({ split: true })`, the Vite plugin splits each locale by area, the file a key's first
|
|
995
|
+
segment names, so a route loaded with `lazy(() => import('./Cart.tsx'))` brings the `cart` messages
|
|
996
|
+
with it and the main bundle holds none of them:
|
|
997
|
+
|
|
998
|
+
```ts
|
|
999
|
+
// vite.config.ts
|
|
1000
|
+
export default defineConfig({
|
|
1001
|
+
plugins: [react(), prosebook({ split: true })],
|
|
1002
|
+
});
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
A module that names an area's keys imports that area, and the bundler puts it beside that module:
|
|
1006
|
+
in the main chunk when the shell reads it, in a route's chunk when only the route does, in a shared
|
|
1007
|
+
chunk when two routes do. An area holds the source's messages and the `bundled` locales'; every
|
|
1008
|
+
other locale loads area by area, as it is needed: switching language fetches only the areas
|
|
1009
|
+
already on the page. The area's module waits for that load (a top-level `await`), so a lazy route
|
|
1010
|
+
renders in the reader's language from its first frame, in every binding, with no change to the code
|
|
1011
|
+
reading it.
|
|
1012
|
+
|
|
1013
|
+
Areas the bundler puts in one chunk share a file in each such locale, so a route reading three areas
|
|
1014
|
+
across its modules loads German in one request, not three. Which areas share a chunk is read from
|
|
1015
|
+
the imports under `scan.dirs` as the build starts, as the bundler will chunk them. An import that
|
|
1016
|
+
can't be followed, such as one through an alias, can leave two areas of a chunk a file each: one
|
|
1017
|
+
more request, never a missing message.
|
|
1018
|
+
|
|
1019
|
+
The Bun plugin takes `split` too. `Bun.build` needs `splitting: true` for its lazy chunks, and in a
|
|
1020
|
+
full-stack build, one `Bun.build` of a server and the pages it imports, only the pages are split.
|
|
1021
|
+
bunfig.toml names a module whose default export is the plugin, so the dev server's comes from a
|
|
1022
|
+
file of the app's own. Under Bun, the files read for areas are those under `scan.dirs`, as for
|
|
1023
|
+
pruning:
|
|
1024
|
+
|
|
1025
|
+
```ts
|
|
1026
|
+
// prosebook.plugin.ts, which bunfig.toml names: plugins = ["./prosebook.plugin.ts"]
|
|
1027
|
+
import { prosebook } from 'prosebook/bun';
|
|
1028
|
+
|
|
1029
|
+
export default prosebook({ split: true });
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
```ts
|
|
1033
|
+
// build.ts
|
|
1034
|
+
await Bun.build({
|
|
1035
|
+
entrypoints: ['./src/server.ts'],
|
|
1036
|
+
target: 'bun',
|
|
1037
|
+
outdir: './dist',
|
|
1038
|
+
splitting: true,
|
|
1039
|
+
plugins: [prosebook({ split: true })],
|
|
1040
|
+
});
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
Under Next, `withProsebook(config, { split: true })` splits what client components read. Next
|
|
1044
|
+
already splits client code by route, so each route's brings only its areas, and a server component
|
|
1045
|
+
reads whole locales, as the server render does. Turbopack has no virtual modules, so the areas are
|
|
1046
|
+
files `next dev` and `next build` write under `node_modules/.prosebook`, and a loader over the app's
|
|
1047
|
+
source under `scan.dirs` adds their imports in the browser.
|
|
1048
|
+
|
|
1049
|
+
A read the build can't see, marked `prosebook-ignore`, finds its area only if some module loaded
|
|
1050
|
+
before it names one of the area's keys; one read before then shows its key and is reported through
|
|
1051
|
+
`onError`, with the fix. Areas `keep` names load with the catalog. Splitting runs in the browser
|
|
1052
|
+
only: a server render, and an app without a plugin, load whole locales. It needs a build target
|
|
1053
|
+
with top-level `await`, which Vite's, Bun's and Next's defaults have.
|
|
1054
|
+
|
|
1055
|
+
A page rendered on a server in a lazy locale hydrates once its areas' files have loaded, and the
|
|
1056
|
+
browser asks for them only after the code reading them has. Under Vite, the server can link them
|
|
1057
|
+
first. A split client build writes down each area's file per lazy locale, the server build after
|
|
1058
|
+
it lists them in its catalog, and a copy hands each file it reads from to `preload`, once:
|
|
1059
|
+
|
|
1060
|
+
```tsx
|
|
1061
|
+
import { preloadModule } from 'react-dom';
|
|
1062
|
+
|
|
1063
|
+
const copy = createCopy(catalog, { locale, preload: (href) => preloadModule(href, { as: 'script' }) });
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
React puts a `<link rel="modulepreload">` in the page for each, so the browser fetches German
|
|
1067
|
+
beside the route's code. Without React, collect them and write the links yourself. Build the client
|
|
1068
|
+
before the server, as TanStack Start and other frameworks on Vite do; `vite build` warns where it
|
|
1069
|
+
built the server with no client to take the files from.
|
|
1070
|
+
|
|
741
1071
|
## Finding a string's entry
|
|
742
1072
|
|
|
743
1073
|
In dev, hold Alt (Option on a Mac) and point at text on the page: the inspector outlines the
|
|
@@ -815,7 +1145,7 @@ export default defineConfig({
|
|
|
815
1145
|
properNouns: [],
|
|
816
1146
|
pathSeparator: '›',
|
|
817
1147
|
},
|
|
818
|
-
native: [], // Rust, Swift or Kotlin files: { language: 'rust', out: 'src/copy.rs', keys: ['tray'] }
|
|
1148
|
+
native: [], // Rust, Swift or Kotlin files, Android's res or Apple's .lproj: { language: 'rust', out: 'src/copy.rs', keys: ['tray'] }
|
|
819
1149
|
scan: { dirs: ['src'] },
|
|
820
1150
|
});
|
|
821
1151
|
```
|
|
@@ -826,13 +1156,17 @@ Each is a small shop in English, German and the pseudo-locale, and uses proseboo
|
|
|
826
1156
|
from the package. CI generates, lints, typechecks and builds each one, then checks what its build
|
|
827
1157
|
serves.
|
|
828
1158
|
|
|
829
|
-
| Example |
|
|
830
|
-
| ---------------------------------------------------- |
|
|
831
|
-
| [`examples/vite`](examples/vite) | React with Vite,
|
|
832
|
-
| [`examples/
|
|
833
|
-
| [`examples/
|
|
834
|
-
| [`examples/
|
|
835
|
-
| [`examples/
|
|
1159
|
+
| Example | |
|
|
1160
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1161
|
+
| [`examples/vite`](examples/vite) | React with Vite, a part-translated Turkish and the inspector. The cart is a lazy route, split by area. |
|
|
1162
|
+
| [`examples/preact`](examples/preact) | Preact with Vite's own JSX transform, the same app as the React one. |
|
|
1163
|
+
| [`examples/vue`](examples/vue) | Vue single-file components: `<Text>` in templates, typed by `vue-tsc`. |
|
|
1164
|
+
| [`examples/svelte`](examples/svelte) | Svelte 5 with runes: the copy set in the root, the link a snippet, typed by `svelte-check`. |
|
|
1165
|
+
| [`examples/solid`](examples/solid) | Solid: reads update in place on a locale switch, and the components run once. |
|
|
1166
|
+
| [`examples/next`](examples/next) | Next.js: server components read a copy per request, client components the provider's. The cart is a route of its own, split by area. |
|
|
1167
|
+
| [`examples/tanstack-start`](examples/tanstack-start) | TanStack Start, rendered on the server in the request's locale, split, with the page's German files linked ahead. |
|
|
1168
|
+
| [`examples/bun`](examples/bun) | `Bun.serve` with a React page, a route that answers in the request's locale, the inspector. The cart is a lazy route, split by area. |
|
|
1169
|
+
| [`examples/node`](examples/node) | No framework and no bundler: a Node script prints a receipt from the generated TypeScript. |
|
|
836
1170
|
|
|
837
1171
|
```bash
|
|
838
1172
|
bun run build # the examples import prosebook from dist
|
|
@@ -843,7 +1177,4 @@ bun run --cwd examples/next dev
|
|
|
843
1177
|
|
|
844
1178
|
Ideas not built yet, roughly in order of value:
|
|
845
1179
|
|
|
846
|
-
1. **
|
|
847
|
-
2. **Per-area splitting**: one chunk per area and locale, so a lazy route loads only its own messages.
|
|
848
|
-
3. **Dates in native targets**: date and time formats taken apart from `Intl` as number formats are,
|
|
849
|
-
so a Rust, Swift or Kotlin message can say when.
|
|
1180
|
+
1. **Preloading a split render's areas under Bun and Next**: Bun's chunks and Turbopack's name no module, so neither says which file holds an area.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/runtime/bind.ts
|
|
2
|
+
/** A reader of `copy` whose reads go through `track`, and whose rich reads get `tags` unless they give their own. */
|
|
3
|
+
function bindCopy(copy, tags, { track = () => {}, tag, whole }) {
|
|
4
|
+
const merged = {
|
|
5
|
+
...copy.tags,
|
|
6
|
+
...tags
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
get locale() {
|
|
10
|
+
track();
|
|
11
|
+
return copy.locale;
|
|
12
|
+
},
|
|
13
|
+
get version() {
|
|
14
|
+
track();
|
|
15
|
+
return copy.version;
|
|
16
|
+
},
|
|
17
|
+
locales: copy.locales,
|
|
18
|
+
source: copy.source,
|
|
19
|
+
tags: copy.tags,
|
|
20
|
+
ready: copy.ready,
|
|
21
|
+
t(key, ...args) {
|
|
22
|
+
track();
|
|
23
|
+
return copy.t(key, ...args);
|
|
24
|
+
},
|
|
25
|
+
has(key) {
|
|
26
|
+
track();
|
|
27
|
+
return copy.has(key);
|
|
28
|
+
},
|
|
29
|
+
setLocale: copy.setLocale,
|
|
30
|
+
subscribe: copy.subscribe,
|
|
31
|
+
number(value, options) {
|
|
32
|
+
track();
|
|
33
|
+
return copy.number(value, options);
|
|
34
|
+
},
|
|
35
|
+
date(value, options) {
|
|
36
|
+
track();
|
|
37
|
+
return copy.date(value, options);
|
|
38
|
+
},
|
|
39
|
+
list(items, options) {
|
|
40
|
+
track();
|
|
41
|
+
return copy.list(items, options);
|
|
42
|
+
},
|
|
43
|
+
relative(value, unit, options) {
|
|
44
|
+
track();
|
|
45
|
+
return copy.relative(value, unit, options);
|
|
46
|
+
},
|
|
47
|
+
rich(key, ...values) {
|
|
48
|
+
track();
|
|
49
|
+
const all = {
|
|
50
|
+
...merged,
|
|
51
|
+
...values[0]
|
|
52
|
+
};
|
|
53
|
+
if (tag !== void 0) {
|
|
54
|
+
for (const [name, value] of Object.entries(all)) if (typeof value === "function") all[name] = (chunks) => tag(value, chunks);
|
|
55
|
+
}
|
|
56
|
+
return whole(copy.rich(key, all));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export { bindCopy as t };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=bind-BIZNv8nU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind-BIZNv8nU.js","names":[],"sources":["../src/runtime/bind.ts"],"sourcesContent":["/**\n * What every framework binding shares: a copy read through the framework's reactivity, with a\n * provider's tag renderers under each rich read's own.\n */\nimport type { Copy } from './copy.ts';\nimport type { MessageShape, RegisteredLocale, RegisteredMessages, RichKey, RichParam, TagFn } from './types.ts';\n\ntype M = RegisteredMessages;\n\n/**\n * `Copy`, with `rich` returning `R`, a node of the framework's, whose tag renderers make `N`s. The locale is\n * a parameter so it survives a lint that, where no catalog is registered, sees `RegisteredLocale` as the default.\n */\nexport interface BoundCopy<N, R = N, L extends string = RegisteredLocale> extends Omit<Copy<M, L>, 'rich'> {\n rich<K extends RichKey<M>>(this: void, key: K, ...values: RichParam<M[K], N>): R;\n}\n\nexport type Tags = { readonly [tag: string]: (chunks: never) => unknown };\n\nexport interface Binding<R> {\n /** Called on every read, so the framework knows the reader depends on the copy's version. */\n track?: () => void;\n /** What a tag's renderer is turned into, given what it wraps. Calls it by default. */\n tag?: (render: (chunks: never) => unknown, chunks: unknown[]) => unknown;\n /** What a rich read returns, given its parts. */\n whole: (parts: unknown[]) => R;\n}\n\n/** A reader of `copy` whose reads go through `track`, and whose rich reads get `tags` unless they give their own. */\nexport function bindCopy<N, R>(copy: Copy, tags: Tags, { track = () => {}, tag, whole }: Binding<R>): BoundCopy<N, R> {\n const merged = { ...copy.tags, ...tags };\n return {\n get locale() {\n track();\n return copy.locale;\n },\n get version() {\n track();\n return copy.version;\n },\n locales: copy.locales,\n source: copy.source,\n tags: copy.tags,\n ready: copy.ready,\n t(key, ...args) {\n track();\n return copy.t(key, ...args);\n },\n has(key): key is keyof M & string {\n track();\n return copy.has(key);\n },\n setLocale: copy.setLocale,\n subscribe: copy.subscribe,\n number(value, options) {\n track();\n return copy.number(value, options);\n },\n date(value, options) {\n track();\n return copy.date(value, options);\n },\n list(items, options) {\n track();\n return copy.list(items, options);\n },\n relative(value, unit, options) {\n track();\n return copy.relative(value, unit, options);\n },\n rich(key, ...values) {\n track();\n const all: Record<string, unknown> = { ...merged, ...(values[0] as object | undefined) };\n if (tag !== undefined) {\n for (const [name, value] of Object.entries(all))\n if (typeof value === 'function') all[name] = (chunks: unknown[]) => tag(value as (chunks: never) => unknown, chunks);\n }\n return whole(copy.rich(key, all as never));\n },\n };\n}\n\ntype NoValues<S extends MessageShape> = {} extends S['args'] & { [T in S['tags']]: unknown } ? true : false;\n\n/**\n * A `<Text>`'s props where the call site renders every tag the provider doesn't: `values` is needed\n * once the message takes arguments or wraps text in a tag.\n */\nexport type TextProps<K extends Extract<keyof M, string>, N> = { id: K } & (NoValues<M[K]> extends true\n ? { values?: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N> } }\n : { values: RichParam<M[K], N>[0] });\n\n/** A `<Text>`'s props where tags may come from elsewhere (slots, snippets): `values` holds arguments, and tags if you like. */\nexport type LooseTextProps<K extends Extract<keyof M, string>, N> = { id: K } & ({} extends M[K]['args']\n ? { values?: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N> } }\n : { values: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N> } });\n"],"mappings":";;AA6BA,SAAgB,SAAe,MAAY,MAAY,EAAE,cAAc,CAAC,GAAG,KAAK,SAAsC;CACpH,MAAM,SAAS;EAAE,GAAG,KAAK;EAAM,GAAG;CAAK;CACvC,OAAO;EACL,IAAI,SAAS;GACX,MAAM;GACN,OAAO,KAAK;EACd;EACA,IAAI,UAAU;GACZ,MAAM;GACN,OAAO,KAAK;EACd;EACA,SAAS,KAAK;EACd,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,EAAE,KAAK,GAAG,MAAM;GACd,MAAM;GACN,OAAO,KAAK,EAAE,KAAK,GAAG,IAAI;EAC5B;EACA,IAAI,KAA8B;GAChC,MAAM;GACN,OAAO,KAAK,IAAI,GAAG;EACrB;EACA,WAAW,KAAK;EAChB,WAAW,KAAK;EAChB,OAAO,OAAO,SAAS;GACrB,MAAM;GACN,OAAO,KAAK,OAAO,OAAO,OAAO;EACnC;EACA,KAAK,OAAO,SAAS;GACnB,MAAM;GACN,OAAO,KAAK,KAAK,OAAO,OAAO;EACjC;EACA,KAAK,OAAO,SAAS;GACnB,MAAM;GACN,OAAO,KAAK,KAAK,OAAO,OAAO;EACjC;EACA,SAAS,OAAO,MAAM,SAAS;GAC7B,MAAM;GACN,OAAO,KAAK,SAAS,OAAO,MAAM,OAAO;EAC3C;EACA,KAAK,KAAK,GAAG,QAAQ;GACnB,MAAM;GACN,MAAM,MAA+B;IAAE,GAAG;IAAQ,GAAI,OAAO;GAA0B;GACvF,IAAI,QAAQ,KAAA,GACL;SAAA,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,GAAG,GAC5C,IAAI,OAAO,UAAU,YAAY,IAAI,SAAS,WAAsB,IAAI,OAAqC,MAAM;GAAA;GAEvH,OAAO,MAAM,KAAK,KAAK,KAAK,GAAY,CAAC;EAC3C;CACF;AACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { C as RichParam, S as RichKey, T as TagFn, b as RegisteredLocale, g as MessageShape, n as Copy, x as RegisteredMessages } from "./copy-BXn0aYQn.js";
|
|
2
|
+
type M = RegisteredMessages;
|
|
3
|
+
/**
|
|
4
|
+
* `Copy`, with `rich` returning `R`, a node of the framework's, whose tag renderers make `N`s. The locale is
|
|
5
|
+
* a parameter so it survives a lint that, where no catalog is registered, sees `RegisteredLocale` as the default.
|
|
6
|
+
*/
|
|
7
|
+
interface BoundCopy<N, R = N, L extends string = RegisteredLocale> extends Omit<Copy<M, L>, 'rich'> {
|
|
8
|
+
rich<K extends RichKey<M>>(this: void, key: K, ...values: RichParam<M[K], N>): R;
|
|
9
|
+
}
|
|
10
|
+
type NoValues<S extends MessageShape> = {} extends S['args'] & { [T in S['tags']]: unknown; } ? true : false;
|
|
11
|
+
/**
|
|
12
|
+
* A `<Text>`'s props where the call site renders every tag the provider doesn't: `values` is needed
|
|
13
|
+
* once the message takes arguments or wraps text in a tag.
|
|
14
|
+
*/
|
|
15
|
+
type TextProps<K extends Extract<keyof M, string>, N> = {
|
|
16
|
+
id: K;
|
|
17
|
+
} & (NoValues<M[K]> extends true ? {
|
|
18
|
+
values?: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N>; };
|
|
19
|
+
} : {
|
|
20
|
+
values: RichParam<M[K], N>[0];
|
|
21
|
+
});
|
|
22
|
+
/** A `<Text>`'s props where tags may come from elsewhere (slots, snippets): `values` holds arguments, and tags if you like. */
|
|
23
|
+
type LooseTextProps<K extends Extract<keyof M, string>, N> = {
|
|
24
|
+
id: K;
|
|
25
|
+
} & ({} extends M[K]['args'] ? {
|
|
26
|
+
values?: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N>; };
|
|
27
|
+
} : {
|
|
28
|
+
values: M[K]['args'] & { [T in M[K]['tags']]?: TagFn<N>; };
|
|
29
|
+
});
|
|
30
|
+
export { LooseTextProps as n, TextProps as r, BoundCopy as t };
|
|
31
|
+
//# sourceMappingURL=bind-ZS86jHXk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind-ZS86jHXk.d.ts","names":[],"sources":["../src/runtime/bind.ts"],"mappings":";KAOK,IAAI;;;;;UAMQ,UAAU,GAAG,IAAI,GAAG,mBAAmB,0BAA0B,KAAK,KAAK,GAAG;EAC7F,KAAK,UAAU,QAAQ,IAAI,YAAY,KAAK,MAAM,QAAQ,UAAU,EAAE,IAAI,KAAK;;KAoE5E,SAAS,UAAU,2BAA2B,eAAe,KAAK;;;;;KAM3D,UAAU,UAAU,cAAc,YAAY;EAAO,IAAI;KAAO,SAAS,EAAE;EACjF,SAAS,EAAE,gBAAgB,KAAK,EAAE,cAAc,MAAM;;EACtD,QAAQ,UAAU,EAAE,IAAI;;;KAGlB,eAAe,UAAU,cAAc,YAAY;EAAO,IAAI;gBAAkB,EAAE;EACxF,SAAS,EAAE,gBAAgB,KAAK,EAAE,cAAc,MAAM;;EACtD,QAAQ,EAAE,gBAAgB,KAAK,EAAE,cAAc,MAAM"}
|
package/dist/bun.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as Prune } from "./bundle-
|
|
2
|
-
import { i as ProsebookConfig } from "./config-
|
|
1
|
+
import { t as Prune } from "./bundle-CLHY_97D.js";
|
|
2
|
+
import { i as ProsebookConfig } from "./config-CzaJJQ60.js";
|
|
3
3
|
import { BunPlugin } from "bun";
|
|
4
4
|
export interface ProsebookBunOptions {
|
|
5
5
|
/** The config, inline. Without it, `prosebook.config.*` at `root` is read. */
|
|
@@ -12,8 +12,17 @@ export interface ProsebookBunOptions {
|
|
|
12
12
|
* check; `false` ships every message.
|
|
13
13
|
*/
|
|
14
14
|
prune?: Prune;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. With
|
|
17
|
+
* `split`, their areas load with the catalog.
|
|
18
|
+
*/
|
|
16
19
|
keep?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Split each locale by area in the page, as the Vite plugin's `split` does, so a lazy route loads
|
|
22
|
+
* only the messages it reads. `Bun.build` needs `splitting: true`. A server's own code loads whole
|
|
23
|
+
* locales.
|
|
24
|
+
*/
|
|
25
|
+
split?: boolean;
|
|
17
26
|
/** Under the dev server, Alt-click a string on the page to open its entry in the editor. On by default. */
|
|
18
27
|
inspect?: boolean;
|
|
19
28
|
}
|
package/dist/bun.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bun.d.ts","names":[],"sources":["../src/bun.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"bun.d.ts","names":[],"sources":["../src/bun.ts"],"mappings":";;;iBAqDiB;;EAEf,SAAS;;EAET;;;;;;EAMA,QAAQ;;;;;EAKR;;;;;;EAMA;;EAEA;;wBAWc,UAAU,UAAS,sBAA2B;iBA0E7C;;EAEf,SAAS;;EAET;;;;;;wBAOc,UAAU,UAAS,yBAA8B,gBAAgB,KAAK,YAAY,QAAQ"}
|