mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 20:22:45 +00:00
fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse, so a plugin main installs could fail to load here. Paseo never reads translations; the registry validates them at publish. Docs now say which checks are automated at publish and which are human review.
This commit is contained in:
@@ -68,11 +68,6 @@ describe("plugin manifest", () => {
|
||||
{ id: "INVALID" },
|
||||
{ description: 42 },
|
||||
{ requirements: { paseoo: ">=0.11.0" } },
|
||||
{ translations: { "zh-cn": { name: "示例" } } },
|
||||
{ translations: { en: { name: "Example" } } },
|
||||
{ translations: { "not a tag": { name: "Example" } } },
|
||||
{ translations: { "zh-CN": { name: " " } } },
|
||||
{ translations: { "zh-CN": { desc: "示例" } } },
|
||||
])("rejects invalid known fields: %j", async (fields) => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "paseo-plugin-manifest-"));
|
||||
directories.push(directory);
|
||||
@@ -133,17 +128,20 @@ describe("plugin manifest", () => {
|
||||
await expect(readPluginManifest(directory)).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("reads listing translations with each field optional", async () => {
|
||||
// Listing translations are read by the registry when it publishes, never by the daemon, so a
|
||||
// value the registry would refuse must not stop a plugin from installing.
|
||||
it.each([
|
||||
{ "zh-cn": { name: "示例" } },
|
||||
{ "zh-CN": { desc: "示例" } },
|
||||
"zh-CN",
|
||||
])("ignores listing translations: %j", async (translations) => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "paseo-plugin-manifest-"));
|
||||
directories.push(directory);
|
||||
const manifest = {
|
||||
id: "described",
|
||||
name: "Reviewer",
|
||||
description: "Reviews changes before merge",
|
||||
translations: { "zh-CN": { description: "合并前审查改动" } },
|
||||
};
|
||||
await writeFile(path.join(directory, "paseo-plugin.json"), JSON.stringify(manifest));
|
||||
await expect(readPluginManifest(directory)).resolves.toEqual(manifest);
|
||||
await writeFile(
|
||||
path.join(directory, "paseo-plugin.json"),
|
||||
JSON.stringify({ id: "described", translations }),
|
||||
);
|
||||
await expect(readPluginManifest(directory)).resolves.toEqual({ id: "described" });
|
||||
});
|
||||
|
||||
it("accepts only non-empty argv arrays for build commands", async () => {
|
||||
|
||||
@@ -22,27 +22,10 @@ const PluginAssetPathSchema = z
|
||||
value.split("/").at(-1) !== ".",
|
||||
"Expected a relative file path inside the plugin package",
|
||||
);
|
||||
// Keys are canonical language tags such as `zh-CN`, so a miscased `zh-cn` fails instead of never
|
||||
// matching a reader's language. A misspelled field would silently show English, so entries are strict.
|
||||
const PluginListingTranslationsSchema = z.record(
|
||||
z
|
||||
.string()
|
||||
.refine(
|
||||
(tag) => tag !== "en" && isCanonicalLanguageTag(tag),
|
||||
"Expected a canonical language tag other than en, such as zh-CN",
|
||||
),
|
||||
z
|
||||
.object({
|
||||
name: z.string().trim().min(1).optional(),
|
||||
description: z.string().trim().min(1).optional(),
|
||||
})
|
||||
.strict(),
|
||||
);
|
||||
const PluginManifestSchema = z.object({
|
||||
id: PluginIdSchema,
|
||||
name: z.string().trim().min(1).optional(),
|
||||
description: z.string().trim().min(1).optional(),
|
||||
translations: PluginListingTranslationsSchema.optional(),
|
||||
icon: PluginAssetPathSchema.refine(
|
||||
(value) => /\.png$/i.test(value),
|
||||
"Expected a relative path to a PNG",
|
||||
@@ -53,14 +36,6 @@ const PluginManifestSchema = z.object({
|
||||
build: z.array(PluginBuildCommandSchema).min(1).optional(),
|
||||
});
|
||||
|
||||
function isCanonicalLanguageTag(tag: string): boolean {
|
||||
try {
|
||||
return Intl.getCanonicalLocales(tag)[0] === tag;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export type PluginManifest = z.infer<typeof PluginManifestSchema>;
|
||||
|
||||
export async function readPluginManifest(directory: string): Promise<PluginManifest> {
|
||||
|
||||
@@ -51,8 +51,8 @@ Also supply a one-sentence description: use `package.json`'s `description` for n
|
||||
|
||||
### Other languages
|
||||
|
||||
The website shows listings in English and Simplified Chinese. English is required; translations are
|
||||
optional. Add translated listing text to `paseo-plugin.json`, keyed by language tag:
|
||||
Listings show in English and Simplified Chinese. English is required and translations are optional.
|
||||
Add a translated `name` and `description` to `paseo-plugin.json`, keyed by language tag:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -65,13 +65,18 @@ optional. Add translated listing text to `paseo-plugin.json`, keyed by language
|
||||
}
|
||||
```
|
||||
|
||||
Each field is optional. A missing translated `name` or `description` shows the English one.
|
||||
Each field is optional, and a missing one shows the English text. The registry checks
|
||||
`translations` when it publishes the plugin: keys are canonical language tags other than `en`, and
|
||||
each value sets only `name` or `description`, as non-empty strings. Paseo itself does not read
|
||||
`translations`, so a mistake there fails publication, not installation.
|
||||
|
||||
To translate the overview, put a whole copy beside `OVERVIEW.md` named for the language, such as
|
||||
`OVERVIEW.zh-CN.md`, and include it in the published package. The scaffold's `files` list already
|
||||
includes `OVERVIEW.*.md`. Readers in that language see the translated overview; without one, they
|
||||
see the English overview with a notice. Keep the translation in step with `OVERVIEW.md`; the overview rules
|
||||
above apply to it too.
|
||||
To translate the overview, publish a complete copy named for the language next to `OVERVIEW.md`,
|
||||
such as `OVERVIEW.zh-CN.md`. The scaffold's `files` list already includes `OVERVIEW.*.md`. The
|
||||
registry reads every `OVERVIEW.<tag>.md` as a translation and fails publication on a tag that is not
|
||||
a canonical language tag, so do not name other files that way. Readers in that language see the
|
||||
translation; others see the English overview with a notice. The overview rules above apply to
|
||||
translations, so update them when `OVERVIEW.md` changes. Reviewers can add or correct translations
|
||||
in the registry.
|
||||
|
||||
## Icons and screenshots
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ nav: 发布
|
||||
|
||||
### 其他语言 {#other-languages}
|
||||
|
||||
网站以英文和简体中文显示展示页。英文是必需的;翻译是可选的。在 `paseo-plugin.json` 中添加翻译后的展示文本,以语言标签为键:
|
||||
展示页以英文和简体中文显示。英文是必需的,翻译是可选的。在 `paseo-plugin.json` 中添加翻译后的 `name` 和 `description`,以语言标签为键:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -48,9 +48,9 @@ nav: 发布
|
||||
}
|
||||
```
|
||||
|
||||
每个字段都是可选的。缺少翻译后的 `name` 或 `description` 时,会显示英文的值。
|
||||
每个字段都是可选的,缺少的字段会显示英文。注册表在发布插件时检查 `translations`:键必须是除 `en` 之外的标准语言标签,每个值只能设置 `name` 或 `description`,且必须是非空字符串。Paseo 本身不读取 `translations`,因此这里的错误会导致发布失败,而不会影响安装。
|
||||
|
||||
要翻译概览,请在 `OVERVIEW.md` 旁边放一份以语言命名的完整副本,例如 `OVERVIEW.zh-CN.md`,并把它包含在发布的包中。脚手架的 `files` 列表已经包含 `OVERVIEW.*.md`。使用该语言的读者会看到翻译后的概览;没有翻译时,他们会看到英文概览和一条提示。请让翻译与 `OVERVIEW.md` 保持同步;上面的概览规则同样适用于它。
|
||||
要翻译概览,请在 `OVERVIEW.md` 旁边发布一份以语言命名的完整副本,例如 `OVERVIEW.zh-CN.md`。脚手架的 `files` 列表已经包含 `OVERVIEW.*.md`。注册表会把每个 `OVERVIEW.<标签>.md` 都当作翻译,标签不是标准语言标签时发布会失败,因此不要用这种方式命名其他文件。使用该语言的读者会看到翻译;其他读者会看到英文概览和一条提示。上面的概览规则同样适用于翻译,因此 `OVERVIEW.md` 变更时请同步更新翻译。审查者可以在注册表中补充或修正翻译。
|
||||
|
||||
## 图标和截图 {#icons-and-screenshots}
|
||||
|
||||
|
||||
@@ -52,16 +52,16 @@ The required root manifest is `paseo-plugin.json`:
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Behavior |
|
||||
| -------------- | -------- | ----------------------------------------------------------------------------------------- |
|
||||
| `id` | Yes | Default installation ID. |
|
||||
| `name` | No | Non-empty display name for the registry and website; independent of the installation ID. |
|
||||
| `icon` | No | Relative path to a PNG inside the plugin package. |
|
||||
| `media` | No | Array of image or video paths inside the package, or HTTPS URLs. An empty array is valid. |
|
||||
| `description` | No | Non-empty summary shown below the plugin ID in **Settings → Plugins**. |
|
||||
| `translations` | No | Listing `name` and `description` in other languages, keyed by language tag. |
|
||||
| `requirements` | No | Supported Paseo versions, described below. |
|
||||
| `build` | No | Preparation commands, described in the CLI reference. |
|
||||
| Field | Required | Behavior |
|
||||
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Yes | Default installation ID. |
|
||||
| `name` | No | Non-empty display name for the registry and website; independent of the installation ID. |
|
||||
| `icon` | No | Relative path to a PNG inside the plugin package. |
|
||||
| `media` | No | Array of image or video paths inside the package, or HTTPS URLs. An empty array is valid. |
|
||||
| `description` | No | Non-empty summary shown below the plugin ID in **Settings → Plugins**. |
|
||||
| `translations` | No | Listing text in other languages, read by the registry and ignored by Paseo. See [other languages](/docs/plugins/publishing#other-languages). |
|
||||
| `requirements` | No | Supported Paseo versions, described below. |
|
||||
| `build` | No | Preparation commands, described in the CLI reference. |
|
||||
|
||||
Paths are relative to `paseo-plugin.json`, use forward slashes, and cannot contain `..`
|
||||
segments. Registry listings for visual plugins require an interface image even though `media` is
|
||||
@@ -74,10 +74,6 @@ Unknown top-level fields are ignored. Known fields still validate, and unknown k
|
||||
Manifests using `name`, `icon`, or `media` require Paseo 0.11.0 or later; older daemons reject
|
||||
these fields during installation.
|
||||
|
||||
`translations` keys are canonical language tags other than `en`, such as `zh-CN`. Each value may
|
||||
set `name`, `description`, or both, as non-empty strings; other keys are rejected. See
|
||||
[other languages](/docs/plugins/publishing#other-languages) for how listings use them.
|
||||
|
||||
### Requirements
|
||||
|
||||
`requirements` is an optional object. Its currently supported key, `paseo`, accepts an npm semver
|
||||
|
||||
@@ -50,23 +50,21 @@ my-plugin/
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 是否必填 | 行为 |
|
||||
| -------------- | -------- | -------------------------------------------------------- |
|
||||
| `id` | 是 | 默认的安装 ID。 |
|
||||
| `name` | 否 | 注册表和网站使用的非空显示名称;与安装 ID 无关。 |
|
||||
| `icon` | 否 | 插件包内 PNG 文件的相对路径。 |
|
||||
| `media` | 否 | 包内图片或视频路径的数组,或 HTTPS URL。空数组是有效的。 |
|
||||
| `description` | 否 | 在 **设置 → 插件** 中显示在插件 ID 下方的非空摘要。 |
|
||||
| `translations` | 否 | 其他语言的展示 `name` 和 `description`,以语言标签为键。 |
|
||||
| `requirements` | 否 | 支持的 Paseo 版本,见下文。 |
|
||||
| `build` | 否 | 准备命令,见 CLI 参考。 |
|
||||
| 字段 | 是否必填 | 行为 |
|
||||
| -------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | 是 | 默认的安装 ID。 |
|
||||
| `name` | 否 | 注册表和网站使用的非空显示名称;与安装 ID 无关。 |
|
||||
| `icon` | 否 | 插件包内 PNG 文件的相对路径。 |
|
||||
| `media` | 否 | 包内图片或视频路径的数组,或 HTTPS URL。空数组是有效的。 |
|
||||
| `description` | 否 | 在 **设置 → 插件** 中显示在插件 ID 下方的非空摘要。 |
|
||||
| `translations` | 否 | 其他语言的展示文本,由注册表读取,Paseo 会忽略它。见[其他语言](/docs/plugins/publishing#other-languages)。 |
|
||||
| `requirements` | 否 | 支持的 Paseo 版本,见下文。 |
|
||||
| `build` | 否 | 准备命令,见 CLI 参考。 |
|
||||
|
||||
路径相对于 `paseo-plugin.json`,使用正斜杠,并且不能包含 `..` 片段。即使 `media` 在运行时 manifest 中是可选的,可视化插件的注册表展示页仍然需要一张界面图片。接受的格式和展示页要求见[图标和截图](/docs/plugins/publishing#icons-and-screenshots)。Paseo 会校验这些引用,但不会打开本地资源或获取 URL。
|
||||
|
||||
未知的顶层字段会被忽略。已知字段仍然会被校验,`requirements` 中的未知键会被拒绝,以免拼写错误的约束悄悄跳过兼容性检查。使用 `name`、`icon` 或 `media` 的 manifest 需要 Paseo 0.11.0 或更高版本;较旧的 daemon 会在安装时拒绝这些字段。
|
||||
|
||||
`translations` 的键是除 `en` 之外的标准语言标签,例如 `zh-CN`。每个值可以设置 `name`、`description` 或两者,必须是非空字符串;其他键会被拒绝。展示页如何使用它们,见[其他语言](/docs/plugins/publishing#other-languages)。
|
||||
|
||||
### 版本要求 {#requirements}
|
||||
|
||||
`requirements` 是一个可选对象。它目前支持的键 `paseo` 接受一个 npm semver 范围。省略 `requirements.paseo` 表示 `<0.8.0`:即该插件早于第一个破坏性的插件版本。Paseo 0.8 及更高版本会拒绝它,并附上[迁移指南](migration)的链接。空字符串、无效的范围以及未知的 manifest 要求键都会被拒绝。
|
||||
|
||||
Reference in New Issue
Block a user