feat(i18n): Simplified Chinese (zh-CN), search answers per locale, fr/de/es data fixes
CI / Lint e auditorias (push) Successful in 1m10s
CI / Trivy (dependências e segredos) (push) Successful in 12s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m39s
Release / Plano (reaproveitar a imagem?) (push) Successful in 6s
Release / ci (push) Skipped
Release / Publica a imagem (linux/arm64) (push) Successful in 18s
Release / Cria a release no Gitea (push) Successful in 5s
CI / Lint e auditorias (push) Successful in 1m10s
CI / Trivy (dependências e segredos) (push) Successful in 12s
CI / Imagem Docker (linux/arm64) (push) Successful in 2m39s
Release / Plano (reaproveitar a imagem?) (push) Successful in 6s
Release / ci (push) Skipped
Release / Publica a imagem (linux/arm64) (push) Successful in 18s
Release / Cria a release no Gitea (push) Successful in 5s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ae61416468
commit
5a9fe676fc
61 files changed
+2517
-920
No files matched your search
+40
-26
@@ -75,6 +75,16 @@ const TIPOS_DE = {
|
||||
erro: { label: "Fehler", Icon: AlertTriangle, cor: "bg-gold-soft text-gold" },
|
||||
componente: { label: "Komponente", Icon: Box, cor: "bg-surface-2 text-ink-soft" },
|
||||
};
|
||||
const TIPOS_ZH = {
|
||||
resposta: { label: "答案", Icon: Sparkles, cor: "bg-accent text-accent-contrast" },
|
||||
pino: { label: "引脚", Icon: Plug, cor: "bg-accent text-accent-contrast" },
|
||||
pagina: { label: "页面", Icon: FileText, cor: "bg-surface-2 text-ink-soft" },
|
||||
serie: { label: "系列", Icon: Cpu, cor: "bg-accent-soft text-accent" },
|
||||
framework: { label: "框架", Icon: Terminal, cor: "bg-teal-soft text-teal" },
|
||||
glossario: { label: "术语", Icon: BookOpen, cor: "bg-copper-soft text-copper" },
|
||||
erro: { label: "错误", Icon: AlertTriangle, cor: "bg-gold-soft text-gold" },
|
||||
componente: { label: "组件", Icon: Box, cor: "bg-surface-2 text-ink-soft" },
|
||||
};
|
||||
|
||||
const INTELIGENTES = new Set(["resposta", "pino"]);
|
||||
|
||||
@@ -82,6 +92,7 @@ const EXEMPLOS_EN = ["flash s3", "usb pins c6", "gpio0 esp32", "brownout", "LVGL
|
||||
const EXEMPLOS_ES = ["flash s3", "pines usb c6", "gpio0 esp32", "brownout", "LVGL", "FreeRTOS"];
|
||||
const EXEMPLOS_FR = ["flash s3", "broches usb c6", "gpio0 esp32", "brownout", "LVGL", "FreeRTOS"];
|
||||
const EXEMPLOS_DE = ["flash s3", "usb pins c6", "gpio0 esp32", "brownout", "LVGL", "FreeRTOS"];
|
||||
const EXEMPLOS_ZH = ["s3 闪存", "c6 usb 引脚", "esp32 gpio0", "brownout", "LVGL", "FreeRTOS"];
|
||||
|
||||
function Destaque({ texto, termos }) {
|
||||
if (!termos.length) return texto;
|
||||
@@ -142,14 +153,17 @@ export default function SearchDialog() {
|
||||
? "fr"
|
||||
: pathname === "/de" || pathname.startsWith("/de/")
|
||||
? "de"
|
||||
: pathname === "/zh-CN" || pathname.startsWith("/zh-CN/")
|
||||
? "zh-CN"
|
||||
: "pt";
|
||||
const isEn = locale === "en";
|
||||
const isEs = locale === "es";
|
||||
const isFr = locale === "fr";
|
||||
const isDe = locale === "de";
|
||||
const isZh = locale === "zh-CN";
|
||||
|
||||
const TIPOS = isEn ? TIPOS_EN : isEs ? TIPOS_ES : isFr ? TIPOS_FR : isDe ? TIPOS_DE : TIPOS_PT;
|
||||
const EXEMPLOS = isEn ? EXEMPLOS_EN : isEs ? EXEMPLOS_ES : isFr ? EXEMPLOS_FR : isDe ? EXEMPLOS_DE : EXEMPLOS_DE_BUSCA;
|
||||
const TIPOS = isEn ? TIPOS_EN : isEs ? TIPOS_ES : isFr ? TIPOS_FR : isZh ? TIPOS_ZH : isDe ? TIPOS_DE : TIPOS_PT;
|
||||
const EXEMPLOS = isEn ? EXEMPLOS_EN : isEs ? EXEMPLOS_ES : isFr ? EXEMPLOS_FR : isZh ? EXEMPLOS_ZH : isDe ? EXEMPLOS_DE : EXEMPLOS_DE_BUSCA;
|
||||
|
||||
const [aberto, setAberto] = useState(false);
|
||||
const [montado, setMontado] = useState(false);
|
||||
@@ -196,7 +210,7 @@ export default function SearchDialog() {
|
||||
? "/api/busca/es?v=2"
|
||||
: isFr
|
||||
? "/api/busca/fr?v=2"
|
||||
: isDe
|
||||
: isZh ? "/api/busca/zh-CN?v=2" : isDe
|
||||
? "/api/busca/de?v=2"
|
||||
: "/api/busca?v=2";
|
||||
fetch(url, { cache: "no-cache" })
|
||||
@@ -222,7 +236,7 @@ export default function SearchDialog() {
|
||||
return () => {
|
||||
cancelado = true;
|
||||
};
|
||||
}, [isEn, isEs, isFr, isDe]);
|
||||
}, [isEn, isEs, isFr, isDe, isZh]);
|
||||
|
||||
// Carrega o índice (textos + especificações + pinos) na primeira abertura.
|
||||
useEffect(() => {
|
||||
@@ -293,17 +307,17 @@ export default function SearchDialog() {
|
||||
id="botao-busca"
|
||||
onClick={() => setAberto(true)}
|
||||
className="flex h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center gap-2 rounded-full border border-line px-3 text-ink transition-colors hover:bg-surface-2 xl:px-4"
|
||||
aria-label={isEn ? "Search ESPDocs (Ctrl+K)" : isEs ? "Buscar en ESPDocs (Ctrl+K)" : isFr ? "Rechercher sur ESPDocs (Ctrl+K)" : isDe ? "ESPDocs durchsuchen (Strg+K)" : "Buscar no ESPDocs (Ctrl+K)"}
|
||||
aria-label={isEn ? "Search ESPDocs (Ctrl+K)" : isEs ? "Buscar en ESPDocs (Ctrl+K)" : isFr ? "Rechercher sur ESPDocs (Ctrl+K)" : isZh ? "在 ESPDocs 中搜索(Ctrl+K)" : isDe ? "ESPDocs durchsuchen (Strg+K)" : "Buscar no ESPDocs (Ctrl+K)"}
|
||||
>
|
||||
<Search className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="hidden text-sm font-medium xl:inline">{isEn ? "Search" : isEs ? "Buscar" : isFr ? "Rechercher" : isDe ? "Suchen" : "Buscar"}</span>
|
||||
<kbd className="hidden rounded-md border border-line bg-surface-2 px-1.5 py-0.5 font-mono text-xs font-semibold text-muted xl:inline">{isDe ? "Strg K" : "Ctrl K"}</kbd>
|
||||
<span className="hidden text-sm font-medium xl:inline">{isEn ? "Search" : isEs ? "Buscar" : isFr ? "Rechercher" : isZh ? "搜索" : isDe ? "Suchen" : "Buscar"}</span>
|
||||
<kbd className="hidden rounded-md border border-line bg-surface-2 px-1.5 py-0.5 font-mono text-xs font-semibold text-muted xl:inline">{isZh ? "Ctrl K" : isDe ? "Strg K" : "Ctrl K"}</kbd>
|
||||
</button>
|
||||
|
||||
{montado &&
|
||||
aberto &&
|
||||
createPortal(
|
||||
<div className="fixed inset-0 z-[120] flex items-start justify-center px-4 pt-[8vh]" role="dialog" aria-modal="true" aria-label={isEn ? "Search ESPDocs" : isEs ? "Buscar en ESPDocs" : isFr ? "Rechercher sur ESPDocs" : isDe ? "ESPDocs durchsuchen" : "Buscar no ESPDocs"}>
|
||||
<div className="fixed inset-0 z-[120] flex items-start justify-center px-4 pt-[8vh]" role="dialog" aria-modal="true" aria-label={isEn ? "Search ESPDocs" : isEs ? "Buscar en ESPDocs" : isFr ? "Rechercher sur ESPDocs" : isZh ? "在 ESPDocs 中搜索" : isDe ? "ESPDocs durchsuchen" : "Buscar no ESPDocs"}>
|
||||
<div className="dialog-scrim absolute inset-0 bg-black/45 backdrop-blur-sm" onClick={fechar} />
|
||||
<div className="dialog-panel relative flex max-h-[80vh] w-full max-w-2xl flex-col overflow-hidden rounded-3xl border border-line bg-canvas shadow-2xl">
|
||||
<div className="flex items-center gap-3 border-b border-line px-5">
|
||||
@@ -322,20 +336,20 @@ export default function SearchDialog() {
|
||||
? "Pregunte a ESPDocs: “flash s3”, “pines usb c6”, “gpio0 esp32”…"
|
||||
: isFr
|
||||
? "Demandez à ESPDocs : « flash s3 », « broches usb c6 », « gpio0 esp32 »…"
|
||||
: isDe
|
||||
: isZh ? "向 ESPDocs 提问:“s3 闪存”、“c6 usb 引脚”、“esp32 gpio0”…" : isDe
|
||||
? "ESPDocs fragen: „flash s3“, „usb pins c6“, „gpio0 esp32“… "
|
||||
: "Pergunte ao ESPDocs: “flash s3”, “pinos usb c6”, “gpio0 esp32”…"
|
||||
}
|
||||
className="flex-1 bg-transparent py-5 text-base text-ink outline-none placeholder:text-muted"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
aria-label={isEn ? "Search query" : isEs ? "Término de búsqueda" : isFr ? "Requête de recherche" : isDe ? "Suchbegriff" : "Termo de busca"}
|
||||
aria-label={isEn ? "Search query" : isEs ? "Término de búsqueda" : isFr ? "Requête de recherche" : isZh ? "搜索词" : isDe ? "Suchbegriff" : "Termo de busca"}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={fechar}
|
||||
className="grid h-9 w-9 cursor-pointer place-items-center rounded-full text-muted transition-colors hover:bg-surface-2 hover:text-ink"
|
||||
aria-label={isEn ? "Close search" : isEs ? "Cerrar búsqueda" : isFr ? "Fermer la recherche" : isDe ? "Suche schließen" : "Fechar busca"}
|
||||
aria-label={isEn ? "Close search" : isEs ? "Cerrar búsqueda" : isFr ? "Fermer la recherche" : isZh ? "关闭搜索" : isDe ? "Suche schließen" : "Fechar busca"}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -343,7 +357,7 @@ export default function SearchDialog() {
|
||||
|
||||
{entendeu && (
|
||||
<div className="flex flex-wrap items-center gap-2 border-b border-line bg-surface px-5 py-2.5">
|
||||
<span className="text-xs font-semibold text-muted">{isEn ? "Understood:" : isEs ? "Entendido:" : isFr ? "Compris :" : isDe ? "Erkannt:" : "Entendi:"}</span>
|
||||
<span className="text-xs font-semibold text-muted">{isEn ? "Understood:" : isEs ? "Entendido:" : isFr ? "Compris :" : isZh ? "理解为:" : isDe ? "Erkannt:" : "Entendi:"}</span>
|
||||
{interpretacao.chips.map((c) => (
|
||||
<Pilula key={c.chave} forte>{c.nome}</Pilula>
|
||||
))}
|
||||
@@ -357,8 +371,8 @@ export default function SearchDialog() {
|
||||
<div className="flex flex-wrap items-center gap-2 border-b border-line px-5 py-2.5">
|
||||
<span className="text-xs font-semibold text-muted">
|
||||
{interpretacao.chips.length
|
||||
? (isEn ? "Ask about:" : isEs ? "Preguntar sobre:" : isFr ? "Poser une question sur :" : isDe ? "Frage zu:" : "Perguntar sobre:")
|
||||
: (isEn ? "Which chip?" : isEs ? "¿Para qué chip?" : isFr ? "Pour quelle puce ?" : isDe ? "Für welchen Chip?" : "Para qual chip?")}
|
||||
? (isEn ? "Ask about:" : isEs ? "Preguntar sobre:" : isFr ? "Poser une question sur :" : isZh ? "查询:" : isDe ? "Frage zu:" : "Perguntar sobre:")
|
||||
: (isEn ? "Which chip?" : isEs ? "¿Para qué chip?" : isFr ? "Pour quelle puce ?" : isZh ? "针对哪款芯片?" : isDe ? "Für welchen Chip?" : "Para qual chip?")}
|
||||
</span>
|
||||
{sugestoes.map((s) => (
|
||||
<Pilula key={s.rotulo} onClick={() => anexar(s.anexar)}>
|
||||
@@ -378,7 +392,7 @@ export default function SearchDialog() {
|
||||
? "No se pudo cargar el índice de búsqueda."
|
||||
: isFr
|
||||
? "Impossible de charger l'index de recherche."
|
||||
: isDe
|
||||
: isZh ? "无法加载搜索索引。" : isDe
|
||||
? "Suchindex konnte nicht geladen werden."
|
||||
: "Não foi possível carregar o índice de busca."}
|
||||
</p>
|
||||
@@ -387,20 +401,20 @@ export default function SearchDialog() {
|
||||
onClick={carregarIndice}
|
||||
className="btn btn-primary btn-sm mt-3"
|
||||
>
|
||||
{isEn ? "Try again" : isEs ? "Reintentar" : isFr ? "Réessayer" : isDe ? "Erneut versuchen" : "Tentar novamente"}
|
||||
{isEn ? "Try again" : isEs ? "Reintentar" : isFr ? "Réessayer" : isZh ? "重试" : isDe ? "Erneut versuchen" : "Tentar novamente"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!erro && !indice && (
|
||||
<p className="p-6 text-center text-sm text-muted">
|
||||
{isEn ? "Loading…" : isEs ? "Cargando…" : isFr ? "Chargement…" : isDe ? "Wird geladen…" : "Carregando…"}
|
||||
{isEn ? "Loading…" : isEs ? "Cargando…" : isFr ? "Chargement…" : isZh ? "加载中…" : isDe ? "Wird geladen…" : "Carregando…"}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{indice && vazia && (
|
||||
<div className="px-3 pb-3 pt-1">
|
||||
<p className="text-xs font-semibold text-muted">
|
||||
{isEn ? "Try asking" : isEs ? "Pruebe a preguntar" : isFr ? "Essayez de demander" : isDe ? "Versuchen Sie" : "Experimente perguntar"}
|
||||
{isEn ? "Try asking" : isEs ? "Pruebe a preguntar" : isFr ? "Essayez de demander" : isZh ? "试着问问" : isDe ? "Versuchen Sie" : "Experimente perguntar"}
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
{EXEMPLOS.map((ex) => (
|
||||
@@ -410,7 +424,7 @@ export default function SearchDialog() {
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-4 text-xs font-semibold text-muted">
|
||||
{isEn ? "Go to" : isEs ? "Ir a" : isFr ? "Aller à" : isDe ? "Gehe zu" : "Ir para"}
|
||||
{isEn ? "Go to" : isEs ? "Ir a" : isFr ? "Aller à" : isZh ? "前往" : isDe ? "Gehe zu" : "Ir para"}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -424,7 +438,7 @@ export default function SearchDialog() {
|
||||
? `Sin resultados para “${consulta}”`
|
||||
: isFr
|
||||
? `Aucun résultat pour « ${consulta} »`
|
||||
: isDe
|
||||
: isZh ? `未找到“${consulta}”的结果` : isDe
|
||||
? `Keine Ergebnisse für „${consulta}“`
|
||||
: `Nenhum resultado para “${consulta}”`}
|
||||
</p>
|
||||
@@ -435,7 +449,7 @@ export default function SearchDialog() {
|
||||
? "Pruebe “brownout”, “flash s3”, “pines usb c6” o “LVGL”."
|
||||
: isFr
|
||||
? "Essayez « brownout », « flash s3 », « broches usb c6 » ou « LVGL »."
|
||||
: isDe
|
||||
: isZh ? "试试“brownout”、“s3 闪存”、“c6 usb 引脚”或“LVGL”。" : isDe
|
||||
? "Versuchen Sie „brownout“, „flash s3“, „usb pins c6“ oder „LVGL“."
|
||||
: "Tente “brownout”, “flash s3”, “pinos usb c6” ou “LVGL”."}
|
||||
</p>
|
||||
@@ -501,9 +515,9 @@ export default function SearchDialog() {
|
||||
</div>
|
||||
|
||||
<div className="hidden items-center gap-4 border-t border-line bg-surface px-4 py-2.5 text-xs text-muted md:flex">
|
||||
<span className="flex items-center gap-1"><ArrowUp className="h-3 w-3" /><ArrowDown className="h-3 w-3" /> {isEn ? "navigate" : isEs ? "navegar" : isFr ? "naviguer" : isDe ? "navigieren" : "navegar"}</span>
|
||||
<span className="flex items-center gap-1"><CornerDownLeft className="h-3 w-3" /> {isEn ? "open" : isEs ? "abrir" : isFr ? "ouvrir" : isDe ? "öffnen" : "abrir"}</span>
|
||||
<span><kbd className="font-mono font-semibold">Esc</kbd> {isEn ? "close" : isEs ? "cerrar" : isFr ? "fermer" : isDe ? "schließen" : "fechar"}</span>
|
||||
<span className="flex items-center gap-1"><ArrowUp className="h-3 w-3" /><ArrowDown className="h-3 w-3" /> {isEn ? "navigate" : isEs ? "navegar" : isFr ? "naviguer" : isZh ? "导航" : isDe ? "navigieren" : "navegar"}</span>
|
||||
<span className="flex items-center gap-1"><CornerDownLeft className="h-3 w-3" /> {isEn ? "open" : isEs ? "abrir" : isFr ? "ouvrir" : isZh ? "打开" : isDe ? "öffnen" : "abrir"}</span>
|
||||
<span><kbd className="font-mono font-semibold">Esc</kbd> {isEn ? "close" : isEs ? "cerrar" : isFr ? "fermer" : isZh ? "关闭" : isDe ? "schließen" : "fechar"}</span>
|
||||
<span className="ml-auto">
|
||||
{vazia
|
||||
? (isEn
|
||||
@@ -512,7 +526,7 @@ export default function SearchDialog() {
|
||||
? "Consejo: presione / en cualquier página"
|
||||
: isFr
|
||||
? "Astuce : appuyez sur / sur n'importe quelle page"
|
||||
: isDe
|
||||
: isZh ? "提示:在任意页面按 / 键" : isDe
|
||||
? "Tipp: Drücken Sie / auf einer beliebigen Seite"
|
||||
: "Dica: pressione / em qualquer página")
|
||||
: `${resultados.length} ${
|
||||
@@ -522,7 +536,7 @@ export default function SearchDialog() {
|
||||
? "resultado(s)"
|
||||
: isFr
|
||||
? "résultat(s)"
|
||||
: isDe
|
||||
: isZh ? "条结果" : isDe
|
||||
? "Ergebnis(se)"
|
||||
: "resultado(s)"
|
||||
}`}
|
||||
|
||||
Reference in new issue
Block a user