Files
ESPDocs/components/PageHero.jsx
T
Lucas RayanandAntigravity 7fce46b9e1
CI / Lint e auditorias (push) Successful in 49s
CI / Trivy (dependências e segredos) (push) Successful in 11s
CI / Imagem Docker (linux/arm64) (push) Successful in 3m14s
feat(i18n): French and German localization (phase 5)
- Add full message catalogs and content for French (fr) and German (de)
- Implement ASCII canonical slugs for French and German routes
- Add language-specific search endpoints (/api/busca/fr, /api/busca/de)
- Configure strict 404 responses for /fr/livro and /de/livro
- Localize SEO, OpenGraph (fr_FR, de_DE), Twitter and JSON-LD schemas
- Add localized code example comments across 14 examples for fr and de
- Enable 'fr' and 'de' in ENABLED_LOCALES with root negotiation and redirect
- Extend test suite in check-i18n, smoke-imagem and verifica-vazamentos

Co-Authored-By: Antigravity <noreply@google.com>
2026-11-01 22:06:36 -03:00

98 lines
2.9 KiB
React

import Link from "next/link";
/**
* Abertura padrão das páginas internas: trilha, rótulo, título, frase de apoio
* e ações. Mesma voz da /livro e da home, para que nenhuma página pareça
* de outro produto.
*/
export default function PageHero({
trail = [],
icon: Icon,
label,
title,
lede,
align = "left",
children,
isEn: isEnProp,
isEs: isEsProp,
isFr: isFrProp,
isDe: isDeProp,
locale: localeProp,
}) {
const center = align === "center";
const isEn =
isEnProp !== undefined
? isEnProp
: localeProp
? localeProp === "en"
: trail.some((t) => t.isEn || (t.href && t.href.startsWith("/en")));
const isEs =
isEsProp !== undefined
? isEsProp
: localeProp
? localeProp === "es"
: trail.some((t) => t.isEs || (t.href && t.href.startsWith("/es")));
const isFr =
isFrProp !== undefined
? isFrProp
: localeProp
? localeProp === "fr"
: trail.some((t) => t.isFr || (t.href && t.href.startsWith("/fr")));
const isDe =
isDeProp !== undefined
? isDeProp
: localeProp
? localeProp === "de"
: trail.some((t) => t.isDe || (t.href && t.href.startsWith("/de")));
const homeHref = isEn ? "/en" : isEs ? "/es" : isFr ? "/fr" : isDe ? "/de" : "/";
const homeLabel = isEn ? "Home" : isEs ? "Inicio" : isFr ? "Accueil" : isDe ? "Startseite" : "Início";
const breadcrumbAria = isEn
? "Breadcrumb navigation"
: isEs
? "Navegación por migas de pan"
: isFr
? "Fil d'Ariane"
: isDe
? "Brotkrumennavigation"
: "Trilha de navegação";
return (
<section className="shell pt-8 sm:pt-12">
{trail.length > 0 && (
<nav aria-label={breadcrumbAria} className="flex flex-wrap items-center gap-2 text-sm text-muted">
<Link href={homeHref} className="inline-flex min-h-11 min-w-11 items-center justify-center transition-colors hover:text-accent">
{homeLabel}
</Link>
{trail.map((item, i) => (
<span key={item.label} className="flex min-h-11 items-center gap-2">
<span aria-hidden="true" className="text-line-strong">/</span>
{item.href && i < trail.length - 1 ? (
<Link href={item.href} className="inline-flex min-h-11 min-w-11 items-center justify-center transition-colors hover:text-accent">
{item.label}
</Link>
) : (
<span aria-current="page" className="text-ink">
{item.label}
</span>
)}
</span>
))}
</nav>
)}
<div className={`${trail.length ? "mt-8 sm:mt-10" : ""} max-w-3xl ${center ? "mx-auto text-center" : ""}`}>
{label && (
<span className="chip chip-accent">
{Icon && <Icon className="h-3.5 w-3.5" aria-hidden="true" />}
{label}
</span>
)}
<h1 className={`page-title ${label ? "mt-5" : ""}`}>{title}</h1>
{lede && <p className={`lede mt-5 max-w-2xl ${center ? "mx-auto" : ""}`}>{lede}</p>}
{children && <div className={`mt-7 flex flex-wrap gap-3 ${center ? "justify-center" : ""}`}>{children}</div>}
</div>
</section>
);
}