prosebook 0.3.0 → 0.4.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.
Files changed (35) hide show
  1. package/README.md +74 -3
  2. package/dist/bun.d.ts +2 -2
  3. package/dist/bun.js +3 -3
  4. package/dist/{bundle-KD-DiiFa.d.ts → bundle-CpUIWb9j.d.ts} +3 -3
  5. package/dist/bundle-CpUIWb9j.d.ts.map +1 -0
  6. package/dist/cli.js +3 -3
  7. package/dist/{compile-xF3F4oIp.d.ts → compile-ClK9htKr.d.ts} +2 -2
  8. package/dist/{compile-xF3F4oIp.d.ts.map → compile-ClK9htKr.d.ts.map} +1 -1
  9. package/dist/{config-DdMkKt_N.d.ts → config-CzaJJQ60.d.ts} +19 -5
  10. package/dist/{config-DdMkKt_N.d.ts.map → config-CzaJJQ60.d.ts.map} +1 -1
  11. package/dist/{config-BPSmoYel.js → config-nWZf9QmF.js} +9 -3
  12. package/dist/{config-BPSmoYel.js.map → config-nWZf9QmF.js.map} +1 -1
  13. package/dist/config.d.ts +1 -1
  14. package/dist/config.js +1 -1
  15. package/dist/{endpoints-CnUfCkIk.js → endpoints-BgG_5Vpm.js} +2 -2
  16. package/dist/{endpoints-CnUfCkIk.js.map → endpoints-BgG_5Vpm.js.map} +1 -1
  17. package/dist/{generate-9pR3i5Bj.js → generate-BW3N8EGb.js} +590 -275
  18. package/dist/generate-BW3N8EGb.js.map +1 -0
  19. package/dist/next.d.ts +2 -2
  20. package/dist/next.js +3 -3
  21. package/dist/{sarif-CWyTAQ0y.js → sarif-B5nrMqrT.js} +3 -3
  22. package/dist/{sarif-CWyTAQ0y.js.map → sarif-B5nrMqrT.js.map} +1 -1
  23. package/dist/tools.d.ts +2 -2
  24. package/dist/tools.js +3 -3
  25. package/dist/vite.d.ts +2 -2
  26. package/dist/vite.js +3 -3
  27. package/package.json +1 -1
  28. package/src/tools/config.ts +24 -6
  29. package/src/tools/native/android.ts +201 -0
  30. package/src/tools/native/apple.ts +139 -0
  31. package/src/tools/native/index.ts +17 -5
  32. package/src/tools/native/model.ts +25 -13
  33. package/src/tools/native/printf.ts +95 -0
  34. package/dist/bundle-KD-DiiFa.d.ts.map +0 -1
  35. package/dist/generate-9pR3i5Bj.js.map +0 -1
package/README.md CHANGED
@@ -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 number formats that say what the browser says.
41
+ with plurals and number formats that say what the browser says, and the string resources Android and Apple read.
42
42
 
43
43
  ## Quick start
44
44
 
@@ -238,7 +238,7 @@ function Ready({ copy, children }: { copy: Copy; children: ReactNode }) {
238
238
  }
239
239
  ```
240
240
 
241
- ### Outside the browser: Rust, Swift and Kotlin
241
+ ### Outside the browser: Rust, Swift, Kotlin, Android and Apple
242
242
 
243
243
  The parts of an app the browser doesn't draw, a tray, a menu bar, a CLI, an Android shell, can read
244
244
  the same catalog. `generate` writes one file per target in `native`, holding every locale:
@@ -295,6 +295,77 @@ What can't be written that way fails `generate`, with the fix: a date (pass it f
295
295
  count (`unitDisplay: 'long'`), and an MF2 option read from an argument. Leave such keys out of the
296
296
  target's `keys`.
297
297
 
298
+ #### Android resources
299
+
300
+ An Android app needs string resources even with the Kotlin target: its manifest, launcher shortcuts,
301
+ an accessibility service's description and every other XML resource name strings as `@string/…`, and
302
+ only a resource answers that. The `android` target writes them, with `out` naming the `res` folder:
303
+
304
+ ```ts
305
+ native: [{ language: 'android', out: 'android/app/src/main/res', keys: ['android'] }],
306
+ ```
307
+
308
+ `values/strings.xml` holds the source locale, and each other locale gets a folder named as Android
309
+ names it (`values-de`, `values-pt-rBR`, `values-b+sr+Latn` for a script), holding what that locale
310
+ shows, every fallback resolved. `name` renames the file, `strings` by default, so it can sit beside
311
+ strings you write yourself. A resource is named for its key, less the prefix, with `.` and `-` as
312
+ `_`: `android.sign-in.denied` is `R.string.sign_in_denied`. Above each is a comment with its context
313
+ and what each argument holds.
314
+
315
+ ```xml
316
+ <!-- Counts the cart. count: how many items the cart holds. -->
317
+ <plurals name="cart_items">
318
+ <item quantity="one">%1$d item in the cart.</item>
319
+ <item quantity="other">%1$d items in the cart.</item>
320
+ </plurals>
321
+ <!-- Greets the person signed in. firstName: the person's first name. -->
322
+ <string name="hello">Hello, %1$s!</string>
323
+ ```
324
+
325
+ Android fills placeholders in with `String.format` and picks a plural's arm itself, so a resource
326
+ holds less than the other targets: text, plain `{name}` placeholders, written `%1$s` by their place in
327
+ the source so a translation can reorder them, and one plural, which becomes `<plurals>` with its count
328
+ as `%1$d` (the text around it goes into each arm). Call it as Android does,
329
+ `resources.getQuantityString(R.plurals.cart_items, count, count)`. `String.format` isn't `Intl`, so
330
+ nothing here is checked against it: `%d` writes 1234 without grouping. Android picks the arm by the
331
+ device's language, so a plural a locale doesn't translate picks among the source's arms by that
332
+ language's rules. Escaping is done for you: quotes, apostrophes, `\`, `&`, `<`, a newline, a leading
333
+ `@` or `?`, `%` where the string is formatted, and spaces Android would collapse or trim.
334
+
335
+ What a resource can't hold fails `generate`, with the fix: a select, a date, a formatted number, a
336
+ second plural, an ordinal, an offset, an exact arm such as `=0` (Android's `zero` is a category, which
337
+ English never picks), a selector other than a bare `:number` or `:integer`, and a tag.
338
+
339
+ #### Apple string tables
340
+
341
+ An Apple app has the same gap: macOS and iOS read the permission prompts, the app's name and other
342
+ Info.plist strings from the bundle, never from Swift. The `apple` target writes `.strings` tables, a
343
+ `<locale>.lproj/<name>.strings` per locale under `out`, each holding what that locale shows. They are
344
+ plain `.strings` files, not a String Catalog, which only Xcode compiles: an app bundled by Tauri or a
345
+ script copies the `.lproj` folders into `Contents/Resources` as they are.
346
+
347
+ With `plist`, the table is `InfoPlist` and translates that Info.plist. A catalog key can't be written
348
+ `NSCameraUsageDescription`, so its words spell the plist key: `macos.ns-camera-usage-description` is
349
+ `NSCameraUsageDescription`. The key must be in the plist, since the system reads only the keys there,
350
+ and the plist's own text must be the source's, since it shows where no `.lproj` matches. Either way
351
+ round, `generate` fails with the fix:
352
+
353
+ ```ts
354
+ native: [{ language: 'apple', out: 'src-tauri/Resources', keys: ['macos'], plist: 'src-tauri/Info.plist' }],
355
+ ```
356
+
357
+ ```c
358
+ /* Asks for the camera. */
359
+ "NSCameraUsageDescription" = "Corner Shop scans a product's barcode with the camera.";
360
+ ```
361
+
362
+ Without `plist`, the table is `Localizable`, for what the system looks up by key, such as a push
363
+ notification's `loc-key`. An entry is named for its key less the prefix, as written (`tray.quit` is
364
+ `"quit"` under `['tray']`), and placeholders are written `%1$@` by their place in the source, filled
365
+ in by `String(format:)`, not `Intl`. A table holds text and plain `{name}` placeholders; a plural (it
366
+ would take a `.stringsdict`), a select, a date, a formatted number and a tag fail `generate`, and an
367
+ Info.plist string takes no placeholders at all. Show those from the Swift target.
368
+
298
369
  ## The catalog
299
370
 
300
371
  One file per area, `copy/catalog/<area>.yaml`. A key's first segment names its file, so
@@ -815,7 +886,7 @@ export default defineConfig({
815
886
  properNouns: [],
816
887
  pathSeparator: '›',
817
888
  },
818
- native: [], // Rust, Swift or Kotlin files: { language: 'rust', out: 'src/copy.rs', keys: ['tray'] }
889
+ native: [], // Rust, Swift or Kotlin files, Android's res or Apple's .lproj: { language: 'rust', out: 'src/copy.rs', keys: ['tray'] }
819
890
  scan: { dirs: ['src'] },
820
891
  });
821
892
  ```
package/dist/bun.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { t as Prune } from "./bundle-KD-DiiFa.js";
2
- import { i as ProsebookConfig } from "./config-DdMkKt_N.js";
1
+ import { t as Prune } from "./bundle-CpUIWb9j.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. */
package/dist/bun.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as installInspector, c as prunedModule, f as watchCatalog, i as hotLocale, l as scanSources, n as HOT_SET, o as keeper, r as beside, t as HOT_INDEX, u as unseenMessage } from "./bundle-DboItAre.js";
2
- import { n as generate, x as CatalogError } from "./generate-9pR3i5Bj.js";
3
- import { r as loadConfig } from "./config-BPSmoYel.js";
4
- import { t as inspectorEndpoints } from "./endpoints-CnUfCkIk.js";
2
+ import { n as generate, x as CatalogError } from "./generate-BW3N8EGb.js";
3
+ import { r as loadConfig } from "./config-nWZf9QmF.js";
4
+ import { t as inspectorEndpoints } from "./endpoints-BgG_5Vpm.js";
5
5
  import { basename, resolve } from "node:path";
6
6
  import { readFileSync } from "node:fs";
7
7
  // src/bun.ts
@@ -1,6 +1,6 @@
1
- import "./compile-xF3F4oIp.js";
2
- import "./config-DdMkKt_N.js";
1
+ import "./compile-ClK9htKr.js";
2
+ import "./config-CzaJJQ60.js";
3
3
  /** How a build prunes: `strict` also fails on a read whose key it can't see. */
4
4
  type Prune = 'strict' | 'loose' | false;
5
5
  export { Prune as t };
6
- //# sourceMappingURL=bundle-KD-DiiFa.d.ts.map
6
+ //# sourceMappingURL=bundle-CpUIWb9j.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-CpUIWb9j.d.ts","names":[],"sources":["../src/tools/bundle.ts"],"mappings":";;;KAiBY"}
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { f as watchCatalog } from "./bundle-DboItAre.js";
3
- import { D as whereOf, O as SYNTAX_NAME, _ as applyFixes, a as render, l as changeOf, n as generate, w as entryLocator, x as CatalogError, y as importTranslations } from "./generate-9pR3i5Bj.js";
4
- import { p as lint, r as loadConfig, t as CONFIG_FILES } from "./config-BPSmoYel.js";
5
- import { a as applyExtract, c as planExtract, i as planMigrate, n as todoOf, r as applyMigrate, t as SarifReport } from "./sarif-CWyTAQ0y.js";
3
+ import { D as whereOf, O as SYNTAX_NAME, _ as applyFixes, a as render, l as changeOf, n as generate, w as entryLocator, x as CatalogError, y as importTranslations } from "./generate-BW3N8EGb.js";
4
+ import { p as lint, r as loadConfig, t as CONFIG_FILES } from "./config-nWZf9QmF.js";
5
+ import { a as applyExtract, c as planExtract, i as planMigrate, n as todoOf, r as applyMigrate, t as SarifReport } from "./sarif-B5nrMqrT.js";
6
6
  import { o as scan, r as isReferenced, s as sourceFiles } from "./scan-Bx0_yDSi.js";
7
7
  import { join, relative, resolve } from "node:path";
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { nt as MessageFormatElement } from "./config-DdMkKt_N.js";
1
+ import { nt as MessageFormatElement } from "./config-CzaJJQ60.js";
2
2
  type Entry = readonly [key: string, ast: MessageFormatElement[]];
3
3
  interface CompiledLocale {
4
4
  /** `const oN = {…};` lines the messages refer to. */
@@ -13,4 +13,4 @@ declare function localeModule(compiled: CompiledLocale, banner: string, ts: bool
13
13
  /** The locale as one expression, for a bundler to inline in place of the module. */
14
14
  declare function localeExpression(compiled: CompiledLocale): string;
15
15
  export { localeModule as a, localeExpression as i, Entry as n, compileLocale as r, CompiledLocale as t };
16
- //# sourceMappingURL=compile-xF3F4oIp.d.ts.map
16
+ //# sourceMappingURL=compile-ClK9htKr.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compile-xF3F4oIp.d.ts","names":[],"sources":["../src/tools/compile.ts"],"mappings":";KA0BY,kBAAkB,aAAa,KAAK;UAoG/B;;EAEf;;EAEA;;;iBAIc,cAAc,SAAS,SAAS,QAAQ,QAAQ,0BAA0B;;iBAW1E,aAAa,UAAU,gBAAgB,gBAAgB;;iBAUvD,iBAAiB,UAAU"}
1
+ {"version":3,"file":"compile-ClK9htKr.d.ts","names":[],"sources":["../src/tools/compile.ts"],"mappings":";KA0BY,kBAAkB,aAAa,KAAK;UAoG/B;;EAEf;;EAEA;;;iBAIc,cAAc,SAAS,SAAS,QAAQ,QAAQ,0BAA0B;;iBAW1E,aAAa,UAAU,gBAAgB,gBAAgB;;iBAUvD,iBAAiB,UAAU"}
@@ -467,21 +467,35 @@ interface LintConfig extends WordLists {
467
467
  }
468
468
  /**
469
469
  * Native code generated from the catalog, for the parts of an app outside the browser: a tray, a
470
- * menu bar, a CLI. One file, holding every locale, with a typed function per message.
470
+ * menu bar, a CLI. One file, holding every locale, with a typed function per message; for Android and
471
+ * Apple, string resources, a file per locale.
471
472
  */
472
473
  interface NativeTarget {
473
- language: 'rust' | 'swift' | 'kotlin';
474
- /** The file written. */
474
+ language: 'rust' | 'swift' | 'kotlin' | 'android' | 'apple';
475
+ /**
476
+ * The file written; for Android, the `res` folder its `values*` folders go in, and for Apple, the
477
+ * folder its `.lproj` folders go in.
478
+ */
475
479
  out: string;
476
480
  /**
477
481
  * Key prefixes the file holds: `['native']` takes every `native.*` key, which it names without the
478
482
  * prefix (`native.tray.quit` is `tray_quit`). Every key, named in full, by default.
479
483
  */
480
484
  keys?: string[];
481
- /** The type holding the locales and messages: `Locale` in Rust, `Copy` in Swift and Kotlin. */
485
+ /**
486
+ * The type holding the locales and messages: `Locale` in Rust, `Copy` in Swift and Kotlin. For
487
+ * Android, the resource file's name: `strings`, for `values/strings.xml`. For Apple, the table's:
488
+ * `Localizable`, or `InfoPlist` with `plist`.
489
+ */
482
490
  name?: string;
483
491
  /** Kotlin's package. */
484
492
  package?: string;
493
+ /**
494
+ * For Apple, the Info.plist the table translates: each key names one of its strings, spelled in
495
+ * words (`ns-camera-usage-description` is `NSCameraUsageDescription`), and the source must say
496
+ * what the plist says.
497
+ */
498
+ plist?: string;
485
499
  }
486
500
  interface ProsebookConfig {
487
501
  /** The source-language catalog: one `<area>.yaml` per area. */
@@ -573,4 +587,4 @@ declare const CONFIG_FILES: string[];
573
587
  declare function loadConfig(root: string, inline?: ProsebookConfig): Promise<ResolvedConfig>;
574
588
  declare function resolveConfig(root: string, config: ProsebookConfig): ResolvedConfig;
575
589
  export { loadCatalog as $, RuleContext as A, DEFAULT_KINDS as B, Finding as C, Readings as D, Plugin as E, lint as F, Catalog as G, KindRule as H, phrase as I, KEY_PATTERN as J, CatalogError as K, sentences as L, applyEdits as M, compileRule as N, Report as O, definePlugin as P, entryLocation as Q, singularOf as R, ExistenceRule as S, OccurrenceRule as T, STRICT_LISTS as U, DEFAULT_SURFACES as V, Severity as W, Problem as X, Message as Y, Translation as Z, CapitalizationRule as _, ResolvedConfig as a, CoreRule as b, loadConfig as c, Mf2Function as d, previewOf as et, intlDate as f, CORE_RULES as g, toMf2 as h, ProsebookConfig as i, SubstitutionRule as j, Rule as k, resolveConfig as l, parseMf2 as m, LintConfig as n, MessageFormatElement as nt, WordLists as o, intlNumber as p, EntryLocation as q, NativeTarget as r, defineConfig as s, CONFIG_FILES as t, whereOf as tt, Isolate as u, CheckRule as v, HOLE as w, Edit as x, ConditionalRule as y, words as z };
576
- //# sourceMappingURL=config-DdMkKt_N.d.ts.map
590
+ //# sourceMappingURL=config-CzaJJQ60.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-DdMkKt_N.d.ts","names":[],"sources":["../src/tools/catalog.ts","../src/tools/presets.ts","../src/tools/lint.ts","../src/tools/mf2.ts","../src/tools/config.ts"],"mappings":"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"}
1
+ {"version":3,"file":"config-CzaJJQ60.d.ts","names":[],"sources":["../src/tools/catalog.ts","../src/tools/presets.ts","../src/tools/lint.ts","../src/tools/mf2.ts","../src/tools/config.ts"],"mappings":"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"}
@@ -917,7 +917,9 @@ const STRICT_LISTS = {
917
917
  const NATIVE_NAMES = {
918
918
  rust: "Locale",
919
919
  swift: "Copy",
920
- kotlin: "Copy"
920
+ kotlin: "Copy",
921
+ android: "strings",
922
+ apple: "Localizable"
921
923
  };
922
924
  /** Typed config, for `prosebook.config.ts`. */
923
925
  function defineConfig(config) {
@@ -978,10 +980,14 @@ function resolveConfig(root, config) {
978
980
  },
979
981
  native: (config.native ?? []).map((target, i) => {
980
982
  if (!(target.language in NATIVE_NAMES)) throw new Error(`native[${i}].language is "${target.language}": name one of ${Object.keys(NATIVE_NAMES).join(", ")}`);
983
+ if (target.plist !== void 0 && target.language !== "apple") throw new Error(`native[${i}].plist names an Info.plist, which only the apple target reads: remove it, or set language to 'apple'`);
984
+ const plist = target.plist === void 0 ? {} : { plist: at(target.plist) };
985
+ const name = target.name ?? (target.plist === void 0 ? NATIVE_NAMES[target.language] : "InfoPlist");
981
986
  return {
982
987
  ...target,
983
988
  out: at(target.out),
984
- name: target.name ?? NATIVE_NAMES[target.language]
989
+ name,
990
+ ...plist
985
991
  };
986
992
  }),
987
993
  scan: {
@@ -1029,4 +1035,4 @@ function lintRules(lint) {
1029
1035
  }
1030
1036
  export { words as _, DEFAULT_KINDS as a, CORE_RULES as c, compileRule as d, definePlugin as f, singularOf as g, sentences as h, resolveConfig as i, HOLE as l, phrase as m, defineConfig as n, DEFAULT_SURFACES as o, lint as p, loadConfig as r, STRICT_LISTS as s, CONFIG_FILES as t, applyEdits as u };
1031
1037
 
1032
- //# sourceMappingURL=config-BPSmoYel.js.map
1038
+ //# sourceMappingURL=config-nWZf9QmF.js.map