From bcb0f4a146ace99f99dc6943aef38c335eca8631 Mon Sep 17 00:00:00 2001 From: "John R. D'Orazio" Date: Fri, 1 May 2026 17:31:54 +0200 Subject: [PATCH] fix(i18n): drop doubled locale prefix in Governance TOC links (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GovernanceTOC was passing Polylang's locale-prefixed `uri` directly to next-intl's ``, which prepended the locale again — Italian links became `/it/it/governance-2/governanza-del-progetto`. Change `getChildPages` to derive the EN canonical slug from `translations` (querying WPGraphQL+Polylang once) and pass it through to GovernanceTOC, which now composes the URL from the parent's path plus the EN slug. next-intl adds the locale prefix exactly once, matching what Header.tsx already does. URLs continue to use EN slugs site-wide; consuming the locale's own translated slugs is tracked separately in #55. Drive-by: drop unused `page` parameter on `renderBlog` to clear a TS6133 LSP warning. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/[lang]/[[...slug]]/page.tsx | 1 + components/sections/GovernanceTOC.tsx | 8 ++++++-- components/sections/PageRenderer.tsx | 12 +++++++----- lib/wordpress/api.ts | 18 +++++++++++++++--- lib/wordpress/queries.ts | 5 ++++- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/app/[lang]/[[...slug]]/page.tsx b/app/[lang]/[[...slug]]/page.tsx index 6bda14c..b8f6270 100644 --- a/app/[lang]/[[...slug]]/page.tsx +++ b/app/[lang]/[[...slug]]/page.tsx @@ -63,6 +63,7 @@ export default async function CatchAllPage({ params }: PageProps) { isLogoSymbolism={isLogoSymbolism} fishExplanationHtml={fishExplanation?.content ?? undefined} childPages={childPages} + parentPath={pageSlug} /> ) } diff --git a/components/sections/GovernanceTOC.tsx b/components/sections/GovernanceTOC.tsx index b924e10..8887f0a 100644 --- a/components/sections/GovernanceTOC.tsx +++ b/components/sections/GovernanceTOC.tsx @@ -3,17 +3,21 @@ import type { WPChildPage } from '@/lib/wordpress/api' interface GovernanceTOCProps { pages: WPChildPage[] + parentPath: string heading?: string description?: string } export default function GovernanceTOC({ pages, + parentPath, heading, description, }: GovernanceTOCProps) { if (pages.length === 0) return null + const base = parentPath && parentPath !== '/' ? `/${parentPath}` : '' + return (
@@ -33,8 +37,8 @@ export default function GovernanceTOC({
{pages.map((page) => (

diff --git a/components/sections/PageRenderer.tsx b/components/sections/PageRenderer.tsx index b8a38ff..1fffb3e 100644 --- a/components/sections/PageRenderer.tsx +++ b/components/sections/PageRenderer.tsx @@ -24,6 +24,7 @@ interface PageRendererProps { isLogoSymbolism?: boolean fishExplanationHtml?: string childPages?: WPChildPage[] + parentPath?: string } export default async function PageRenderer({ @@ -34,6 +35,7 @@ export default async function PageRenderer({ isLogoSymbolism, fishExplanationHtml, childPages = [], + parentPath = '/', }: PageRendererProps) { const template = page.template?.templateName || 'Default' const t = await getTranslations('about') @@ -52,9 +54,9 @@ export default async function PageRenderer({ {template === 'About' && renderAbout(page, t)} {template === 'Projects' && renderProjects(page, projects, await getTranslations('projects'))} {template === 'Community' && renderCommunity(page, await getTranslations('community'))} - {template === 'Blog' && renderBlog(page, posts)} + {template === 'Blog' && renderBlog(posts)} {template === 'Contact' && renderContact(page)} - {template === 'Governance TOC' && renderGovernanceTOC(page, childPages)} + {template === 'Governance TOC' && renderGovernanceTOC(page, childPages, parentPath)} {template === 'Default' && renderDefault(page, isLogoSymbolism, fishExplanationHtml)} {/* CTA — shared across templates that use it */} @@ -181,7 +183,7 @@ function renderCommunity(page: WPPage, t: (key: string) => string) { ) } -function renderBlog(page: WPPage, posts: WPPost[]) { +function renderBlog(posts: WPPost[]) { return } @@ -199,13 +201,13 @@ function renderContact(page: WPPage) { ) } -function renderGovernanceTOC(page: WPPage, childPages: WPChildPage[]) { +function renderGovernanceTOC(page: WPPage, childPages: WPChildPage[], parentPath: string) { return ( <> {page.content && ( )} - + ) } diff --git a/lib/wordpress/api.ts b/lib/wordpress/api.ts index cd750f9..5142c76 100644 --- a/lib/wordpress/api.ts +++ b/lib/wordpress/api.ts @@ -168,10 +168,16 @@ export async function getAllPages( } export interface WPChildPage { + title: string + enSlug: string + modified: string +} + +interface RawChildPage { title: string slug: string - uri: string modified: string + translations?: { language: { code: string }; slug: string }[] } export async function getChildPages( @@ -181,10 +187,16 @@ export async function getChildPages( ): Promise { try { const data = await wpQuery<{ - pages: { nodes: WPChildPage[] } + pages: { nodes: RawChildPage[] } }>(GET_CHILD_PAGES, { parentId: parentDatabaseId, language: langCode(locale) }, options) - return data.pages.nodes + return data.pages.nodes.map((node) => { + const enSlug = + locale === 'en' + ? node.slug + : node.translations?.find((t) => t.language.code === 'EN')?.slug ?? node.slug + return { title: node.title, enSlug, modified: node.modified } + }) } catch (error) { console.error('Failed to fetch child pages:', error) return [] diff --git a/lib/wordpress/queries.ts b/lib/wordpress/queries.ts index aec218a..0becb6a 100644 --- a/lib/wordpress/queries.ts +++ b/lib/wordpress/queries.ts @@ -406,8 +406,11 @@ export const GET_CHILD_PAGES = ` nodes { title slug - uri modified + translations { + language { code } + slug + } } } }