feat(i18n): translate all case studies and resume to English

- add .en.html overrides for 16 case pages + resume via apinex translator
- translate remaining partials (projects-related, mini-projects-*, fab-stack-home-contact,
  fab-stack-resume, content-edit-fab-stack, cookie-notice)
- enhance translate.mjs to protect <script>/<style> from translation
- second pass: translate attribute values (aria-label/alt/title/placeholder/content/
  data-hint-default) and visible text DeepL skipped in html mode
- make logo language-aware (NM / НМ) via i18n
This commit is contained in:
root committed 2026-09-15 23:18:01 +02:00
1 parent 20ee81bfaf
commit 4b52cec00d
58 files changed
+14812 -6924

No files matched your search

+8 -9
View File
@@ -91,21 +91,20 @@ function getKey() {
throw new Error("API key not found (HERMES_CUSTOM_APINEX_API_KEY / APINEX_API_KEY / " + ENV_PATH + ")");
}
// ---- защита токенов и <code> для HTML ----
// ---- защита токенов, <script>, <style> и <code> для HTML ----
function protect(html) {
const store = [];
let out = html.replace(/\{\{[\s\S]*?\}\}/g, (m) => {
store.push(m);
return `<!--TPL${store.length - 1}-->`;
});
out = out.replace(/<code>([\s\S]*?)<\/code>/g, (_m, inner) => {
store.push(inner);
return `<code><!--CPL${store.length - 1}--></code>`;
});
let out = html
// <script>/<style> целиком: переводчик не должен трогать JS/CSS
.replace(/<script[\s\S]*?<\/script>/gi, (m) => { store.push(m); return `<!--SCP${store.length - 1}-->`; })
.replace(/<style[\s\S]*?<\/style>/gi, (m) => { store.push(m); return `<!--SCP${store.length - 1}-->`; })
.replace(/\{\{[\s\S]*?\}\}/g, (m) => { store.push(m); return `<!--TPL${store.length - 1}-->`; })
.replace(/<code>([\s\S]*?)<\/code>/g, (_m, inner) => { store.push(inner); return `<code><!--CPL${store.length - 1}--></code>`; });
return { out, store };
}
function restore(html, store) {
return html
.replace(/<!--SCP(\d+)-->/g, (_m, i) => store[+i])
.replace(/<!--TPL(\d+)-->/g, (_m, i) => store[+i])
.replace(/<code><!--CPL(\d+)--><\/code>/g, (_m, i) => `<code>${store[+i]}</code>`);
}