@hoardodile/create-plugin 0.1.14 → 0.1.15
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/dist/template/README.md +21 -0
- package/dist/template/manifest.json +8 -2
- package/dist/template/readme/README.de.md +19 -0
- package/dist/template/readme/README.es.md +18 -0
- package/dist/template/readme/README.ja.md +18 -0
- package/dist/template/src/TemplateView.tsx +5 -4
- package/dist/template/src/__tests__/locales.test.ts +86 -0
- package/dist/template/src/i18n.ts +16 -0
- package/dist/template/src/locales/de.ts +7 -0
- package/dist/template/src/locales/en.ts +6 -0
- package/dist/template/src/locales/es.ts +6 -0
- package/dist/template/src/locales/ja.ts +8 -0
- package/dist/template/src/locales/zh.ts +6 -0
- package/package.json +2 -2
package/dist/template/README.md
CHANGED
|
@@ -43,6 +43,9 @@ pnpm dev
|
|
|
43
43
|
`src/hooks.ts`.
|
|
44
44
|
- `src/shared.ts` — the plugin schema (`PluginSchema`) typed once and
|
|
45
45
|
shared between the server and client sides.
|
|
46
|
+
- `src/i18n.ts` + `src/locales/` — plugin UI strings via
|
|
47
|
+
`createPluginTranslation`, one bundle per supported language (see
|
|
48
|
+
[Localization](#localization)).
|
|
46
49
|
- `testdata/` — sample data for `pnpm dev`.
|
|
47
50
|
- `__tests__/` — Vitest unit tests against `createResourceAPIFixture`.
|
|
48
51
|
|
|
@@ -112,6 +115,24 @@ git tag v<version> && git push origin v<version>
|
|
|
112
115
|
Local installs (zip upload in **Settings → Plugins**) still work for
|
|
113
116
|
private packages.
|
|
114
117
|
|
|
118
|
+
## Localization
|
|
119
|
+
|
|
120
|
+
The app is localized in five languages (**en, zh, ja, de, es**). A plugin
|
|
121
|
+
can localize three surfaces:
|
|
122
|
+
|
|
123
|
+
1. **Display name / description** — the `i18n` block in `manifest.json`
|
|
124
|
+
maps `name`/`description` (and any extra label keys) to a locale table;
|
|
125
|
+
the host picks the text for the user's UI language.
|
|
126
|
+
2. **Plugin UI strings** — `src/i18n.ts` wraps `createPluginTranslation`
|
|
127
|
+
with one bundle per language in `src/locales/`; the hook starts in the
|
|
128
|
+
host's language and follows `languageChanged` pushes, falling back to
|
|
129
|
+
English for a language the plugin does not ship. `src/__tests__/locales.test.ts`
|
|
130
|
+
keeps the bundles in lockstep (key set, interpolation placeholders,
|
|
131
|
+
no untranslated English copy).
|
|
132
|
+
3. **The marketplace readme** — see [Publishing a readme](#publishing-a-readme):
|
|
133
|
+
one `README.<locale>.md` per language, `README.md` as the English
|
|
134
|
+
fallback.
|
|
135
|
+
|
|
115
136
|
## Publishing a readme
|
|
116
137
|
|
|
117
138
|
The marketplace detail view shows a per-release **Readme** tab. Ship the
|
|
@@ -13,11 +13,17 @@
|
|
|
13
13
|
"i18n": {
|
|
14
14
|
"name": {
|
|
15
15
|
"en": "Template",
|
|
16
|
-
"zh": "模板"
|
|
16
|
+
"zh": "模板",
|
|
17
|
+
"ja": "テンプレート",
|
|
18
|
+
"de": "Vorlage",
|
|
19
|
+
"es": "Plantilla"
|
|
17
20
|
},
|
|
18
21
|
"description": {
|
|
19
22
|
"en": "Template plugin — copy this directory to start your own plugin",
|
|
20
|
-
"zh": "模板插件——复制此目录作为你自己插件的起点"
|
|
23
|
+
"zh": "模板插件——复制此目录作为你自己插件的起点",
|
|
24
|
+
"ja": "テンプレートプラグイン — このディレクトリをコピーして独自プラグインを作成してください",
|
|
25
|
+
"de": "Vorlagen-Plugin — kopiere dieses Verzeichnis, um dein eigenes Plugin zu starten",
|
|
26
|
+
"es": "Plugin de plantilla — copia este directorio para empezar tu propio plugin"
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Mein Plugin
|
|
2
|
+
|
|
3
|
+
_Ersetze diese Readme durch einen echten Überblick: welchen Inhalt das
|
|
4
|
+
Plugin erkennt, wie es ihn rendert und wo Nutzer ihn sehen._
|
|
5
|
+
|
|
6
|
+
Diese Readme wird mit **jedem GitHub-Release** veröffentlicht — bearbeite
|
|
7
|
+
sie pro Release, damit jede Version ihre eigenen Notizen im Marktplatz
|
|
8
|
+
präsentiert.
|
|
9
|
+
|
|
10
|
+
## Funktionen
|
|
11
|
+
|
|
12
|
+
- Erkennt Ressourcen mit … und rendert sie inline.
|
|
13
|
+
- Deklariert folgende Berechtigungen: _siehe `manifest.json`_.
|
|
14
|
+
|
|
15
|
+
## Voraussetzungen
|
|
16
|
+
|
|
17
|
+
- hoardodile ≥ 0.1.1 (Details im Repository-README).
|
|
18
|
+
- Vertraue dem Repository, bevor du installierst — Plugin-Code läuft
|
|
19
|
+
serverseitig in einer Sandbox.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Mi plugin
|
|
2
|
+
|
|
3
|
+
_Sustituye esta readme por una descripción real: qué contenido detecta el
|
|
4
|
+
plugin, cómo lo renderiza y dónde lo verán las personas usuarias._
|
|
5
|
+
|
|
6
|
+
Esta readme se publica con **cada release de GitHub** — edítala en cada
|
|
7
|
+
versión para que cada release presente sus propias notas en el marketplace.
|
|
8
|
+
|
|
9
|
+
## Funciones
|
|
10
|
+
|
|
11
|
+
- Detecta recursos que contienen … y los renderiza en línea.
|
|
12
|
+
- Declara los siguientes permisos: _consulta `manifest.json`_.
|
|
13
|
+
|
|
14
|
+
## Requisitos
|
|
15
|
+
|
|
16
|
+
- hoardodile ≥ 0.1.1 (detalles en el README del repositorio).
|
|
17
|
+
- Confía en el repositorio antes de instalar: el código del plugin se
|
|
18
|
+
ejecuta en el servidor dentro de un sandbox restringido.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 私のプラグイン
|
|
2
|
+
|
|
3
|
+
_この README を実際の概要に置き換えてください:プラグインが認識する内容、
|
|
4
|
+
そのレンダリング方法、ユーザーがどこで目にするかを説明します。_
|
|
5
|
+
|
|
6
|
+
この README は**すべての GitHub Release** と一緒に公開されます——リリースの
|
|
7
|
+
たびに編集し、各バージョンがマーケットプレイスでそれぞれの説明を提示できるようにしてください。
|
|
8
|
+
|
|
9
|
+
## 機能
|
|
10
|
+
|
|
11
|
+
- ~を含むリソースを認識し、インラインでレンダリングします。
|
|
12
|
+
- 権限の宣言:_`manifest.json` を参照してください_。
|
|
13
|
+
|
|
14
|
+
## 要件
|
|
15
|
+
|
|
16
|
+
- hoardodile ≥ 0.1.1(詳しくはリポジトリの README を参照)。
|
|
17
|
+
- インストール前にこのリポジトリを信頼してください——プラグインコードは
|
|
18
|
+
制限付きサンドボックス内のサーバー側コードとして実行されます。
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { usePluginAPI } from "./hooks"
|
|
2
|
+
import { useTranslation } from "./i18n"
|
|
2
3
|
|
|
3
4
|
export function TemplateView() {
|
|
4
5
|
const api = usePluginAPI()
|
|
6
|
+
const { t } = useTranslation()
|
|
5
7
|
const files = api.resource.sourceMeta?.files ?? []
|
|
6
8
|
const hdtplCount = api.resource.sourceMeta?.hdtplCount
|
|
7
9
|
|
|
@@ -10,14 +12,13 @@ export function TemplateView() {
|
|
|
10
12
|
<header>
|
|
11
13
|
<h1 className="text-lg font-semibold">{api.resource.name}</h1>
|
|
12
14
|
<p className="text-gray-500 dark:text-gray-400">
|
|
13
|
-
|
|
14
|
-
your own.
|
|
15
|
+
{t("renderedBy")} {t("editHint")}
|
|
15
16
|
</p>
|
|
16
17
|
</header>
|
|
17
18
|
<p>
|
|
18
19
|
{hdtplCount !== undefined
|
|
19
|
-
?
|
|
20
|
-
: "
|
|
20
|
+
? t("fileCount", { count: hdtplCount })
|
|
21
|
+
: t("noSourceMeta")}
|
|
21
22
|
</p>
|
|
22
23
|
<ul className="list-inside list-disc">
|
|
23
24
|
{files.map((file) => (
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import de from "../locales/de"
|
|
3
|
+
import en from "../locales/en"
|
|
4
|
+
import es from "../locales/es"
|
|
5
|
+
import ja from "../locales/ja"
|
|
6
|
+
import zh from "../locales/zh"
|
|
7
|
+
|
|
8
|
+
const BUNDLES: Record<string, Record<string, unknown>> = {
|
|
9
|
+
en: en as unknown as Record<string, unknown>,
|
|
10
|
+
zh: zh as unknown as Record<string, unknown>,
|
|
11
|
+
ja: ja as unknown as Record<string, unknown>,
|
|
12
|
+
de: de as unknown as Record<string, unknown>,
|
|
13
|
+
es: es as unknown as Record<string, unknown>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function flatten(
|
|
17
|
+
obj: Record<string, unknown>,
|
|
18
|
+
path: string[] = [],
|
|
19
|
+
out: { key: string; value: string }[] = [],
|
|
20
|
+
): { key: string; value: string }[] {
|
|
21
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
22
|
+
if (typeof v === "object" && v !== null) {
|
|
23
|
+
flatten(v as Record<string, unknown>, [...path, k], out)
|
|
24
|
+
} else {
|
|
25
|
+
out.push({ key: [...path, k].join("."), value: String(v) })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function vars(value: string): string {
|
|
32
|
+
return (value.match(/\{\{[^}]+\}\}/g) ?? [])
|
|
33
|
+
.map((m) => m.slice(2, -2))
|
|
34
|
+
.sort()
|
|
35
|
+
.join(",")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("plugin locale parity", () => {
|
|
39
|
+
const enFlat = flatten(BUNDLES.en!)
|
|
40
|
+
const enKeys = new Set(enFlat.map((r) => r.key))
|
|
41
|
+
|
|
42
|
+
it("ships every hoardodile-supported language", () => {
|
|
43
|
+
expect(Object.keys(BUNDLES).sort()).toEqual(["de", "en", "es", "ja", "zh"])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("has identical flat key sets across all bundles", () => {
|
|
47
|
+
for (const [lang, bundle] of Object.entries(BUNDLES)) {
|
|
48
|
+
const flat = flatten(bundle)
|
|
49
|
+
const keys = new Set(flat.map((r) => r.key))
|
|
50
|
+
expect(
|
|
51
|
+
[...enKeys].filter((k) => !keys.has(k)),
|
|
52
|
+
`keys only in en`,
|
|
53
|
+
).toEqual([])
|
|
54
|
+
expect(
|
|
55
|
+
[...keys].filter((k) => !enKeys.has(k)),
|
|
56
|
+
`keys only in ${lang}`,
|
|
57
|
+
).toEqual([])
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("uses the same interpolation placeholders per key", () => {
|
|
62
|
+
for (const [lang, bundle] of Object.entries(BUNDLES)) {
|
|
63
|
+
const byKey = new Map(flatten(bundle).map((r) => [r.key, r.value]))
|
|
64
|
+
const mismatched: string[] = []
|
|
65
|
+
for (const { key, value } of enFlat) {
|
|
66
|
+
const other = byKey.get(key)
|
|
67
|
+
if (other !== undefined && vars(value) !== vars(other)) {
|
|
68
|
+
mismatched.push(key)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
expect(mismatched, `placeholder mismatch in ${lang}`).toEqual([])
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("never ships untranslated English source copy", () => {
|
|
76
|
+
const english = new Map(enFlat.map((r) => [r.key, r.value]))
|
|
77
|
+
for (const [lang, bundle] of Object.entries(BUNDLES)) {
|
|
78
|
+
if (lang === "en") continue
|
|
79
|
+
const untranslated: string[] = []
|
|
80
|
+
for (const { key, value } of flatten(bundle)) {
|
|
81
|
+
if (value === english.get(key)) untranslated.push(key)
|
|
82
|
+
}
|
|
83
|
+
expect(untranslated, `untranslated keys in ${lang}`).toEqual([])
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createPluginTranslation } from "@hoardodile/sdk-react"
|
|
2
|
+
import de from "./locales/de"
|
|
3
|
+
import en from "./locales/en"
|
|
4
|
+
import es from "./locales/es"
|
|
5
|
+
import ja from "./locales/ja"
|
|
6
|
+
import zh from "./locales/zh"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Plugin UI strings, one bundle per hoardodile-supported language (see
|
|
10
|
+
* `@hoardodile/i18n` — en, zh, ja, de, es). The host's current language is
|
|
11
|
+
* picked automatically and follows `languageChanged` pushes; a language
|
|
12
|
+
* the plugin misses falls back to English.
|
|
13
|
+
*/
|
|
14
|
+
const { useTranslation } = createPluginTranslation({ en, zh, ja, de, es })
|
|
15
|
+
|
|
16
|
+
export { useTranslation }
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
renderedBy: "Gerendert vom Template-Plugin.",
|
|
3
|
+
editHint:
|
|
4
|
+
"Bearbeite src/TemplateView.tsx, um daraus dein eigenes Plugin zu machen.",
|
|
5
|
+
fileCount: "{{count}} .hdtpl-Datei(en) — einmal durch detect klassifiziert.",
|
|
6
|
+
noSourceMeta: "Noch keine sourceMeta.",
|
|
7
|
+
} as const
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
renderedBy: "Renderizado por el plugin de plantilla.",
|
|
3
|
+
editHint: "Edita src/TemplateView.tsx para convertirlo en tu propio plugin.",
|
|
4
|
+
fileCount: "{{count}} archivo(s) .hdtpl — clasificados una vez por detect.",
|
|
5
|
+
noSourceMeta: "Aún no hay sourceMeta.",
|
|
6
|
+
} as const
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoardodile/create-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Scaffold a hoardodile content plugin.",
|
|
6
6
|
"keywords": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@clack/prompts": "^1.7.0",
|
|
39
39
|
"zod": "^4.4.3",
|
|
40
|
-
"@hoardodile/sdk-types": "0.1.
|
|
40
|
+
"@hoardodile/sdk-types": "0.1.15"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.2.0",
|