fix(i18n): complete Portal German headings and contextual help
Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
+2
-1
@@ -14,7 +14,8 @@
|
||||
"./styles/portal.css": "./src/styles/portal.css"
|
||||
},
|
||||
"scripts": {
|
||||
"test:interface-pattern": "node scripts/test-interface-pattern.mjs"
|
||||
"test:interface-pattern": "node scripts/test-interface-pattern.mjs",
|
||||
"test:translations": "node --test scripts/test-translations.mjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { resolve } from "node:path";
|
||||
import { test } from "node:test";
|
||||
|
||||
const webui = resolve(import.meta.dirname, "..");
|
||||
const core = resolve(webui, "../../govoplan-core/webui");
|
||||
const require = createRequire(resolve(core, "package.json"));
|
||||
const { buildSync } = require("esbuild");
|
||||
const expected = {
|
||||
"My function postboxes": "Meine Funktionspostfächer",
|
||||
"Sign in to view status": "Anmelden, um den Status anzuzeigen",
|
||||
"Request a short-lived status link": "Kurzlebigen Statuslink anfordern",
|
||||
"Timeline": "Verlauf",
|
||||
};
|
||||
|
||||
// Exercise the real locale provider and shared Card contract. Compile in memory:
|
||||
// no server, shared build directory, optional module imports, or browser effects.
|
||||
const { outputFiles } = buildSync({
|
||||
stdin: {
|
||||
contents: `
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { generatedTranslations } from ${JSON.stringify(resolve(webui, "src/i18n/generatedTranslations.ts"))};
|
||||
import { PlatformLanguageProvider, usePlatformLanguage } from ${JSON.stringify(resolve(core, "src/i18n/LanguageContext.tsx"))};
|
||||
import Card from ${JSON.stringify(resolve(core, "src/components/Card.tsx"))};
|
||||
export const catalog = generatedTranslations;
|
||||
function NativeHeading({ label }) {
|
||||
const { translateText } = usePlatformLanguage();
|
||||
return <h2 id="portal-postboxes-heading">{translateText(label)}</h2>;
|
||||
}
|
||||
export function heading(language, label, native) {
|
||||
return renderToStaticMarkup(
|
||||
<PlatformLanguageProvider preferredLanguageCode={language} moduleTranslations={[catalog]}>
|
||||
{native ? <NativeHeading label={label} /> : <Card title={label}>Fixture</Card>}
|
||||
</PlatformLanguageProvider>
|
||||
);
|
||||
}
|
||||
`,
|
||||
loader: "tsx",
|
||||
resolveDir: core,
|
||||
},
|
||||
bundle: true,
|
||||
write: false,
|
||||
platform: "node",
|
||||
format: "cjs",
|
||||
jsx: "automatic",
|
||||
external: ["react", "react-dom/server"],
|
||||
logLevel: "silent",
|
||||
});
|
||||
const compiled = { exports: {} };
|
||||
new Function("require", "module", "exports", outputFiles[0].text)(require, compiled, compiled.exports);
|
||||
const { catalog, heading } = compiled.exports;
|
||||
|
||||
test("Portal registers and owns all four translations", () => {
|
||||
const module = readFileSync(resolve(webui, "src/module.ts"), "utf8");
|
||||
assert.match(module, /import\s*\{\s*generatedTranslations\s*\}\s*from\s*["']\.\/i18n\/generatedTranslations["']/);
|
||||
assert.match(module, /translations:\s*generatedTranslations/);
|
||||
for (const [english, german] of Object.entries(expected)) {
|
||||
assert.equal(catalog.en[english], english);
|
||||
assert.equal(catalog.de[english], german);
|
||||
}
|
||||
});
|
||||
|
||||
test("each heading renders in English and German without the DOM translation bridge", () => {
|
||||
for (const [english, german] of Object.entries(expected)) {
|
||||
for (const [language, translation] of [["en", english], ["de", german]]) {
|
||||
const native = english === "My function postboxes";
|
||||
const markup = heading(language, english, native);
|
||||
const tag = native ? '<h2 id="portal-postboxes-heading">' : "<h2>";
|
||||
assert.ok(markup.includes(`${tag}${translation}</h2>`), `${language}: ${english}`);
|
||||
if (language === "de") assert.ok(!markup.includes(`>${english}</h2>`));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("Portal pages keep the verified native-heading and shared-card translation paths", () => {
|
||||
const directory = readFileSync(resolve(webui, "src/features/portal/PortalPage.tsx"), "utf8");
|
||||
assert.match(directory, /const\s*\{\s*translateText\s*\}\s*=\s*usePlatformLanguage\(\)/);
|
||||
assert.ok(directory.includes('<h2 id="portal-postboxes-heading">{translateText("My function postboxes")}</h2>'));
|
||||
const status = readFileSync(resolve(webui, "src/features/portal/PortalStatusPage.tsx"), "utf8");
|
||||
for (const english of Object.keys(expected).slice(1)) {
|
||||
assert.ok(status.includes(`<Card title="${english}"`), `Shared card title: ${english}`);
|
||||
}
|
||||
});
|
||||
@@ -18,6 +18,7 @@ import { ActionBlockerHint,
|
||||
StatusBadge,
|
||||
ToggleSwitch,
|
||||
useGuardedNavigate,
|
||||
usePlatformLanguage,
|
||||
WorkspaceActionBar,
|
||||
WorkspaceFrame,
|
||||
type PlatformRouteContext
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
|
||||
export default function PortalPage({ settings }: PlatformRouteContext) {
|
||||
const navigate = useGuardedNavigate();
|
||||
const { translateText } = usePlatformLanguage();
|
||||
const [query, setQuery] = useState("");
|
||||
const [submittedQuery, setSubmittedQuery] = useState("");
|
||||
const [includeUnavailable, setIncludeUnavailable] = useState(true);
|
||||
@@ -155,7 +157,9 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
||||
onChange={setIncludeUnavailable}
|
||||
/>
|
||||
</>}
|
||||
helpAction={<DocumentationHelpLink
|
||||
title="Service directory"
|
||||
titleLevel={1}
|
||||
titleHelp={<DocumentationHelpLink
|
||||
reference={{ topicId: "portal.service-directory", documentationType: "user" }}
|
||||
label="Open service directory documentation"
|
||||
/>}
|
||||
@@ -177,7 +181,7 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
||||
<section className="portal-postboxes" aria-labelledby="portal-postboxes-heading">
|
||||
<div className="portal-section-heading">
|
||||
<Inbox size={18} aria-hidden="true" />
|
||||
<h2 id="portal-postboxes-heading">My function postboxes</h2>
|
||||
<h2 id="portal-postboxes-heading">{translateText("My function postboxes")}</h2>
|
||||
</div>
|
||||
<div className="portal-postbox-list">
|
||||
{postboxes.map((entry) => (
|
||||
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
import type { PlatformTranslations } from "@govoplan/core-webui";
|
||||
|
||||
/** Module-owned translations for contextual headings. */
|
||||
export const generatedTranslations: PlatformTranslations = {
|
||||
en: {
|
||||
"Service directory": "Service directory",
|
||||
"My function postboxes": "My function postboxes",
|
||||
"Sign in to view status": "Sign in to view status",
|
||||
"Request a short-lived status link": "Request a short-lived status link",
|
||||
"Timeline": "Timeline",
|
||||
},
|
||||
de: {
|
||||
"Service directory": "Leistungsverzeichnis",
|
||||
"My function postboxes": "Meine Funktionspostfächer",
|
||||
"Sign in to view status": "Anmelden, um den Status anzuzeigen",
|
||||
"Request a short-lived status link": "Kurzlebigen Statuslink anfordern",
|
||||
"Timeline": "Verlauf",
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createElement, lazy } from "react";
|
||||
import type { PlatformWebModule } from "@govoplan/core-webui";
|
||||
import { generatedTranslations } from "./i18n/generatedTranslations";
|
||||
import "./styles/portal.css";
|
||||
|
||||
|
||||
@@ -7,6 +8,7 @@ const PortalPage = lazy(() => import("./features/portal/PortalPage"));
|
||||
const PortalStatusPage = lazy(() => import("./features/portal/PortalStatusPage"));
|
||||
|
||||
export const portalModule: PlatformWebModule = {
|
||||
translations: generatedTranslations,
|
||||
id: "portal",
|
||||
label: "Services",
|
||||
version: "0.1.19",
|
||||
|
||||
Reference in New Issue
Block a user