feat(i18n): Spanish localization (phase 4)
Complete implementation of Spanish (es-419) language support: - Canonical catalogs and data: messages/es.json, data/es/**, lib/data/es/**, public/exemplos/es/** - Canonical slugs: /es/series, /es/catalogo, /es/comparacion, /es/componentes, /es/diagnostico, /es/frameworks, /es/glosario, /es/selector, /es/acerca - Dedicated search index at /api/busca/es with Spanish accent normalization - Strict exclusion of book references in Spanish routes and catalogs - Language selector and root negotiation with es active (ENABLED_LOCALES = ['pt', 'en', 'es']) - Extended verification suite: check-i18n, smoke-imagem and verifica-vazamentos Co-Authored-By: Antigravity <noreply@google.com>
This commit is contained in:
1 parent
0940814522
commit
660b28c09d
72 files changed
+17848
-947
No files matched your search
+29
-17
@@ -7,6 +7,8 @@ import { usePathname } from "next/navigation";
|
||||
export default function CodeExample({ framework }) {
|
||||
const pathname = usePathname() || "";
|
||||
const isEn = pathname === "/en" || pathname.startsWith("/en/");
|
||||
const isEs = pathname === "/es" || pathname.startsWith("/es/");
|
||||
const loc = isEn ? "en" : isEs ? "es" : "pt";
|
||||
|
||||
const examplesUrl = framework?.exemplos;
|
||||
|
||||
@@ -27,15 +29,17 @@ export default function CodeExample({ framework }) {
|
||||
setSample(
|
||||
isEn
|
||||
? `// Full examples: ${examplesUrl || "see repository"}\n`
|
||||
: `// Exemplos completos: ${examplesUrl || "ver repositório"}\n`
|
||||
: isEs
|
||||
? `// Ejemplos completos: ${examplesUrl || "ver repositorio"}\n`
|
||||
: `// Exemplos completos: ${examplesUrl || "ver repositório"}\n`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Se locale for en, busca primeiro em /exemplos/en/
|
||||
// Se locale for diferente de pt, busca primeiro em /exemplos/<loc>/
|
||||
let url = exemploPath.startsWith("/") ? exemploPath : `/${exemploPath}`;
|
||||
if (isEn && url.startsWith("/exemplos/") && !url.startsWith("/exemplos/en/")) {
|
||||
url = url.replace("/exemplos/", "/exemplos/en/");
|
||||
if (loc !== "pt" && url.startsWith("/exemplos/") && !url.startsWith(`/exemplos/${loc}/`)) {
|
||||
url = url.replace("/exemplos/", `/exemplos/${loc}/`);
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
@@ -64,9 +68,11 @@ export default function CodeExample({ framework }) {
|
||||
setSample(
|
||||
isEn
|
||||
? "// The example file is empty. Refer to the official repository.\n"
|
||||
: "// O arquivo de exemplo está vazio. Consulte o repositório oficial.\n"
|
||||
: isEs
|
||||
? "// El archivo de ejemplo está vacío. Consulte el repositorio oficial.\n"
|
||||
: "// O arquivo de exemplo está vazio. Consulte o repositório oficial.\n"
|
||||
);
|
||||
setError(isEn ? "empty file" : "arquivo vazio");
|
||||
setError(isEn ? "empty file" : isEs ? "archivo vacío" : "arquivo vazio");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -74,9 +80,11 @@ export default function CodeExample({ framework }) {
|
||||
setSample(
|
||||
isEn
|
||||
? "// Could not load example.\n// Refer to official repository for complete examples.\n"
|
||||
: "// Não foi possível carregar o exemplo.\n// Consulte o repositório oficial para exemplos completos.\n"
|
||||
: isEs
|
||||
? "// No fue posible cargar el ejemplo.\n// Consulte el repositorio oficial para ver los ejemplos completos.\n"
|
||||
: "// Não foi possível carregar o exemplo.\n// Consulte o repositório oficial para exemplos completos.\n"
|
||||
);
|
||||
setError(err.message || (isEn ? "load failure" : "falha ao carregar"));
|
||||
setError(err.message || (isEn ? "load failure" : isEs ? "fallo al cargar" : "falha ao carregar"));
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
@@ -85,7 +93,7 @@ export default function CodeExample({ framework }) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [framework?.exemplo, framework?.exemplos, isEn]);
|
||||
}, [framework?.exemplo, framework?.exemplos, isEn, isEs, loc]);
|
||||
|
||||
const lines = (sample || "").replace(/\n$/, "").split("\n");
|
||||
|
||||
@@ -102,25 +110,27 @@ export default function CodeExample({ framework }) {
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">
|
||||
{isEn ? "Code example" : "Exemplo de código"}
|
||||
{isEn ? "Code example" : isEs ? "Ejemplo de código" : "Exemplo de código"}
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-muted">
|
||||
{isEn
|
||||
? `Starter snippet in ${framework?.linguagem}. Adjust pins and configuration for your board.`
|
||||
: `Trecho de partida em ${framework?.linguagem}. Ajuste pinos e configuração à sua placa.`}
|
||||
: isEs
|
||||
? `Fragmento de inicio en ${framework?.linguagem}. Ajuste los pines y la configuración para su placa.`
|
||||
: `Trecho de partida em ${framework?.linguagem}. Ajuste pinos e configuração à sua placa.`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<button type="button" onClick={handleCopy} className="btn btn-ghost btn-sm">
|
||||
{copied ? <Check className="h-4 w-4 text-ok" aria-hidden="true" /> : <Copy className="h-4 w-4" aria-hidden="true" />}
|
||||
<span role="status">{copied ? (isEn ? "Copied" : "Copiado") : (isEn ? "Copy" : "Copiar")}</span>
|
||||
<span role="status">{copied ? (isEn ? "Copied" : isEs ? "Copiado" : "Copiado") : (isEn ? "Copy" : isEs ? "Copiar" : "Copiar")}</span>
|
||||
</button>
|
||||
{examplesUrl && (
|
||||
<a href={examplesUrl} target="_blank" rel="noopener noreferrer" className="btn btn-ghost btn-sm">
|
||||
{isEn ? "More examples" : "Mais exemplos"}
|
||||
{isEn ? "More examples" : isEs ? "Más ejemplos" : "Mais exemplos"}
|
||||
<ExternalLink className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="sr-only">{isEn ? " (opens in new tab)" : " (abre em nova aba)"}</span>
|
||||
<span className="sr-only">{isEn ? " (opens in new tab)" : isEs ? " (abre en una pestaña nueva)" : " (abre em nova aba)"}</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
@@ -129,9 +139,9 @@ export default function CodeExample({ framework }) {
|
||||
<div className="code-panel mt-5" aria-busy={loading}>
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-4 py-2.5 text-xs text-[#a6afbd]">
|
||||
<span className="font-mono">{framework?.nome}</span>
|
||||
<span>{loading ? (isEn ? "Loading example…" : "Carregando exemplo…") : framework?.linguagem}</span>
|
||||
<span>{loading ? (isEn ? "Loading example…" : isEs ? "Cargando ejemplo…" : "Carregando exemplo…") : framework?.linguagem}</span>
|
||||
</div>
|
||||
<pre tabIndex={0} aria-label={isEn ? `Code example in ${framework?.linguagem}` : `Código de exemplo em ${framework?.linguagem}`}>
|
||||
<pre tabIndex={0} aria-label={isEn ? `Code example in ${framework?.linguagem}` : isEs ? `Código de ejemplo en ${framework?.linguagem}` : `Código de exemplo em ${framework?.linguagem}`}>
|
||||
<code>
|
||||
{lines.map((line, idx) => (
|
||||
<span key={idx} className="flex">
|
||||
@@ -147,7 +157,9 @@ export default function CodeExample({ framework }) {
|
||||
<p role="alert" className="callout callout-warn mt-4">
|
||||
{isEn
|
||||
? `Could not load local example (${error}). Use “More examples” to view official code.`
|
||||
: `Não foi possível carregar o exemplo local (${error}). Use “Mais exemplos” para ver o código oficial.`}
|
||||
: isEs
|
||||
? `No se pudo cargar el ejemplo local (${error}). Utilice “Más ejemplos” para ver el código oficial.`
|
||||
: `Não foi possível carregar o exemplo local (${error}). Use “Mais exemplos” para ver o código oficial.`}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user