feat(i18n): French and German localization (phase 5)
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

- 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>
This commit is contained in:
Lucas RayanandAntigravity committed 2026-11-01 22:06:36 -03:00
1 parent 7b673aeb4b
commit 7fce46b9e1
104 files changed
+33369 -686

No files matched your search

+29 -3
View File
@@ -15,6 +15,8 @@ export default function PageHero({
children,
isEn: isEnProp,
isEs: isEsProp,
isFr: isFrProp,
isDe: isDeProp,
locale: localeProp,
}) {
const center = align === "center";
@@ -30,13 +32,37 @@ export default function PageHero({
: 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={isEn ? "Breadcrumb navigation" : isEs ? "Navegación por migas de pan" : "Trilha de navegação"} className="flex flex-wrap items-center gap-2 text-sm text-muted">
<Link href={isEn ? "/en" : isEs ? "/es" : "/"} className="inline-flex min-h-11 min-w-11 items-center justify-center transition-colors hover:text-accent">
{isEn ? "Home" : isEs ? "Inicio" : "Início"}
<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">