Skip to content

Add Lunaria translation tracking and i18n dashboard#461

Merged
ascorbic merged 13 commits into
mainfrom
feat/lunaria-translations
Apr 11, 2026
Merged

Add Lunaria translation tracking and i18n dashboard#461
ascorbic merged 13 commits into
mainfrom
feat/lunaria-translations

Conversation

@ascorbic
Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds translation infrastructure for the admin UI using Lunaria with PO dictionary support (via lunariajs/lunaria#178).

  • Lunaria config (lunaria.config.ts) — tracks PO catalogs with dictionary mode, detecting per-key completion
  • Spanish locale — first translation target, extracted via Lingui with 40 strings
  • i18n dashboard (i18n/) — static HTML dashboard showing per-locale progress bars, missing keys, and GitHub edit links. Deployable to i18n.emdashcms.com via Cloudflare Workers
  • GitHub Action — comments on PRs with translation impact summary (uses pull_request_target for fork PRs)
  • Root scriptslocale:extract and locale:compile delegate to the admin package

Type of change

  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)

AI-generated code disclosure

  • This PR includes AI-generated code

Configure Lunaria to track PO files directly using dictionary mode
(via lunariajs/lunaria#178). Add Spanish as first translation locale.

- Add lunaria.config.json with PO dictionary tracking
- Add Spanish locale to Lingui config and extract empty catalog
- Add locale:extract and locale:compile scripts to root
Static HTML dashboard generated from Lunaria, deployed as Cloudflare
Workers static assets. Shows per-locale completion with progress bars,
missing keys, and GitHub edit links.
Copilot AI review requested due to automatic review settings April 11, 2026 19:08
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 11, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
emdash-playground 8f324d1 Apr 11 2026, 07:24 PM

@github-actions
Copy link
Copy Markdown
Contributor

Scope check

This PR changes 688 lines across 11 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 11, 2026

⚠️ No Changeset found

Latest commit: 8f324d1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 11, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@461

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@461

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@461

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@461

emdash

npm i https://pkg.pr.new/emdash@461

create-emdash

npm i https://pkg.pr.new/create-emdash@461

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@461

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@461

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@461

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@461

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@461

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@461

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@461

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@461

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@461

commit: 8f324d1

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds translation tracking for the admin UI using Lunaria (PO “dictionary” mode) and introduces a deployable static i18n status dashboard, plus supporting workspace/scripts and a PR-commenting GitHub Action.

Changes:

  • Add Lunaria configuration + GitHub Action workflow for translation-overview reporting.
  • Add Spanish (es) PO catalog support and expand Lingui locale configuration.
  • Add a new i18n/ workspace package that generates & deploys a static translation-progress dashboard.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds i18n/ as a workspace package.
pnpm-lock.yaml Locks new Lunaria dependency graph and related resolution changes.
package.json Adds Lunaria core dependency + root locale scripts delegating to admin.
lunaria.config.ts Defines Lunaria repo/source locale/locales and tracked PO file patterns.
lingui.config.ts Adds es to supported locales for PO extraction/compilation.
packages/admin/src/locales/es/messages.po Adds initial Spanish PO catalog (currently untranslated entries).
i18n/package.json Defines dashboard build/deploy scripts.
i18n/build.ts Generates i18n/dist/index.html + status.json from Lunaria status.
i18n/wrangler.jsonc Wrangler config to deploy the dashboard to i18n.emdashcms.com.
i18n/.gitignore Ignores generated dist/ artifacts.
.github/workflows/lunaria.yml Adds PR-target workflow to run Lunaria overview action.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread i18n/build.ts
Comment on lines +84 to +94
function localeCard(s: LocaleStatus): string {
return `
<details class="locale">
<summary>
<strong>${s.label} <span class="lang">${s.lang}</span></strong>
<span class="stats">${s.completedKeys}/${s.totalKeys} · ${s.percentComplete}%</span>
<div class="bar"><div class="fill ${barClass(s.percentComplete)}" style="width:${s.percentComplete}%"></div></div>
</summary>
<div class="links">
<a href="${s.editUrl}">Edit translation</a>
${s.historyUrl ? `· <a href="${s.historyUrl}">History</a>` : ""}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated HTML interpolates dynamic values into markup/attributes without escaping (e.g. locale label/lang in text and editUrl/historyUrl inside href). If any of these contain characters like <, &, or ", the dashboard HTML can break and could become an injection vector. Use HTML escaping for all inserted text, and an attribute-safe escape for href values (or build DOM with URL objects + encodeURIComponent for paths) before writing index.html.

Suggested change
function localeCard(s: LocaleStatus): string {
return `
<details class="locale">
<summary>
<strong>${s.label} <span class="lang">${s.lang}</span></strong>
<span class="stats">${s.completedKeys}/${s.totalKeys} · ${s.percentComplete}%</span>
<div class="bar"><div class="fill ${barClass(s.percentComplete)}" style="width:${s.percentComplete}%"></div></div>
</summary>
<div class="links">
<a href="${s.editUrl}">Edit translation</a>
${s.historyUrl ? `· <a href="${s.historyUrl}">History</a>` : ""}
function escapeHtmlAttribute(s: string): string {
return escapeHtml(s);
}
function localeCard(s: LocaleStatus): string {
return `
<details class="locale">
<summary>
<strong>${escapeHtml(s.label)} <span class="lang">${escapeHtml(s.lang)}</span></strong>
<span class="stats">${s.completedKeys}/${s.totalKeys} · ${s.percentComplete}%</span>
<div class="bar"><div class="fill ${barClass(s.percentComplete)}" style="width:${s.percentComplete}%"></div></div>
</summary>
<div class="links">
<a href="${escapeHtmlAttribute(s.editUrl)}">Edit translation</a>
${s.historyUrl ? `· <a href="${escapeHtmlAttribute(s.historyUrl)}">History</a>` : ""}

Copilot uses AI. Check for mistakes.
Comment thread i18n/package.json Outdated
"build": "cd .. && node --experimental-strip-types i18n/build.ts",
"deploy": "node --run build && wrangler deploy"
},
"dependencies": {
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package lists @types/node under dependencies, but it's a types-only dev-time dependency. Keeping it in dependencies can unnecessarily bloat installs and is inconsistent with other workspace packages (which place @types/node in devDependencies). Move @types/node to devDependencies here.

Suggested change
"dependencies": {
"devDependencies": {

Copilot uses AI. Check for mistakes.
Comment thread package.json
"@changesets/changelog-github": "^0.5.2",
"@changesets/cli": "^2.29.8",
"@e18e/eslint-plugin": "^0.2.0",
"@lunariajs/core": "https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@83617cc",
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunariajs/core is being pulled from pkg.pr.new (a PR tarball URL). This can reduce reproducibility and can break installs if the tarball is GC’d or the service is unavailable. Prefer a published npm version (or a pinned git URL/commit in a registry you control) once the upstream change is released, and document the rationale if this must stay temporary.

Suggested change
"@lunariajs/core": "https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@83617cc",
"@lunariajs/core": "REPLACE_WITH_EXACT_PUBLISHED_NPM_VERSION",

Copilot uses AI. Check for mistakes.
Comment thread pnpm-lock.yaml Outdated
vitest:
specifier: 'catalog:'
version: 4.0.18(@types/node@24.10.13)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)
version: 4.0.18(@types/node@24.10.13)(@vitest/browser-playwright@4.0.18)(@vitest/ui@4.0.17)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lockfile now records vitest@4.0.18 being resolved with @vitest/ui@4.0.17. Since Vitest typically expects the UI package to match its own patch version, this can cause pnpm peer-dependency warnings and potentially break vitest --ui. Consider aligning @vitest/ui to the same version as vitest (or removing it if it's not needed) to keep the dependency graph consistent.

Suggested change
version: 4.0.18(@types/node@24.10.13)(@vitest/browser-playwright@4.0.18)(@vitest/ui@4.0.17)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)
version: 4.0.18(@types/node@24.10.13)(@vitest/browser-playwright@4.0.18)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)

Copilot uses AI. Check for mistakes.
Comment thread i18n/package.json Outdated
"deploy": "node --run build && wrangler deploy"
},
"dependencies": {
"@types/node": "catalog:"
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deploy script calls wrangler deploy, but this package doesn’t declare wrangler in dependencies/devDependencies. In other workspace packages, wrangler is explicitly listed so scripts work in isolation; consider adding it here (likely as a devDependency) to avoid relying on incidental hoisting from other workspaces.

Suggested change
"@types/node": "catalog:"
"@types/node": "catalog:"
},
"devDependencies": {
"wrangler": "^4.11.1"

Copilot uses AI. Check for mistakes.
@ascorbic ascorbic enabled auto-merge (squash) April 11, 2026 19:21
@ascorbic ascorbic merged commit cde3834 into main Apr 11, 2026
27 checks passed
@ascorbic ascorbic deleted the feat/lunaria-translations branch April 11, 2026 19:31
fmhall pushed a commit to fmhall/emdash that referenced this pull request Apr 13, 2026
* Add Lunaria translation tracking with PO dictionary support

Configure Lunaria to track PO files directly using dictionary mode
(via lunariajs/lunaria#178). Add Spanish as first translation locale.

- Add lunaria.config.json with PO dictionary tracking
- Add Spanish locale to Lingui config and extract empty catalog
- Add locale:extract and locale:compile scripts to root

* Switch Lunaria config to TypeScript with defineConfig

* Fix Lunaria config: include only source file, not all PO files

* Add translation status dashboard deployable to i18n.emdashcms.com

Static HTML dashboard generated from Lunaria, deployed as Cloudflare
Workers static assets. Shows per-locale completion with progress bars,
missing keys, and GitHub edit links.

* Move Lunaria build/deploy scripts into i18n/ package

* Add Lunaria GitHub Action for PR translation impact comments

* Add i18n workspace package, update lockfile

* style: format

* Replace Spanish with German as first translation locale

* fix ts

* style: format

* Add wrangler

---------

Co-authored-by: emdashbot[bot] <emdashbot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants