@vestra-player/sdk 1.5.8 → 1.5.9
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/CHANGELOG.md +13 -0
- package/README.md +4 -1
- package/bin/vestra-plugin.mjs +170 -7
- package/dist/i18n-tools.d.ts +67 -0
- package/dist/i18n-tools.js +731 -0
- package/dist/i18n.d.ts +130 -0
- package/dist/i18n.js +673 -0
- package/dist/player-runtime.d.ts +1 -0
- package/dist/player-runtime.js +1 -0
- package/dist/plugin/appearance.js +27 -1
- package/dist/plugin/language.d.ts +4 -0
- package/dist/plugin/language.js +5 -0
- package/dist/plugin/osd.d.ts +3 -0
- package/dist/plugin/osd.js +45 -9
- package/dist/plugin/packages.js +10 -0
- package/dist/plugin/runtime.d.ts +3 -1
- package/dist/plugin/runtime.js +6 -0
- package/dist/plugin/scope.d.ts +1 -0
- package/dist/plugin/scope.js +2 -0
- package/dist/plugin/testing.d.ts +133 -3
- package/dist/plugin/testing.js +289 -47
- package/dist/plugin/types.d.ts +194 -20
- package/dist/plugin/ui.js +4 -1
- package/dist/plugin/window.d.ts +2 -0
- package/dist/plugin/window.js +4 -0
- package/dist/wire.d.ts +26 -2
- package/dist/wire.js +12 -2
- package/llms-full.txt +361 -21
- package/llms.txt +43 -39
- package/package.json +1 -1
- package/slint/vestra/markdown.slint +5 -5
- package/slint/vestra/widgets.slint +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.9
|
|
4
|
+
|
|
5
|
+
**Translations** (player 0.4.3). Additive; `apiVersion` stays `vestra/1`, and plugins written for 1.5.8 need no change. One test-visible change: a core action's `declaration.path` in `TestHost` is now the player's English key (read `display` for what the menu shows). The player now shows its interface in English, Simplified Chinese, Japanese, Korean, German, French or Spanish, chosen at start-up; a plugin writes every text in English and ships one gettext file per language. Plugins write no translation code: the player translates. Guides: [Translate a plugin](docs/how-to/i18n.md), [Migrate a plugin to English source text](docs/how-to/i18n-migration.md); example: `examples/i18n`.
|
|
6
|
+
|
|
7
|
+
- **`plugin.i18n.locale`** (new `I18n`, `Locale`): the interface language of this run, `"en"`, `"zh-Hans"`, `"ja"`, `"ko"`, `"de"`, `"fr"` or `"es"`; it never changes while the player runs. `"en"` from older players.
|
|
8
|
+
- **Templates with arguments**: `osd.show(text, args?)`, `OsdPanel.set(text, args?)` and `ConfirmOptions.args` (new `TemplateArgs`: an array for `{}` / `{0}`, an object for `{name}`). The player translates the template with the plugin's catalog, then fills it in. Without `args` the text shows as it is, so existing calls are unchanged; older players show the template unfilled.
|
|
9
|
+
- **Declarations are translated by the player** from the plugin's catalog: command titles, path segments and `reason`, settings page and panel titles, and the manifest's display fields. Menu paths are English source text and menus merge by them; the player's own menus have the English keys `Open`, `Playback`, `Subtitles`, `Video`, `Audio`, `Screen`, `Settings` (the `@video`-style ids still work, and the player still accepts the old Chinese titles of its own menus). New `PluginInfo.display` (`PluginDisplay`): a package's name, punchline, description and tags as shown.
|
|
10
|
+
- **`appearance.language`** (`LanguagePreference`: `"system"` or a locale) and **`appearance.setLanguage(language)`** (needs `ui.theme`): the saved choice, used from the next start.
|
|
11
|
+
- **Catalogs**: `locales/messages.pot` and `locales/<lang>.po` in the plugin's folder. New commands **`vestra-plugin i18n extract`** (finds `@tr` in `.slint` files, the texts of `commands.add`, `osd.show`, dialogs, settings and panel titles through the TypeScript type checker, and the manifest's display fields; merges into every `.po`, keeping translations; `--locale <lang>` starts one) and **`vestra-plugin i18n check`** (coverage, placeholder and plural problems, a stale template, sentences composed in TypeScript, non-literal values, non-English source text, Slint text without `@tr`; warnings only). `vestra-plugin check` prints the same warnings when the plugin has `locales/`. `vestra-plugin build` compiles each `.po` into `dist/locales/<lang>.json` and no longer packs `.po` files.
|
|
12
|
+
- **`TestHost`**: `locale` (also `startPlugin({ locale })`) loads the plugin's catalog for that language; `FakeCommand.display` is the menu as shown, `host.osd` and `osdPanel` the shown text (the template and args stay in `host.sent(...)`), new `host.confirmTexts`, `host.translate(text, args)`, `host.loadCatalog(directory)`, `host.language` / `setLanguage(...)`, settings page and panel titles as shown. The core actions' declarations use the player's English keys (`["Playback", "Seek", "Forward 5 s"]`); their `display` and the binding table show the player's words. While `locale` is not set, the player's own messages (core actions, refusals) stay in the Chinese of 1.5.8, so existing tests keep passing; with a `locale` they are the player's English (or its Chinese for `"zh-Hans"`).
|
|
13
|
+
- `LOCALES` in `@vestra-player/sdk/testing`.
|
|
14
|
+
- The Slint library copy (`slint/vestra/widgets.slint`, `markdown.slint`) uses `@tr` for its own texts (`UrlInputRow`'s default buttons, the code block's Copy button, the image placeholder).
|
|
15
|
+
|
|
3
16
|
## 1.5.8
|
|
4
17
|
|
|
5
18
|
**Trial loading is removed; `resolvers.fileTypes`; the CLI under `--permission`.** `apiVersion` stays `vestra/1`. The removal breaks code that called `PackageCandidate.trial()`: only Vestra Player AI did, and it moves to installing (an install can be rolled back).
|
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ Complete, tested plugins in [`examples/`](https://github.com/vestrasys/vestra-pl
|
|
|
72
72
|
| [menus](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/menus) | Radio items, separator groups and a menu item whose title changes. | [Step by step](https://vestrasys.github.io/vestra-player-sdk/documents/Example__Menus.html) |
|
|
73
73
|
| [theme-widgets](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/theme-widgets) | A tool window drawn with the player's theme and widgets. | [Step by step](https://vestrasys.github.io/vestra-player-sdk/documents/Example__Theme_widgets.html) |
|
|
74
74
|
| [markdown](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/markdown) | A window that shows a Markdown document. | [Step by step](https://vestrasys.github.io/vestra-player-sdk/documents/Example__Markdown.html) |
|
|
75
|
+
| [i18n](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/i18n) | A plugin in the player's language: English source text, a translation. | [Step by step](https://vestrasys.github.io/vestra-player-sdk/documents/Counter__i18n_.html) |
|
|
75
76
|
|
|
76
77
|
## Command line
|
|
77
78
|
|
|
@@ -81,6 +82,8 @@ Complete, tested plugins in [`examples/`](https://github.com/vestrasys/vestra-pl
|
|
|
81
82
|
| `vestra-plugin test [dir...]` | Runs `node --test` on `tests/*.test.{ts,mjs}`. |
|
|
82
83
|
| `vestra-plugin build [dir...] [--version x.y.z]` | Writes `dist/`: types stripped, files copied, manifest versioned. |
|
|
83
84
|
| `vestra-plugin pack [dir...] [--version x.y.z] [--out dir]` | Builds, then packs `dist/` as `<id>-<version>.vxp`. |
|
|
85
|
+
| `vestra-plugin i18n extract [dir...] [--locale lang]` | Collects the texts to translate into `locales/`, keeping translations. |
|
|
86
|
+
| `vestra-plugin i18n check [dir...]` | Translation coverage and untranslatable text (warnings only). |
|
|
84
87
|
|
|
85
88
|
## Compatibility
|
|
86
89
|
|
|
@@ -91,7 +94,7 @@ Complete, tested plugins in [`examples/`](https://github.com/vestrasys/vestra-pl
|
|
|
91
94
|
| 1.2 | `vestra/1` | 0.2 or newer | 26 (shipped with the player) |
|
|
92
95
|
| 1.3 | `vestra/1` | 0.2 or newer; 0.3 for the 1.3 additions (menu ids, `Theme.danger`, empty video chains) | 26 (shipped with the player) |
|
|
93
96
|
| 1.4 | `vestra/1` | 0.2 or newer; 0.3 for the 1.3 additions. `ThemeId` is `"vestra"` only (see the CHANGELOG) | 26 (shipped with the player) |
|
|
94
|
-
| 1.5 | `vestra/1` | 0.2 or newer; 0.3 for the 1.3 additions; 0.4 for the 1.5 additions (`vestra-light`, `@vestra/icons.slint`) | 26 (shipped with the player) |
|
|
97
|
+
| 1.5 | `vestra/1` | 0.2 or newer; 0.3 for the 1.3 additions; 0.4 for the 1.5 additions (`vestra-light`, `@vestra/icons.slint`); 0.4.3 for translations (1.5.9) | 26 (shipped with the player) |
|
|
95
98
|
|
|
96
99
|
Minor releases only add backwards-compatible API; a breaking change gets a new `apiVersion` and a new major version. `@vestra-player/sdk/runtime` is for the player only and not covered by semver.
|
|
97
100
|
|
package/bin/vestra-plugin.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* vestra-plugin: check, build, test and pack a Vestra Player plugin
|
|
3
|
+
* vestra-plugin: check, build, test and pack a Vestra Player plugin, and
|
|
4
|
+
* keep its translations up to date.
|
|
4
5
|
*
|
|
5
6
|
* vestra-plugin <check|build|test|pack> [plugin-dir...] [--version x.y.z] [--out dir]
|
|
7
|
+
* vestra-plugin i18n extract [plugin-dir...] [--locale <lang>]...
|
|
8
|
+
* vestra-plugin i18n check [plugin-dir...]
|
|
6
9
|
*
|
|
7
10
|
* A plugin directory holds manifest.json; the default is the current
|
|
8
11
|
* directory. Works in any project that depends on @vestra-player/sdk.
|
|
@@ -14,6 +17,24 @@
|
|
|
14
17
|
* pack build, then dist/ → <out>/<id>-<version>.vxp (a zip; default
|
|
15
18
|
* out: the current directory) with its sha256 and size
|
|
16
19
|
*
|
|
20
|
+
* Translations (docs/how-to/i18n.md). English is the source text; each
|
|
21
|
+
* language is a gettext file locales/<lang>.po:
|
|
22
|
+
*
|
|
23
|
+
* i18n extract find the texts to translate (@tr in .slint files, the
|
|
24
|
+
* texts handed to commands.add, osd.show, dialogs, settings
|
|
25
|
+
* and panel titles, the manifest's display fields), write
|
|
26
|
+
* locales/messages.pot and merge it into every
|
|
27
|
+
* locales/<lang>.po (translations kept, vanished ones marked
|
|
28
|
+
* obsolete); --locale <lang> starts a new language
|
|
29
|
+
* i18n check coverage per language, placeholder and plural problems, a
|
|
30
|
+
* stale messages.pot, and texts that cannot be translated
|
|
31
|
+
* (composed in TypeScript, not literals, not English, Slint
|
|
32
|
+
* text without @tr); warnings only, an unreadable .po fails
|
|
33
|
+
*
|
|
34
|
+
* `check` prints the same i18n warnings when the plugin has locales/;
|
|
35
|
+
* `build` compiles locales/<lang>.po to dist/locales/<lang>.json (the .po
|
|
36
|
+
* files themselves are not packed).
|
|
37
|
+
*
|
|
17
38
|
* Every command first checks manifest.json against the player's rules
|
|
18
39
|
* (display fields, paths inside the package, unknown fields); `check` also
|
|
19
40
|
* prints hints, such as an id without the `<owner>.` prefix the official
|
|
@@ -62,6 +83,10 @@ const sdkRoot = resolve(import.meta.dirname, "..");
|
|
|
62
83
|
const { manifestProblems } = await import(
|
|
63
84
|
pathToFileURL(join(sdkRoot, "dist/manifest.js")).href
|
|
64
85
|
);
|
|
86
|
+
const i18n = await import(pathToFileURL(join(sdkRoot, "dist/i18n.js")).href);
|
|
87
|
+
const i18nTools = await import(
|
|
88
|
+
pathToFileURL(join(sdkRoot, "dist/i18n-tools.js")).href
|
|
89
|
+
);
|
|
65
90
|
const SDK = "@vestra-player/sdk";
|
|
66
91
|
|
|
67
92
|
/** Never part of a production package. */
|
|
@@ -79,7 +104,8 @@ const SKIP_FILES = new Set([
|
|
|
79
104
|
"package.json",
|
|
80
105
|
"package-lock.json",
|
|
81
106
|
]);
|
|
82
|
-
|
|
107
|
+
// Translation sources: build packs the compiled dist/locales/<lang>.json.
|
|
108
|
+
const SKIP_EXTENSIONS = new Set([".md", ".log", ".zip", ".vxp", ".po", ".pot"]);
|
|
83
109
|
const isTypeScript = (file) => file.endsWith(".ts") && !file.endsWith(".d.ts");
|
|
84
110
|
const isDeclaration = (file) => /\.d\.(m|c)?ts$/.test(file);
|
|
85
111
|
|
|
@@ -299,6 +325,113 @@ async function check(target) {
|
|
|
299
325
|
await rm(scratch, { recursive: true, force: true });
|
|
300
326
|
}
|
|
301
327
|
console.log(`${target.name}: type check passed (${sources.length} files)`);
|
|
328
|
+
// Translation warnings never fail the check.
|
|
329
|
+
if (present(join(target.directory, "locales"))) {
|
|
330
|
+
try {
|
|
331
|
+
const { warnings, errors } = await i18nReport(target);
|
|
332
|
+
for (const line of [...warnings, ...errors])
|
|
333
|
+
console.log(`${target.name}: i18n: ${line}`);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.log(`${target.name}: i18n: ${error.message}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** The plugin's own production .ts / .mts and .slint files. */
|
|
341
|
+
async function sourceFiles(target) {
|
|
342
|
+
const all = await files(target.directory);
|
|
343
|
+
return {
|
|
344
|
+
typescript: all.filter(
|
|
345
|
+
(file) =>
|
|
346
|
+
(isTypeScript(file) || file.endsWith(".mts")) && !isDeclaration(file),
|
|
347
|
+
),
|
|
348
|
+
slint: all.filter((file) => file.endsWith(".slint")),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Compiler options of the type check (its tsconfig), for extraction. */
|
|
353
|
+
async function extraction(target) {
|
|
354
|
+
const ts = await typescript();
|
|
355
|
+
const { typescript: sources, slint } = await sourceFiles(target);
|
|
356
|
+
const config = checkConfig(
|
|
357
|
+
target,
|
|
358
|
+
sources.map((file) => join(target.directory, file)),
|
|
359
|
+
);
|
|
360
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
361
|
+
config,
|
|
362
|
+
ts.sys,
|
|
363
|
+
target.directory,
|
|
364
|
+
);
|
|
365
|
+
return i18nTools.extract({
|
|
366
|
+
directory: target.directory,
|
|
367
|
+
manifest: target.manifest,
|
|
368
|
+
slint,
|
|
369
|
+
typescript: sources,
|
|
370
|
+
compilerOptions: parsed.options,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function i18nReport(target) {
|
|
375
|
+
const { messages, findings } = await extraction(target);
|
|
376
|
+
return i18nTools.checkCatalogs(target.directory, messages, findings);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function i18nExtract(target, locales) {
|
|
380
|
+
for (const locale of locales) {
|
|
381
|
+
if (locale === "en")
|
|
382
|
+
fail("--locale en: English is the source language, it has no .po file");
|
|
383
|
+
if (!i18n.LOCALES.includes(locale))
|
|
384
|
+
fail(
|
|
385
|
+
`--locale ${locale}: the player's languages are ${i18n.LOCALES.filter((l) => l !== "en").join(", ")}`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
dependencies(target);
|
|
389
|
+
const { messages, findings } = await extraction(target);
|
|
390
|
+
const directory = join(target.directory, "locales");
|
|
391
|
+
await mkdir(directory, { recursive: true });
|
|
392
|
+
await writeFile(
|
|
393
|
+
join(directory, "messages.pot"),
|
|
394
|
+
i18nTools.template(target.manifest, messages),
|
|
395
|
+
);
|
|
396
|
+
console.log(
|
|
397
|
+
`${target.name}: locales/messages.pot: ${messages.length} messages`,
|
|
398
|
+
);
|
|
399
|
+
const languages = new Set(locales);
|
|
400
|
+
for (const name of await readdir(directory))
|
|
401
|
+
if (name.endsWith(".po")) languages.add(name.slice(0, -3));
|
|
402
|
+
for (const language of [...languages].sort()) {
|
|
403
|
+
if (language === "en" || !i18n.LOCALES.includes(language)) {
|
|
404
|
+
console.log(
|
|
405
|
+
`${target.name}: locales/${language}.po: skipped (not a player language)`,
|
|
406
|
+
);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const file = join(directory, `${language}.po`);
|
|
410
|
+
let existing = null;
|
|
411
|
+
if (present(file)) existing = await readFile(file, "utf8");
|
|
412
|
+
let merged;
|
|
413
|
+
try {
|
|
414
|
+
merged = i18nTools.merge(existing, language, messages);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
fail(`${target.name}: locales/${language}.po: ${error.message}`);
|
|
417
|
+
}
|
|
418
|
+
await writeFile(file, merged.text);
|
|
419
|
+
console.log(
|
|
420
|
+
`${target.name}: locales/${language}.po: ${merged.translated}/${merged.total} translated, ${merged.added} new, ${merged.obsolete} obsolete${existing === null ? " (created)" : ""}`,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
for (const finding of findings)
|
|
424
|
+
console.log(
|
|
425
|
+
`${target.name}: i18n: ${finding.file}:${finding.line}: ${finding.message}`,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function i18nCheck(target) {
|
|
430
|
+
dependencies(target);
|
|
431
|
+
const { warnings, errors } = await i18nReport(target);
|
|
432
|
+
for (const line of warnings) console.log(`${target.name}: i18n: ${line}`);
|
|
433
|
+
if (errors.length) fail(`${target.name}: ${errors.join("; ")}`);
|
|
434
|
+
if (!warnings.length) console.log(`${target.name}: i18n: no problems`);
|
|
302
435
|
}
|
|
303
436
|
|
|
304
437
|
function version(target, flag) {
|
|
@@ -455,6 +588,14 @@ async function build(target, flag) {
|
|
|
455
588
|
entry.built,
|
|
456
589
|
await readFile(join(out, entry.built), "utf8"),
|
|
457
590
|
);
|
|
591
|
+
let localeWarnings;
|
|
592
|
+
try {
|
|
593
|
+
localeWarnings = i18nTools.compileLocales(target.directory, out);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
fail(`${target.name}: ${error.message}`);
|
|
596
|
+
}
|
|
597
|
+
for (const line of localeWarnings)
|
|
598
|
+
console.log(`${target.name}: warning: ${line}`);
|
|
458
599
|
await writeFile(join(out, "package.json"), '{"type":"module"}\n');
|
|
459
600
|
await writeFile(
|
|
460
601
|
join(out, "manifest.json"),
|
|
@@ -558,8 +699,11 @@ async function pack(target, flag, outDirectory) {
|
|
|
558
699
|
);
|
|
559
700
|
}
|
|
560
701
|
|
|
561
|
-
const USAGE =
|
|
562
|
-
"usage: vestra-plugin <check|build|test|pack> [plugin-dir...] [--version x.y.z] [--out dir]"
|
|
702
|
+
const USAGE = [
|
|
703
|
+
"usage: vestra-plugin <check|build|test|pack> [plugin-dir...] [--version x.y.z] [--out dir]",
|
|
704
|
+
" vestra-plugin i18n extract [plugin-dir...] [--locale <lang>]...",
|
|
705
|
+
" vestra-plugin i18n check [plugin-dir...]",
|
|
706
|
+
].join("\n");
|
|
563
707
|
|
|
564
708
|
async function main() {
|
|
565
709
|
const args = process.argv.slice(2);
|
|
@@ -575,10 +719,25 @@ async function main() {
|
|
|
575
719
|
console.log(USAGE);
|
|
576
720
|
return;
|
|
577
721
|
}
|
|
578
|
-
|
|
722
|
+
let subcommand = null;
|
|
723
|
+
if (command === "i18n") {
|
|
724
|
+
subcommand = args.shift();
|
|
725
|
+
if (!["extract", "check"].includes(subcommand)) {
|
|
726
|
+
console.error(USAGE);
|
|
727
|
+
process.exit(2);
|
|
728
|
+
}
|
|
729
|
+
} else if (!["check", "build", "test", "pack"].includes(command)) {
|
|
579
730
|
console.error(USAGE);
|
|
580
731
|
process.exit(2);
|
|
581
732
|
}
|
|
733
|
+
const locales = [];
|
|
734
|
+
for (let index; (index = args.indexOf("--locale")) >= 0;) {
|
|
735
|
+
const [, value] = args.splice(index, 2);
|
|
736
|
+
if (value === undefined) fail("--locale needs a value");
|
|
737
|
+
locales.push(value);
|
|
738
|
+
}
|
|
739
|
+
if (locales.length && subcommand !== "extract")
|
|
740
|
+
fail("--locale applies to i18n extract");
|
|
582
741
|
const flag = option("--version");
|
|
583
742
|
const out = option("--out");
|
|
584
743
|
const directories = args.length ? args : ["."];
|
|
@@ -588,7 +747,9 @@ async function main() {
|
|
|
588
747
|
for (const directory of directories) {
|
|
589
748
|
try {
|
|
590
749
|
const target = await plugin(directory);
|
|
591
|
-
if (
|
|
750
|
+
if (subcommand === "extract") await i18nExtract(target, locales);
|
|
751
|
+
else if (subcommand === "check") await i18nCheck(target);
|
|
752
|
+
else if (command === "check") await check(target);
|
|
592
753
|
else if (command === "build") await build(target, flag);
|
|
593
754
|
else if (command === "pack") await pack(target, flag, out);
|
|
594
755
|
else await test(target);
|
|
@@ -599,7 +760,9 @@ async function main() {
|
|
|
599
760
|
}
|
|
600
761
|
}
|
|
601
762
|
if (failed.length) {
|
|
602
|
-
console.error(
|
|
763
|
+
console.error(
|
|
764
|
+
`${subcommand ? `i18n ${subcommand}` : command} failed for: ${failed.join(", ")}`,
|
|
765
|
+
);
|
|
603
766
|
process.exit(1);
|
|
604
767
|
}
|
|
605
768
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
/** A translatable text found in the sources. */
|
|
3
|
+
export interface Message {
|
|
4
|
+
context?: string;
|
|
5
|
+
id: string;
|
|
6
|
+
plural?: string;
|
|
7
|
+
/** `#.` notes (from `TRANSLATORS:` comments). */
|
|
8
|
+
notes: string[];
|
|
9
|
+
/** `path:line` (or `manifest.json`), relative to the plugin, sorted. */
|
|
10
|
+
references: string[];
|
|
11
|
+
}
|
|
12
|
+
/** Something extraction or the heuristics want the author to look at. */
|
|
13
|
+
export interface Finding {
|
|
14
|
+
/** Relative path, forward slashes. */
|
|
15
|
+
file: string;
|
|
16
|
+
line: number;
|
|
17
|
+
kind: "dynamic" | "composed" | "not-english" | "slint-text";
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ExtractOptions {
|
|
21
|
+
/** The plugin directory. */
|
|
22
|
+
directory: string;
|
|
23
|
+
/** The plugin's `manifest.json` (display fields are extracted). */
|
|
24
|
+
manifest: Record<string, unknown>;
|
|
25
|
+
/** The plugin's `.slint` files, relative to `directory`. */
|
|
26
|
+
slint: readonly string[];
|
|
27
|
+
/** The plugin's production `.ts` / `.mts` files, relative to `directory`. */
|
|
28
|
+
typescript: readonly string[];
|
|
29
|
+
/** Compiler options for a program over those files (as `check` uses). */
|
|
30
|
+
compilerOptions?: ts.CompilerOptions;
|
|
31
|
+
}
|
|
32
|
+
export declare const pluralForms: (language: string) => "nplurals=1; plural=0;" | "nplurals=2; plural=(n != 1);";
|
|
33
|
+
/** Find every translatable text of a plugin, in reference order. */
|
|
34
|
+
export declare function extract(options: ExtractOptions): {
|
|
35
|
+
messages: Message[];
|
|
36
|
+
findings: Finding[];
|
|
37
|
+
};
|
|
38
|
+
/** `locales/messages.pot` for these messages. */
|
|
39
|
+
export declare function template(manifest: Record<string, unknown>, messages: readonly Message[]): string;
|
|
40
|
+
/** The header of a new `<lang>.po`. */
|
|
41
|
+
export declare const newHeader: (language: string) => string;
|
|
42
|
+
/**
|
|
43
|
+
* Merge the template into a language's file: translations and fuzzy flags
|
|
44
|
+
* of present messages stay, new ones come untranslated, notes and
|
|
45
|
+
* references follow the template, vanished translated messages become
|
|
46
|
+
* obsolete (`#~`, at the end) and come back with their translation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function merge(existing: string | null, language: string, messages: readonly Message[]): {
|
|
49
|
+
text: string;
|
|
50
|
+
total: number;
|
|
51
|
+
translated: number;
|
|
52
|
+
added: number;
|
|
53
|
+
obsolete: number;
|
|
54
|
+
};
|
|
55
|
+
/** Problems found by `check` in one plugin's catalogs, as text lines. */
|
|
56
|
+
export declare function checkCatalogs(directory: string, messages: readonly Message[], findings: readonly Finding[]): {
|
|
57
|
+
warnings: string[];
|
|
58
|
+
errors: string[];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Compile every `locales/<lang>.po` of a player language to
|
|
62
|
+
* `<out>/locales/<lang>.json`. Returns the warnings (files the player never
|
|
63
|
+
* reads); throws on a `.po` that does not parse.
|
|
64
|
+
*/
|
|
65
|
+
export declare function compileLocales(directory: string, out: string): string[];
|
|
66
|
+
/** The relative path of a file from the plugin directory, forward slashes. */
|
|
67
|
+
export declare const relativePath: (directory: string, file: string) => string;
|