fix(i18n): deep-merge locale files — shallow spread was eating sections
The common namespace was assembled with a shallow spread, so any top-level section present in two locale files had the later file clobber the earlier one WHOLESALE. Two live casualties: - header: adding header.shareProject to common2 (yesterday's raw-string cleanup) erased common.json's whole header — header.auth.* and header.nav.* — so the account menu rendered "header.auth.signOut" as its label. My regression, caught verifying the account-menu fixes. - editor.share.*: common2's editor section has been clobbering common.json's since forever — the Share modal's seven visibility labels were silently lost in every locale. Pre-existing. Locale files merge by key now (deepMerge, both the synchronous en init and the dynamic per-locale loader), which ends the failure class instead of the instance; shareProject also moves to common.json's header where it belonged. Verified: all header.auth/nav and editor.share leaves resolve again in all nine locales.
This commit is contained in:
parent
155513a493
commit
697a9de4f4
|
|
@ -30,6 +30,38 @@ import enSeo3 from "./locales/en/seo3.json";
|
|||
import enSeo4 from "./locales/en/seo4.json";
|
||||
import { DEFAULT_LOCALE, LOCALES, type Locale } from "./config";
|
||||
|
||||
/**
|
||||
* Deep merge for locale resource files. The namespace used to be built
|
||||
* with a SHALLOW spread, so a top-level section present in two files had
|
||||
* the later file silently clobber the earlier one wholesale: common2's
|
||||
* `editor` erased common.json's `editor.share.*` (the Share modal's
|
||||
* visibility labels rendered as raw keys), and the same collision on
|
||||
* `header` later wiped `header.auth.*` — the account menu showed
|
||||
* "header.auth.signOut". Merging by key ends the failure class.
|
||||
*/
|
||||
function deepMerge(
|
||||
base: Record<string, unknown>,
|
||||
extra: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const out: Record<string, unknown> = { ...base };
|
||||
for (const [k, v] of Object.entries(extra)) {
|
||||
const prev = out[k];
|
||||
if (
|
||||
prev && v &&
|
||||
typeof prev === "object" && typeof v === "object" &&
|
||||
!Array.isArray(prev) && !Array.isArray(v)
|
||||
) {
|
||||
out[k] = deepMerge(
|
||||
prev as Record<string, unknown>,
|
||||
v as Record<string, unknown>,
|
||||
);
|
||||
} else {
|
||||
out[k] = v;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const NAMESPACES = ["common"] as const;
|
||||
type Namespace = (typeof NAMESPACES)[number];
|
||||
|
||||
|
|
@ -47,9 +79,7 @@ void i18n
|
|||
resources: {
|
||||
en: {
|
||||
common: {
|
||||
...enCommon,
|
||||
...enCommon2,
|
||||
...enReleases,
|
||||
...deepMerge(deepMerge(enCommon, enCommon2), enReleases),
|
||||
seo: {
|
||||
...enSeo.seo,
|
||||
...enSeo2.seo,
|
||||
|
|
@ -131,9 +161,10 @@ export async function loadLocale(locale: Locale): Promise<void> {
|
|||
...((seo4Mod.default ?? seo4Mod).seo ?? {}),
|
||||
};
|
||||
const merged = {
|
||||
...(commonMod.default ?? commonMod),
|
||||
...(common2Mod.default ?? common2Mod),
|
||||
...(releasesMod.default ?? releasesMod),
|
||||
...deepMerge(
|
||||
deepMerge(commonMod.default ?? commonMod, common2Mod.default ?? common2Mod),
|
||||
releasesMod.default ?? releasesMod,
|
||||
),
|
||||
seo: seoBody,
|
||||
docs: { ...docs1Body, ...docs2Body },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Abmelden"
|
||||
},
|
||||
"language": "Sprache",
|
||||
"search": "Suche"
|
||||
"search": "Suche",
|
||||
"shareProject": "Projekt teilen"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio ist ein kostenloser, quelloffener Schaltkreis- und Mikrocontroller-Simulator, der vollständig in Ihrem Browser läuft. Arduino, ESP32, RP2040 und ATtiny85, mit SPICE-genauer analoger Co-Simulation. Entwickelt für Maker, Studenten und Embedded-Ingenieure, die durch praktisches Lernen lernen. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "Keine Anmeldung erforderlich. Läuft zu 100 % in Ihrem Browser. Kostenlos und Open Source.",
|
||||
"openEditor": "Editor öffnen"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Projekt teilen"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Sign out"
|
||||
},
|
||||
"language": "Language",
|
||||
"search": "Search"
|
||||
"search": "Search",
|
||||
"shareProject": "Share project"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio is a free, open-source circuit and microcontroller simulator that runs entirely in your browser. Arduino, ESP32, RP2040 and ATtiny85, with SPICE-accurate analog co-simulation. Built for makers, students and embedded engineers learning by doing. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -465,8 +465,5 @@
|
|||
"subtitle": "No signup required. Runs 100% in your browser. Free and open source.",
|
||||
"openEditor": "Open Editor"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Share project"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Cerrar sesión"
|
||||
},
|
||||
"language": "Idioma",
|
||||
"search": "Buscar"
|
||||
"search": "Buscar",
|
||||
"shareProject": "Compartir proyecto"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio es un simulador gratuito y de código abierto de circuitos y microcontroladores que funciona completamente en tu navegador. Arduino, ESP32, RP2040 y ATtiny85, con co-simulación analógica precisa con SPICE. Creado para makers, estudiantes e ingenieros de sistemas embebidos que aprenden haciendo. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -465,8 +465,5 @@
|
|||
"subtitle": "No requiere registro. Funciona 100% en tu navegador. Gratuito y de código abierto.",
|
||||
"openEditor": "Abrir editor"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Compartir proyecto"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Déconnexion"
|
||||
},
|
||||
"language": "Langue",
|
||||
"search": "Rechercher"
|
||||
"search": "Rechercher",
|
||||
"shareProject": "Partager le projet"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio est un simulateur de circuits et microcontrôleurs gratuit et open-source qui fonctionne entièrement dans votre navigateur. Arduino, ESP32, RP2040 et ATtiny85, avec co-simulation analogique précise SPICE. Conçu pour les makers, étudiants et ingénieurs embarqués qui apprennent en pratiquant. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "Aucune inscription requise. Fonctionne à 100 % dans votre navigateur. Gratuit et open source.",
|
||||
"openEditor": "Ouvrir l'éditeur"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Partager le projet"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Esci"
|
||||
},
|
||||
"language": "Lingua",
|
||||
"search": "Cerca"
|
||||
"search": "Cerca",
|
||||
"shareProject": "Condividi progetto"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio è un simulatore gratuito e open-source di circuiti e microcontrollori che funziona interamente nel tuo browser. Arduino, ESP32, RP2040 e ATtiny85, con co-simulazione analogica SPICE accurata. Creato per maker, studenti e ingegneri embedded che imparano facendo. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "Nessuna registrazione richiesta. Funziona al 100% nel tuo browser. Gratuito e open source.",
|
||||
"openEditor": "Apri Editor"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Condividi progetto"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "サインアウト"
|
||||
},
|
||||
"language": "言語",
|
||||
"search": "検索"
|
||||
"search": "検索",
|
||||
"shareProject": "プロジェクトを共有"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxioは、ブラウザ上で完全に動作する無料のオープンソース回路・マイクロコントローラシミュレータです。Arduino、ESP32、RP2040、ATtiny85に対応し、SPICE精度のアナログ同時シミュレーションを提供します。メーカー、学生、組み込みエンジニアが実践的に学ぶために作られました。AGPLv3ライセンス。"
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "サインアップ不要。100% ブラウザで動作。無料でオープンソース。",
|
||||
"openEditor": "エディタを開く"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "プロジェクトを共有"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Sair"
|
||||
},
|
||||
"language": "Idioma",
|
||||
"search": "Pesquisar"
|
||||
"search": "Pesquisar",
|
||||
"shareProject": "Compartilhar projeto"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio é um simulador gratuito e de código aberto de circuitos e microcontroladores que roda inteiramente no seu navegador. Arduino, ESP32, RP2040 e ATtiny85, com co-simulação analógica precisa em SPICE. Construído para makers, estudantes e engenheiros embarcados que aprendem na prática. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "Sem necessidade de cadastro. Roda 100% no seu navegador. Gratuito e de código aberto.",
|
||||
"openEditor": "Abrir Editor"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Compartilhar projeto"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "Выйти"
|
||||
},
|
||||
"language": "Язык",
|
||||
"search": "Поиск"
|
||||
"search": "Поиск",
|
||||
"shareProject": "Поделиться проектом"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio — это бесплатный симулятор цепей и микроконтроллеров с открытым исходным кодом, работающий прямо в браузере. Arduino, ESP32, RP2040 и ATtiny85 с аналоговым ко-симулятором на SPICE. Создан для любителей, студентов и инженеров, изучающих встраиваемые системы на практике. AGPLv3."
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "Регистрация не требуется. Работает 100% в вашем браузере. Бесплатно и с открытым исходным кодом.",
|
||||
"openEditor": "Открыть редактор"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "Поделиться проектом"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
"signOut": "退出"
|
||||
},
|
||||
"language": "语言",
|
||||
"search": "搜索"
|
||||
"search": "搜索",
|
||||
"shareProject": "分享项目"
|
||||
},
|
||||
"footer": {
|
||||
"about": "Velxio 是一款免费、开源的电路和微控制器模拟器,完全在浏览器中运行。支持 Arduino、ESP32、RP2040 和 ATtiny85,并具有 SPICE 精度的模拟协同仿真。专为通过实践学习的设计者、学生和嵌入式工程师打造。采用 AGPLv3 许可。"
|
||||
|
|
|
|||
|
|
@ -463,8 +463,5 @@
|
|||
"subtitle": "无需注册。100% 在浏览器中运行。免费且开源。",
|
||||
"openEditor": "打开编辑器"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"shareProject": "分享项目"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue