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>
This commit is contained in:
1 parent
7b673aeb4b
commit
7fce46b9e1
104 files changed
+33369
-686
No files matched your search
+69
-8
@@ -19,6 +19,20 @@ import { errosCatalogo as errosEs } from "@/lib/data/es/diagnostico";
|
||||
import { COMPONENTES_REGISTRY as componentesEs } from "@/lib/data/es/componentes";
|
||||
import { montarConhecimento as montarConhecimentoEs } from "@/lib/data/es/topicos";
|
||||
|
||||
import seriesDataFr from "@/data/fr/series.json";
|
||||
import frameworksDataFr from "@/data/fr/frameworks.json";
|
||||
import { categoriasGlossario as glossarioFr } from "@/lib/data/fr/glossario";
|
||||
import { errosCatalogo as errosFr } from "@/lib/data/fr/diagnostico";
|
||||
import { COMPONENTES_REGISTRY as componentesFr } from "@/lib/data/fr/componentes";
|
||||
import { montarConhecimento as montarConhecimentoFr } from "@/lib/data/fr/topicos";
|
||||
|
||||
import seriesDataDe from "@/data/de/series.json";
|
||||
import frameworksDataDe from "@/data/de/frameworks.json";
|
||||
import { categoriasGlossario as glossarioDe } from "@/lib/data/de/glossario";
|
||||
import { errosCatalogo as errosDe } from "@/lib/data/de/diagnostico";
|
||||
import { COMPONENTES_REGISTRY as componentesDe } from "@/lib/data/de/componentes";
|
||||
import { montarConhecimento as montarConhecimentoDe } from "@/lib/data/de/topicos";
|
||||
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -66,15 +80,43 @@ const PAGINAS_ES = [
|
||||
{ titulo: "Diagnóstico de Errores", descricao: "Solucione brownout, bucles de arranque, WDT y fallos", href: "/es/diagnostico", keywords: "error problema fallo crash" },
|
||||
{ titulo: "Frameworks y SDKs", descricao: "ESP-IDF, Arduino, Rust, RainMaker y más", href: "/es/frameworks", keywords: "sdk" },
|
||||
{ titulo: "Component Registry", descricao: "Catálogo de controladores y generador de idf_component.yml", href: "/es/componentes", keywords: "bibliotecas controladores drivers yml" },
|
||||
{ titulo: "Glosario Técnico", descricao: "Conceptos esenciales de hardware y software", href: "/es/glosario", keywords: "términos diccionario significado" },
|
||||
{ titulo: "Glosario Técnico", descricao: "Conceptos esenciales de hardware e software", href: "/es/glosario", keywords: "términos diccionario significado" },
|
||||
{ titulo: "Acerca del Proyecto", descricao: "Historia, autor y código abierto", href: "/es/acerca", keywords: "autor contacto código abierto" },
|
||||
];
|
||||
|
||||
const PAGINAS_FR = [
|
||||
{ titulo: "Accueil", descricao: "Page d'accueil d'ESPDocs", href: "/fr", keywords: "accueil home" },
|
||||
{ titulo: "Toutes les Séries", descricao: "Brochages, spécifications et restrictions pour chaque série ESP32", href: "/fr/series", keywords: "puces brochage specifications pinout" },
|
||||
{ titulo: "Comparateur de Séries", descricao: "Comparez jusqu'à 4 modèles côte à côte", href: "/fr/comparaison", keywords: "comparer comparaison versus" },
|
||||
{ titulo: "Catalogue de Cartes", descricao: "Cartes de développement, brochages et achat", href: "/fr/catalogue", keywords: "cartes dev boards devkit acheter" },
|
||||
{ titulo: "Sélecteur Intelligent", descricao: "Trouvez la puce idéale pour votre projet", href: "/fr/selecteur", keywords: "quiz choisir recommander selecteur" },
|
||||
{ titulo: "Diagnostic d'Erreurs", descricao: "Résolvez brownout, boucles de démarrage, WDT et crashs", href: "/fr/diagnostic", keywords: "erreur probleme panne crash bug" },
|
||||
{ titulo: "Frameworks et SDKs", descricao: "ESP-IDF, Arduino, Rust, RainMaker et plus", href: "/fr/frameworks", keywords: "sdk" },
|
||||
{ titulo: "Component Registry", descricao: "Catalogue de pilotes et générateur idf_component.yml", href: "/fr/composants", keywords: "bibliotheques pilotes drivers yml composants" },
|
||||
{ titulo: "Glossaire Technique", descricao: "Concepts essentiels de matériel et de logiciel", href: "/fr/glossaire", keywords: "termes dictionnaire definition signification" },
|
||||
{ titulo: "À Propos du Projet", descricao: "Histoire, auteur et open source", href: "/fr/a-propos", keywords: "auteur contact open source" },
|
||||
];
|
||||
|
||||
const PAGINAS_DE = [
|
||||
{ titulo: "Startseite", descricao: "ESPDocs Startseite", href: "/de", keywords: "startseite home" },
|
||||
{ titulo: "Alle Serien", descricao: "Pinbelegungen, Spezifikationen und Einschränkungen für jede ESP32-Serie", href: "/de/serien", keywords: "chips pinbelegung spezifikationen pinout" },
|
||||
{ titulo: "Serienvergleich", descricao: "Vergleichen Sie bis zu 4 Modelle nebeneinander", href: "/de/vergleich", keywords: "vergleichen vergleich versus" },
|
||||
{ titulo: "Board-Katalog", descricao: "Entwicklungsboards, Pinbelegungen und Kauf", href: "/de/katalog", keywords: "boards dev boards devkit kaufen" },
|
||||
{ titulo: "Intelligente Auswahl", descricao: "Finden Sie den idealen Chip für Ihr Projekt", href: "/de/auswahl", keywords: "quiz waehlen empfehlen auswahl" },
|
||||
{ titulo: "Fehlerdiagnose", descricao: "Beheben Sie Brownout, Bootloops, WDT und Abstürze", href: "/de/diagnose", keywords: "fehler problem absturz crash bug" },
|
||||
{ titulo: "Frameworks & SDKs", descricao: "ESP-IDF, Arduino, Rust, RainMaker und mehr", href: "/de/frameworks", keywords: "sdk" },
|
||||
{ titulo: "Component Registry", descricao: "Treiberkatalog und idf_component.yml-Generator", href: "/de/komponenten", keywords: "bibliotheken treiber drivers yml komponenten" },
|
||||
{ titulo: "Technisches Glossar", descricao: "Wesentliche Hardware- und Softwarekonzepte", href: "/de/glossar", keywords: "begriffe woerterbuch definition bedeutung" },
|
||||
{ titulo: "Über das Projekt", descricao: "Geschichte, Autor und Open Source", href: "/de/ueber-uns", keywords: "autor kontakt open source" },
|
||||
];
|
||||
|
||||
const cortar = (texto = "", max = 140) => (texto.length > max ? `${texto.slice(0, max - 1).trimEnd()}…` : texto);
|
||||
|
||||
export function construirIndiceBusca(locale = "pt") {
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
|
||||
let paginas = PAGINAS_PT;
|
||||
let seriesData = seriesDataPt;
|
||||
@@ -100,14 +142,29 @@ export function construirIndiceBusca(locale = "pt") {
|
||||
errosCatalogo = errosEs;
|
||||
componentes = componentesEs;
|
||||
montarConhecimento = montarConhecimentoEs;
|
||||
} else if (isFr) {
|
||||
paginas = PAGINAS_FR;
|
||||
seriesData = seriesDataFr;
|
||||
frameworksData = frameworksDataFr;
|
||||
categoriasGlossario = glossarioFr;
|
||||
errosCatalogo = errosFr;
|
||||
componentes = componentesFr;
|
||||
montarConhecimento = montarConhecimentoFr;
|
||||
} else if (isDe) {
|
||||
paginas = PAGINAS_DE;
|
||||
seriesData = seriesDataDe;
|
||||
frameworksData = frameworksDataDe;
|
||||
categoriasGlossario = glossarioDe;
|
||||
errosCatalogo = errosDe;
|
||||
componentes = componentesDe;
|
||||
montarConhecimento = montarConhecimentoDe;
|
||||
}
|
||||
|
||||
const prefixo = isEn ? "/en" : isEs ? "/es" : "";
|
||||
const slugSeries = `${prefixo}/series`;
|
||||
const slugFrameworks = `${prefixo}/frameworks`;
|
||||
const slugGlossario = isEn ? "/en/glossary" : isEs ? "/es/glosario" : "/glossario";
|
||||
const slugDiagnostico = isEn ? "/en/diagnostics" : isEs ? "/es/diagnostico" : "/diagnostico";
|
||||
const slugComponentes = isEn ? "/en/components" : isEs ? "/es/componentes" : "/componentes";
|
||||
const slugSeries = isDe ? "/de/serien" : isFr ? "/fr/series" : isEn ? "/en/series" : isEs ? "/es/series" : "/series";
|
||||
const slugFrameworks = isDe ? "/de/frameworks" : isFr ? "/fr/frameworks" : isEn ? "/en/frameworks" : isEs ? "/es/frameworks" : "/frameworks";
|
||||
const slugGlossario = isDe ? "/de/glossar" : isFr ? "/fr/glossaire" : isEn ? "/en/glossary" : isEs ? "/es/glosario" : "/glossario";
|
||||
const slugDiagnostico = isDe ? "/de/diagnose" : isFr ? "/fr/diagnostic" : isEn ? "/en/diagnostics" : isEs ? "/es/diagnostico" : "/diagnostico";
|
||||
const slugComponentes = isDe ? "/de/komponenten" : isFr ? "/fr/composants" : isEn ? "/en/components" : isEs ? "/es/componentes" : "/componentes";
|
||||
|
||||
const itens = [];
|
||||
|
||||
@@ -174,7 +231,11 @@ export function construirIndiceBusca(locale = "pt") {
|
||||
? path.join(process.cwd(), "data", "en", "conexoes")
|
||||
: isEs
|
||||
? path.join(process.cwd(), "data", "es", "conexoes")
|
||||
: path.join(process.cwd(), "public", "conexoes");
|
||||
: isFr
|
||||
? path.join(process.cwd(), "data", "fr", "conexoes")
|
||||
: isDe
|
||||
? path.join(process.cwd(), "data", "de", "conexoes")
|
||||
: path.join(process.cwd(), "public", "conexoes");
|
||||
const arquivo = path.join(pastaConexoes, `${chave}.json`);
|
||||
if (existsSync(arquivo)) {
|
||||
conexoes[chave] = JSON.parse(readFileSync(arquivo, "utf8"));
|
||||
|
||||
Reference in new issue
Block a user