mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* feat(landing-page): localize plugins library across 18 locales PR #2926 shipped the new `/plugins/` library hub + four kind sub-routes + detail pages, but the chrome was English-only — visitors landing on `/zh/plugins/` saw the old marketplace registry placeholder rendered by the catch-all instead, and detail pages rendered identical English copy regardless of locale prefix. This PR brings the plugin surface to feature parity with `/zh/skills/`, `/zh/templates/`, `/zh/systems/`, `/zh/craft/`. ## What changes - New `app/_lib/plugins-i18n.ts` — single source for all plugin chrome copy (hub, list pages, chip rails, share dialog, detail-page meta labels). English baseline + 17 locale overrides keyed on `LandingLocaleCode` (the same short-code shape `localeFromPath()` returns). Missing keys per locale fall back to English so a partially-translated locale still renders sensibly. Translations cover hub copy, four tile titles + blurbs, seven artifact-kind labels + descriptions, 23 scene-subcategory labels, 18 detail-page chrome strings, and a six-key share-dialog table with a per-locale `shareTemplate({title, url})` function (translated for every locale where `_lib/i18n.ts` already had one — same voice). - `app/pages/plugins/{,templates/,templates/[kind]/,skills/,systems/, craft/,[slug]/}/index.astro` — every hardcoded English string now reads `getPluginsCopy(locale)` keys. Page logic and routing unchanged. - New short-code wrappers under `app/pages/[locale]/plugins/` — six files (hub + three sub-routes + `[kind]/` and `[slug]/`) following the same pattern `[locale]/skills/index.astro` already uses: each re-exports the canonical page component and adds a per-locale `getStaticPaths()` so the build emits 17 locale prefixes per plugin route. Total plugin-route prerender count goes from ~390 to ~7 000, matching the existing skill/template scaling. - Catch-all (`[locale]/[...path].astro`) — old `getPublicPlugins` / `getRegistryCounts` registry rendering removed (placeholder UI that was never wired to a real marketplace data source). Plugin routes now live exclusively under `[locale]/plugins/...` short-code wrappers, so the catch-all stops claiming `'plugins'` as a route root. The dead-code path also drops a `pluginCounts.all` reference the title row was reading. - `.plugins-tile-grid` styles promoted from a scoped `<style>` in the default-locale hub to global `app/sub-pages.css` so the short-code wrapper renders the same hub markup without re-mounting per-page CSS — `display: contents`-style scoping pitfalls in Astro's per-component CSS scoping made this the cleanest fix. ## Surface area - [ ] **UI** — new page / dialog / panel / menu item / setting / empty state in `apps/web` or `apps/desktop` - [ ] **Keyboard shortcut** — new or changed - [ ] **CLI / env var** — new `od` subcommand or flag, new `tools-dev` flag, or new `OD_*` env var - [ ] **API / contract** — new `/api/*` endpoint, new SSE event, or changed shape in `packages/contracts` - [ ] **Extension point** — new entry under `skills/`, `design-systems/`, `design-templates/`, or `craft/`, or change to the skills protocol - [ ] **i18n keys** — new translation keys (full plugin chrome added across all 18 locales) - [ ] **New top-level dependency** — adding any new entry to the **root** `package.json` - [ ] **Default behavior change** — changes what existing users experience without opting in - [x] **None** — landing-page-only restoration of i18n parity for the plugin surface ## Validation - `pnpm --filter @open-design/landing-page typecheck` → 0 errors - `pnpm --filter @open-design/landing-page build:static` → 16 127 pages built (+6 584 over current main: ~388 plugin detail pages × 17 locale prefixes plus the hub + four sub-routes × 17 locales). - `copy-example-html.ts` reports `266 entry files + 65 referenced files`, identical to before — no regression in the asset-mirroring pipeline. - Local Playwright smoke (`/zh/plugins/...`): - `/zh/plugins/` renders `<title>插件库 · Open Design</title>`, label `插件库`, h1 `407 个可组合的构件。`, four tiles labelled `模板 / 技能 / 设计系统 / 工艺`. - `/zh/plugins/templates/video/` renders h1 `48 视频`, scene chips `全部 / 动效 / 短视频 / 营销 / 产品 / 数据讲解`. - `/zh/plugins/example-article-magazine/` share dialog renders `复制下面的文案、然后跳到你想分享的平台粘贴即可` etc., share template auto-interpolates plugin title + URL into Chinese voice. - All 18 locale prefixes (`/zh`, `/zh-tw`, `/ja`, `/ko`, `/de`, `/fr`, `/ru`, `/es`, `/pt-br`, `/it`, `/vi`, `/pl`, `/id`, `/nl`, `/ar`, `/tr`, `/uk`) → 200 across hub + four sub-routes + sample detail page. - English `/plugins/` unchanged (default-locale path bypasses the `[locale]/...` wrapper). * feat(landing-page): finish plugins i18n chrome across 18 locales The first localization pass shipped a partial fix: hub headings, lead copy, two-level page chrome, detail-page metadata labels, the share dialog, and the chip rail were still falling back to English on every non-English locale because plugins-i18n.ts only filled a chrome slice for `zh` and the file header even claimed "7 artifact-kind labels and 25 scene-subcategory labels are translated" for every locale that did not yet have those blocks. Three changes close the visible gap: 1. plugins-i18n.ts: fills the 27 still-missing chrome fields per locale for zh-tw / ja / ko / de / fr / ru / es / pt-br / it / vi / pl / id / nl / ar / tr / uk. Includes the 7-key category map, the 23-key subcategory map, hubHeading / hubLead, the 4 *Label / *Heading / *Lead triples for the templates / skills / systems / craft hub pages, the 4 tile blurbs, the 4 browse buttons, sceneLabel, allChip, the 12 detail-page metadata labels (mode / scenario / platform / surface / author / manifest id / tags / preview caption / find on GitHub / homepage / open in new tab) and bucket label map, the detail share dialog (title / copy link / jump-to), and the header-side nav.plugins entry. zh receives the same 11 detail-page and share-dialog labels it was also missing. 2. header.tsx + site-footer.astro: routes the hardcoded "Plugins / Templates / Skills / Systems / Craft" labels through `nav.*` from HeaderCopy, so every locale gets its own dropdown trigger and footer column. Adds `nav.plugins` to HeaderCopy and fills it in 18 locales with the local form ("插件" / "プラグイン" / "Plugins" / "Plug-ins" / "Plaginy" / "الإضافات" / etc). 3. plugin-row.astro + content-i18n.ts: chip rail. The bundled-plugin branch now runs raw `mode` / `scenario` slugs through the shared localizeTaxonomyValue, and that helper now also consults the plugins-i18n subcategory map before giving up. localizeTaxonomyValue now returns undefined on a true miss instead of the unknownTag placeholder, so chips drop quietly instead of showing "Category" / "分類" / "Categoría" for taxonomy slugs we have not localized yet. Callers that genuinely want the placeholder (`localizeContentTag`, blog `category`, system noun) still keep the explicit fallback. Out of scope and tracked separately: per-plugin title and description in plugins/_official/* (author-supplied English metadata, ~401 plugins without an i18n schema in the manifest yet — needs RFC + tooling before the manifests can be expanded), and adding the long tail of mode / scenario / category slugs (`code-migration`, `plugin-sharing`, `tune-collab`, `live-artifacts`, `engineering`, ...) to TAXONOMY_TERMS so chips render localized labels for every taxonomy value rather than dropping silently. * feat(landing-page): cover plugins chip rail long-tail taxonomy slugs PR #3010's first round localized the high-frequency mode/scenario chips (prototype, video, image, marketing, design, ...) but left the ~37 mode/scenario and 14 category slugs that show up in real `od.*` metadata — code-migration, plugin-sharing, design-system, planning, scenario, refine, discovery, handoff, token-map, tune-collab, orbit, live-artifacts, engineering, healthcare, hr, sales, support, default-router, downstream-export, figma-migration, media-generation, plugin-authoring, validation, 3d-shaders, animation-motion, audio-music, creative-direction, design-systems, diagrams, documents, image-generation, marketing-creative, screenshots, slides, video-generation, web-artifacts, ... — falling through to undefined and dropping their chip silently on every non-English locale. The data layer is the source of truth here, so this expansion lands in `content-i18n.ts:TAXONOMY_TERMS` / `CATEGORY_LABELS` rather than the plugins-i18n catalog: a single dictionary entry per slug fans out to every chip-rail consumer (catalog rows, detail metadata, the templates/[kind] facets) without each consumer touching its own copy. Translations cover all 17 non-`en` locales. Brand and product nouns (Figma, Open Design, BYOK, plugin) stay literal; technical taxonomy slugs get short equivalents that read as chips rather than full prose. The result on `/ja/plugins/skills/` matches `/plugins/skills/` chip-for-chip (30 chips both sides) instead of dropping 27 of them the way the previous iteration did. * feat(landing-page): read manifest title_i18n / description_i18n on bundled plugins PR #3010's prior rounds localized chrome and chip rails but the catalog's most prominent text — each row's plugin name and blurb — stayed English on every non-English locale. The plugin manifest schema (`packages/contracts/src/plugins/manifest.ts`) has supported `title_i18n` and `description_i18n` (Record<locale, string>) on every manifest from spec v1; ~24 of the 401 first-party manifests already carry one for `zh-CN`. The reader was just never wired to use them. This change does the reader half: bundled-plugins.ts captures the two i18n maps off each `open-design.json`, plugin-row.astro and the detail page resolve them at render time via two new helpers (`resolveBundledTitle`, `resolveBundledDescription`) that mirror the short→long fallback chain documented in the manifest spec (`htmlLang` like `zh-CN` → short `LandingLocaleCode` like `zh` → primary tag → `en` → English baseline). The static-paths pass still runs once for all locales — it has to, since each manifest produces one URL — but the title/description shown on the rendered page now reads the locale off `Astro.url.pathname` and picks the right entry out of the maps. Verified locally: `/zh/plugins/example-card-twitter/` now reads "Twitter 分享卡 / 推特金句 / 数据卡, 适合配推文" from the manifest's existing `zh-CN` block instead of the English baseline. Plugin-data half follows in a separate commit. The 17 non-English locales × 401 manifests need backfilling so the reader has something to resolve to; that's data, not schema, and lands as a sequence of manifest patches rather than tangled with this code change. * feat(plugins): translate scenarios bucket title/description across 17 locales Closes the first chunk of #3028. Eleven scenarios plugins (the default-scenario bundle for each taskKind: code-migration, figma-migration, media-generation, new-generation, tune-collab, plugin-authoring; the default design router; the React / Vue / Next.js downstream-export starters; and the Refine baseline) get title_i18n + description_i18n filled for all 17 non-English locales the landing page serves (zh-CN, zh-TW, ja, ko, de, fr, ru, es, pt-BR, it, vi, pl, id, nl, ar, tr, uk). The reader landed in 7ddfe36; this commit is data-only. taskKind slugs that other docs reference by name (`code-migration`, `figma-migration`, `tune-collab`, etc.) stay literal in the descriptions so cross-references still resolve. Brand nouns — Open Design, Next.js, React, Vue, Figma — also stay literal. `/ja/plugins/od-code-migration/` now reads "コードマイグレーション(デフォルトシナリオ)" instead of the English baseline; `/zh/plugins/skills/` shows "代码迁移(默认场景)" in the catalog row. Remaining buckets (image-templates 45, video-templates 50, examples 140, design-systems 142 = 377 plugins) follow in subsequent commits in this PR. * fix(landing-page): drop CJK template wrap when source name is still English The Chinese / Japanese / Korean fallback templates for craft, skill, template, system, plugin, and blog text splice the source `name` / `title` into a CJK sentence frame: ``${name}工艺规则``, ``Open Design 指南:${topic}``, ``${name} は…のスキルです``. When the underlying SKILL.md / craft markdown / blog frontmatter still ships an English name (true for ~95% of the catalog today), that produces mid-sentence script straddling on `/zh/...`, `/zh-tw/...`, `/ja/...`, `/ko/...` like: H1 : "Editorial typography hierarchy工艺规则" Lead : "这条 Open Design 工艺规则定义 Editorial typography hierarchy 的执行标准…" Plug : "video 插件 · 3D Animated Boy Building Lego" That reads worse than the all-English fallback, because the visitor parses the page in two scripts at once. Adds a `nameNeedsEnglishFallback` guard that fires for the four CJK locales whenever the spliced-in name has no CJK characters of its own, and threads it through every `localizeXxxText` helper: craft, template, system, plugin, skill, blog. When it fires the helper returns the raw English content untouched, so the section renders end-to-end in one language. Chrome (header, footer, breadcrumb, buttons, share dialog) keeps its CJK rendering — only the title-and-lead block falls back. Side benefit: the same guard kicks in on the long tail of plugin manifests still pending `title_i18n` / `description_i18n` backfill (tracked in #3028), so `/zh/plugins/<bundled>/` no longer pairs a "video 插件 · 3D Animated Boy Building Lego" title with a Chinese breadcrumb. The page reads "3D Animated Boy Building Lego" + the English manifest description, while header / footer / breadcrumbs stay localized. Once a manifest ships its i18n maps, the chrome and body re-converge automatically. Non-CJK non-Latin scripts (ar, vi, ...) keep the previous behavior — their templates already read tolerably with English names. If that turns out to be wrong on a real audit, the same guard generalizes by adding the matching Unicode range and locale set. * feat(plugins): translate image-templates bucket title/description across 17 locales 44 of 45 image-templates plugins get title_i18n + description_i18n filled for all 17 non-English locales (zh-CN, zh-TW, ja, ko, de, fr, ru, es, pt-BR, it, vi, pl, id, nl, ar, tr, uk). Generated via Claude Sonnet 4.5 over the OpenRouter gateway, ~$1.38 in API spend, 156s wall-clock. Brand and cultural references stay literal (Open Design, Lego, Hanfu, Showa, Pokémon, Black Myth: Wukong). Long AI generation prompts collapse to a 1-2 sentence summary capturing what the plugin does — the description doubles as catalog blurb on the landing site, not as the actual generation prompt (which lives in example.html / the manifest's preview entry). Skipped: `profile-avatar-realistically-imperfect-ai-selfie` returned malformed JSON on three retries; will rerun with a tighter prompt in a follow-up commit. Catalog rows for that plugin keep falling back to the raw English fields per #3010's reader change, so nothing breaks. Tracking: closes the image-templates row in #3028. * feat(plugins): translate video-templates bucket title/description across 17 locales 49 of 50 video-templates plugins get title_i18n + description_i18n filled for the 17 non-English landing locales. Generated via Claude Sonnet 4.5 over OpenRouter, ~$1.47 in API spend, 177s wall-clock. HyperFrames templates, the Three Kingdoms cinematic series, the Seedance/short-film prompts, and the K-pop / wuxia / anime variants all get a 1-2 sentence catalog blurb in each locale; brand and cultural tokens (Black Myth: Wukong, Hanfu, Showa, Pokémon, Three Kingdoms / 三国志, Lego, Disney, K-pop, HyperFrames) stay literal. Skipped: `live-action-anime-adaptation-water-vs-thunder-breathing-duel` returned malformed JSON on three retries; will rerun in followup. Falls back to the raw English fields per the reader landed in |
||
|---|---|---|
| .. | ||
| DESIGN.md | ||
| open-design.json | ||