Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
2e996c4
docs: add admin UI i18n POC design spec
ophirbucai Apr 3, 2026
c8b48e7
docs: add admin i18n POC implementation plan
ophirbucai Apr 3, 2026
23b66b2
feat(admin): add i18n types, provider, and useTranslation hook
ophirbucai Apr 3, 2026
a369f1d
feat(admin): add English and French common translation files
ophirbucai Apr 3, 2026
778da33
feat(admin): export i18n module and locale JSON files
ophirbucai Apr 3, 2026
2d77d75
feat(core): add locale resolution to admin shell route
ophirbucai Apr 3, 2026
a8aa7dd
feat: wire i18n provider through PluginRegistry and AdminApp
ophirbucai Apr 3, 2026
f8775b4
feat(admin): add locale selector to header user dropdown
ophirbucai Apr 3, 2026
d0dc0a0
feat(admin): replace hardcoded strings with t() in ConfirmDialog and …
ophirbucai Apr 3, 2026
72d69e3
feat(admin): add locale selector to login page
ophirbucai Apr 3, 2026
ed36df2
feat(admin): add language selector to settings hub
ophirbucai Apr 3, 2026
80e5c2f
refactor(admin): client-side locale switching without page reload
ophirbucai Apr 4, 2026
cef9a28
feat(admin): add nav and settings translation namespaces
ophirbucai Apr 4, 2026
df598d1
chore: format spec and plan docs
ophirbucai Apr 4, 2026
5d02a35
refactor(admin): centralize i18n config and use import.meta.glob
ophirbucai Apr 4, 2026
398c9a3
docs: add adding-admin-locale skill
ophirbucai Apr 4, 2026
ecc7d3a
feat(admin): type-safe i18n translation keys
ophirbucai Apr 4, 2026
d527bf3
docs: update adding-admin-locale skill for barrel-derived types
ophirbucai Apr 4, 2026
fead644
refactor(admin): remove namespace prefix from translation keys
ophirbucai Apr 4, 2026
b4f8bd1
feat(admin): migrate i18n from custom system to Lingui
ophirbucai Apr 4, 2026
d13753e
docs: rewrite adding-admin-locale skill for Lingui
ophirbucai Apr 4, 2026
0e0c938
chore: remove French locale files and simplify config
ophirbucai Apr 4, 2026
9602267
chore(deps): remove an unused package and restore previously existing…
ophirbucai Apr 4, 2026
32524a0
chore(admin): i18n infra — ci check, cleanup, review fixes
ophirbucai Apr 4, 2026
953f2a6
feat(admin): wrap all admin components with Lingui macros
ophirbucai Apr 4, 2026
0566c0c
chore(admin): sync catalog after plural macro conversion
ophirbucai Apr 4, 2026
cde0e3b
refactor(admin): type BlockMenu transform labels with useMemo
ophirbucai Apr 4, 2026
6f1dc5c
chore(admin): rename i18n scripts to locale:extract/locale:check
ophirbucai Apr 4, 2026
1ce2cc2
fix(admin): don't translate email placeholder in InviteUserModal
ophirbucai Apr 4, 2026
07707e4
refactor(admin): remove role translation hook — role names are universal
ophirbucai Apr 4, 2026
cd5e9af
chore: format and fix skill JSX prettier issue
ophirbucai Apr 4, 2026
4273480
style: move React import to top of PluginRegistry
ophirbucai Apr 4, 2026
3b69e7e
chore: add lingui packages to catalog, fix core type deps
ophirbucai Apr 4, 2026
f16df46
revert: remove @lingui/core peer dep and @types/react from core
ophirbucai Apr 4, 2026
964b2b3
feat(admin): wrap PluginBlockNode and PasskeyItem with Lingui macros
ophirbucai Apr 4, 2026
d804c8d
fix(admin): use useLayoutEffect for i18n initialization
ophirbucai Apr 4, 2026
20d42d8
fix(admin): harden BlockTransformId type with as const
ophirbucai Apr 4, 2026
0411fde
fix(admin): use Plural macro and extract nested t`` in i18n strings
ophirbucai Apr 4, 2026
5026a47
revert(admin): restore inline nested t`` in WordPressImport
ophirbucai Apr 4, 2026
baf2d11
fix(admin): use catalog for demo lingui deps, restore untranslated roles
ophirbucai Apr 4, 2026
d56d32a
fix(admin): match SetupWizard continue button to main branch
ophirbucai Apr 4, 2026
b6cf647
fix(admin): add Lingui Babel plugin to vitest config for component tests
ophirbucai Apr 4, 2026
5d098c2
fix(admin): inline translatable data into useMemo for Lingui macros
ophirbucai Apr 4, 2026
a394c32
fix(admin): restore three-dot ellipsis, escape ICU braces in i18n str…
ophirbucai Apr 4, 2026
62cb801
fix(admin): add I18nProvider test wrapper for Lingui component tests
ophirbucai Apr 4, 2026
9ceab5d
fix(admin): update test imports to use I18nProvider-wrapped render
ophirbucai Apr 4, 2026
0f457da
chore(admin): sync .po catalog after useMemo migration and fixes
ophirbucai Apr 4, 2026
5b7593b
docs: add lingui-macro-patterns skill with codebase conventions
ophirbucai Apr 5, 2026
1aeaa39
fix(admin): use ref-based i18n init to avoid null render warning
ophirbucai Apr 5, 2026
06f2d93
fix(e2e): add Lingui Vite and Babel plugins to e2e fixture
ophirbucai Apr 5, 2026
686cadc
feat(admin): wrap aria-label attributes with Lingui t for accessibili…
ophirbucai Apr 5, 2026
00d3a54
fix(admin): inline command palette nav items into useNavItems hook
ophirbucai Apr 6, 2026
ac7b408
fix(e2e): update upload test to match Lingui plural format
ophirbucai Apr 6, 2026
f00690e
style: format
emdashbot[bot] Apr 6, 2026
d372fac
fix(admin): restore singular "File uploaded" message to match main
ophirbucai Apr 6, 2026
e5b8e8d
fix(e2e): restore exact upload test assertion to match main
ophirbucai Apr 6, 2026
e868357
feat(admin): pre-compile .po catalogs to JS, remove Vite plugin requi…
ophirbucai Apr 6, 2026
9667c37
feat(admin): conditional .po/.mjs import based on dev/prod mode
ophirbucai Apr 6, 2026
b392e21
chore(demo): restore Lingui Vite plugin for dev mode .po compilation
ophirbucai Apr 6, 2026
8739854
docs: update adding-admin-locale skill for dev/prod catalog workflow
ophirbucai Apr 6, 2026
bb67b25
fix(e2e): add Lingui Vite plugin for dev-mode .po imports
ophirbucai Apr 6, 2026
cafa894
refactor(admin): clean up test render wrapper with Fragment default
ophirbucai Apr 7, 2026
076ab57
fix: import pre-compiled .mjs catalogs everywhere, remove Vite plugin
ophirbucai Apr 7, 2026
d302589
fix(e2e): remove leftover Lingui Vite plugin from e2e fixture
ophirbucai Apr 7, 2026
eb302a7
fix(demos): add Babel macro plugin to all demos for Lingui support
ophirbucai Apr 7, 2026
e6b0c2f
feat(core): inject Lingui Babel macro plugin in dev mode
ophirbucai Apr 8, 2026
c08c56f
docs(skill): update adding-admin-locale with macro compilation archit…
ophirbucai Apr 8, 2026
72032a9
style: format
emdashbot[bot] Apr 8, 2026
35a14e8
Merge branch 'main' into feat/admin-i18n-lingui
ascorbic Apr 8, 2026
a397265
Update lockfile
ascorbic Apr 8, 2026
2240079
fix(core): externalize @babel/core from tsdown build
ophirbucai Apr 8, 2026
97adcbe
fix: remove orphaned @lingui/babel-plugin-lingui-macro from demos and…
ophirbucai Apr 8, 2026
61dcdc8
Merge branch 'main' into feat/admin-i18n-lingui
ophirbucai Apr 8, 2026
a650af5
fix(core): use import.meta.glob for locale catalog loading
ophirbucai Apr 8, 2026
8d57ae6
Revert "fix(core): use import.meta.glob for locale catalog loading"
ophirbucai Apr 8, 2026
ef9b594
chore(skills): fix skill.md casing, add compile note for translations
ophirbucai Apr 8, 2026
b454018
fix(skill): correct .mjs commit claim — files are gitignored
ophirbucai Apr 8, 2026
856bc30
fix(admin): copy locale catalogs to dist, fix export path and scripts
ophirbucai Apr 8, 2026
304a01c
Merge branch 'main' into feat/admin-i18n-lingui
ophirbucai Apr 8, 2026
ee5d299
fix(admin): wrap missed aria-label and placeholder strings with t macro
ophirbucai Apr 8, 2026
07e8af0
docs(skill): remove misleading commit instruction from locale workflow
ophirbucai Apr 8, 2026
b66e7ce
refactor: strip to infrastructure-only for PR split
ophirbucai Apr 8, 2026
bf8cf11
Merge branch 'main' into feat/admin-i18n-lingui
ophirbucai Apr 8, 2026
7cb1831
refactor: consolidate locale label lookup into config.ts
ophirbucai Apr 8, 2026
a5d5e3d
refactor: extract locale:copy to ./scripts/copy-locales.js
ophirbucai Apr 8, 2026
45c1865
fix: rename linguiBabelPlugin to linguiMacroPlugin for clarity
ophirbucai Apr 8, 2026
2ae5e6e
refactor: remove source aliasing and Babel from published core package
ophirbucai Apr 9, 2026
49f4501
style: format
emdashbot[bot] Apr 9, 2026
33594c1
revert: restore integration files to upstream main state
ophirbucai Apr 9, 2026
634b7d3
Revert "revert: restore integration files to upstream main state"
ophirbucai Apr 9, 2026
de5fe81
fix: subscribe to i18n change events in useLocale for reactive locale…
ophirbucai Apr 9, 2026
460ef2f
feat: dev-only source aliasing with dynamic babel macro transform
ophirbucai Apr 9, 2026
1a037c5
fix: restore uppercase locale codes in content locale switcher
ophirbucai Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lingui.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { LinguiConfig } from "@lingui/conf";

const config: LinguiConfig = {
sourceLocale: "en",
locales: ["en"],
catalogs: [
{
path: "<rootDir>/packages/admin/src/locales/{locale}/messages",
include: ["<rootDir>/packages/admin/src/**/*.{ts,tsx}"],
},
],
format: "po",
};

export default config;
20 changes: 17 additions & 3 deletions packages/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./styles.css": "./dist/styles.css"
"./styles.css": "./dist/styles.css",
"./locales": {
"types": "./dist/locales/index.d.ts",
"default": "./dist/locales/index.js"
},
"./locales/*": "./dist/locales/*"
},
"scripts": {
"build": "tsdown && npx @tailwindcss/cli -i src/styles.css -o dist/styles.css --minify",
"build": "node --run locale:compile && tsdown && node --run locale:copy && npx @tailwindcss/cli -i src/styles.css -o dist/styles.css --minify",
"dev": "tsdown src/index.ts --format esm --dts --watch",
"prepublishOnly": "node --run build",
"check": "publint && attw --pack --ignore-rules=cjs-resolves-to-esm --ignore-rules=no-resolution",
"test": "vitest",
"typecheck": "tsgo --noEmit"
"typecheck": "tsgo --noEmit",
"locale:compile": "lingui compile --namespace es",
"locale:copy": "node ./scripts/copy-locales.js",
"locale:extract": "lingui extract --clean"
},
"dependencies": {
"@cloudflare/kumo": "^1.16.0",
Expand All @@ -29,6 +37,8 @@
"@dnd-kit/utilities": "^3.2.2",
"@emdash-cms/blocks": "workspace:*",
"@floating-ui/react": "^0.27.16",
"@lingui/core": "catalog:",
"@lingui/react": "catalog:",
"@phosphor-icons/react": "catalog:",
"@tanstack/react-query": "catalog:",
"@tanstack/react-router": "catalog:",
Expand Down Expand Up @@ -58,6 +68,10 @@
},
"devDependencies": {
"@arethetypeswrong/cli": "catalog:",
"@babel/core": "^7.29.0",
"@lingui/babel-plugin-lingui-macro": "catalog:",
"@lingui/cli": "catalog:",
"@lingui/macro": "catalog:",
"@tailwindcss/cli": "^4.1.10",
"@tailwindcss/typography": "^0.5.19",
"@testing-library/react": "^16.3.0",
Expand Down
18 changes: 18 additions & 0 deletions packages/admin/scripts/copy-locales.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copy compiled locale catalogs (.mjs) from src/locales to dist/locales.
* Run after `lingui compile` to include catalogs in the published package.
*/
import { readdirSync, mkdirSync, copyFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const srcDir = join(__dirname, "..", "src", "locales");
const distDir = join(__dirname, "..", "dist", "locales");

for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const destDir = join(distDir, entry.name);
mkdirSync(destDir, { recursive: true });
copyFileSync(join(srcDir, entry.name, "messages.mjs"), join(destDir, "messages.mjs"));
}
35 changes: 27 additions & 8 deletions packages/admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
*/

import { Toasty } from "@cloudflare/kumo";
import { i18n } from "@lingui/core";
import type { Messages } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider } from "@tanstack/react-router";
import * as React from "react";
Expand All @@ -33,27 +36,43 @@ const router = createAdminRouter(queryClient);
export interface AdminAppProps {
/** Plugin admin modules keyed by plugin ID */
pluginAdmins?: PluginAdmins;
/** Active locale code */
locale?: string;
/** Compiled Lingui messages for the active locale */
messages?: Messages;
}

/**
* Main Admin Application
*/
const EMPTY_PLUGINS: PluginAdmins = {};

export function AdminApp({ pluginAdmins = EMPTY_PLUGINS }: AdminAppProps) {
export function AdminApp({
pluginAdmins = EMPTY_PLUGINS,
locale = "en",
messages = {},
}: AdminAppProps) {
React.useEffect(() => {
document.getElementById("emdash-boot-loader")?.remove();
}, []);

const i18nInitialized = React.useRef(false);
if (!i18nInitialized.current) {
i18n.loadAndActivate({ locale, messages });
i18nInitialized.current = true;
}

return (
<ThemeProvider>
<Toasty>
<PluginAdminProvider pluginAdmins={pluginAdmins}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</PluginAdminProvider>
</Toasty>
<I18nProvider i18n={i18n}>
<Toasty>
<PluginAdminProvider pluginAdmins={pluginAdmins}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</PluginAdminProvider>
</Toasty>
</I18nProvider>
</ThemeProvider>
);
}
Expand Down
16 changes: 8 additions & 8 deletions packages/admin/src/components/LocaleSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Only renders when i18n is configured (manifest.i18n is present).
*/

import { useLingui } from "@lingui/react/macro";
import { GlobeSimple } from "@phosphor-icons/react";
import React from "react";

Expand Down Expand Up @@ -46,6 +47,7 @@ export function LocaleSwitcher({
className,
size = "md",
}: LocaleSwitcherProps) {
const { t } = useLingui();
return (
<div className={cn("flex items-center gap-1.5", className)}>
<GlobeSimple
Expand All @@ -55,19 +57,19 @@ export function LocaleSwitcher({
<select
value={value}
onChange={(e) => onChange(e.target.value)}
aria-label="Locale"
aria-label={t`Locale`}
className={cn(
"rounded-md border bg-transparent font-medium transition-colors",
"focus:ring-kumo-ring focus:outline-none focus:ring-2 focus:ring-offset-1",
"hover:bg-kumo-tint/50 cursor-pointer",
size === "sm" ? "px-1.5 py-0.5 text-xs" : "px-2 py-1 text-sm",
)}
>
{showAll && <option value="">All locales</option>}
{showAll && <option value="">{t`All locales`}</option>}
{locales.map((locale) => (
<option key={locale} value={locale}>
{locale.toUpperCase()}
{locale === defaultLocale ? " (default)" : ""}
{locale === defaultLocale ? t` (default)` : ""}
</option>
))}
</select>
Expand All @@ -88,23 +90,21 @@ export function LocaleBadges({
existingLocales: string[];
onLocaleClick?: (locale: string) => void;
}) {
const { t } = useLingui();
const existingSet = new Set(existingLocales);

return (
<div className="flex items-center gap-0.5">
{locales.map((locale) => {
const exists = existingSet.has(locale);
const label = getLocaleLabel(locale);
return (
<button
key={locale}
type="button"
onClick={() => onLocaleClick?.(locale)}
disabled={!onLocaleClick}
title={
exists
? `${getLocaleLabel(locale)} \u2014 view translation`
: `${getLocaleLabel(locale)} \u2014 no translation`
}
title={exists ? t`${label} \u2014 view translation` : t`${label} \u2014 no translation`}
className={cn(
"rounded px-1 py-0.5 text-[10px] font-semibold uppercase leading-none transition-colors",
exists
Expand Down
Loading
Loading