From c20f72b41a5edef42942e1009f8e2b8cbbbd41a2 Mon Sep 17 00:00:00 2001 From: Meow <197331664+Meo597@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:11:35 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E8=BF=87=E6=97=B6=E6=8F=90?= =?UTF-8?q?=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- .../theme/components/TranslationNotice.vue | 72 ++++++++++++ .vitepress/theme/index.mts | 2 + package.json | 6 +- scripts/gen-i18n-stale.mjs | 105 ++++++++++++++++++ 5 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 .vitepress/theme/components/TranslationNotice.vue create mode 100644 scripts/gen-i18n-stale.mjs diff --git a/.gitignore b/.gitignore index dd18b2a5..020e7e66 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ node_modules/ # VitePress .vitepress/dist/ .vitepress/cache/ -.vitepress/.generated/contributors.json +.vitepress/.generated/ diff --git a/.vitepress/theme/components/TranslationNotice.vue b/.vitepress/theme/components/TranslationNotice.vue new file mode 100644 index 00000000..f25d4f32 --- /dev/null +++ b/.vitepress/theme/components/TranslationNotice.vue @@ -0,0 +1,72 @@ + + + + + + {{ text.title }} + + + + + {{ text.body }} + + + This page is not translated yet. Please refer to the Chinese original. + + + + {{ text.go }} + + + + + + diff --git a/.vitepress/theme/index.mts b/.vitepress/theme/index.mts index 5ec6b655..d844a91f 100644 --- a/.vitepress/theme/index.mts +++ b/.vitepress/theme/index.mts @@ -9,6 +9,7 @@ import { onMounted, watch, nextTick } from "vue"; import { useRoute } from "vitepress"; import { h } from "vue"; +import TranslationNotice from "./components/TranslationNotice.vue"; import PageContributors from "./components/PageContributors.vue"; export default { @@ -31,6 +32,7 @@ export default { Layout() { return h(DefaultTheme.Layout, null, { + "doc-before": () => h(TranslationNotice), "doc-after": () => h(PageContributors), }); }, diff --git a/package.json b/package.json index 105b3a36..6c5168bc 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "vitepress-plugin-mermaid": "^2.0.17" }, "scripts": { - "docs:dev": "node scripts/gen-contributors.mjs && vitepress dev", - "docs:build": "node scripts/gen-contributors.mjs && vitepress build", - "docs:preview": "node scripts/gen-contributors.mjs && vitepress preview" + "docs:dev": "node scripts/gen-i18n-stale.mjs && node scripts/gen-contributors.mjs && vitepress dev", + "docs:build": "node scripts/gen-i18n-stale.mjs && node scripts/gen-contributors.mjs && vitepress build", + "docs:preview": "node scripts/gen-i18n-stale.mjs && node scripts/gen-contributors.mjs && vitepress preview" } } diff --git a/scripts/gen-i18n-stale.mjs b/scripts/gen-i18n-stale.mjs new file mode 100644 index 00000000..4893ce2c --- /dev/null +++ b/scripts/gen-i18n-stale.mjs @@ -0,0 +1,105 @@ +import fs from "node:fs"; +import path from "node:path"; +import { execSync } from "node:child_process"; + +const ROOT = process.cwd(); +const DOCS_DIR = path.resolve(ROOT, "docs"); +const OUT_DIR = path.resolve(ROOT, ".vitepress/.generated"); +const OUT_FILE = path.join(OUT_DIR, "i18n-status.json"); + +const LOCALES = ["en", "ru"]; + +function walk(dir) { + const out = []; + for (const name of fs.readdirSync(dir)) { + const p = path.join(dir, name); + const st = fs.statSync(p); + if (st.isDirectory()) out.push(...walk(p)); + else if (p.endsWith(".md")) out.push(p); + } + return out; +} + +function gitLastCommitISO(fileAbsPath) { + const rel = path.relative(ROOT, fileAbsPath).replaceAll("\\", "/"); + try { + const iso = execSync(`git log -1 --format=%cI -- "${rel}"`, { + encoding: "utf8", + }).trim(); + return iso || null; + } catch { + return null; + } +} + +function toRouteFromDocsRel(relToDocs) { + let route = "/" + relToDocs.replace(/\.md$/, ""); + route = route.replace(/\/index$/, "/"); + return route; +} + +const zhFiles = walk(DOCS_DIR).filter((p) => { + const rel = path.relative(DOCS_DIR, p).replaceAll("\\", "/"); + return !LOCALES.some((l) => rel.startsWith(l + "/")); +}); + +const zhByRel = new Map(); // relToDocs -> abs +for (const abs of zhFiles) { + const relToDocs = path.relative(DOCS_DIR, abs).replaceAll("\\", "/"); + zhByRel.set(relToDocs, abs); +} + +const data = {}; +for (const locale of LOCALES) { + const localeDir = path.join(DOCS_DIR, locale); + if (!fs.existsSync(localeDir)) continue; + + const tFiles = walk(localeDir); + for (const tAbs of tFiles) { + const relToDocs = path.relative(DOCS_DIR, tAbs).replaceAll("\\", "/"); // en/config/log.md + const relNoLocale = relToDocs.replace(new RegExp(`^${locale}/`), ""); // config/log.md + const zhAbs = zhByRel.get(relNoLocale); + + const tRoute = toRouteFromDocsRel(relToDocs); // /en/config/log + const zhRoute = toRouteFromDocsRel(relNoLocale); // /config/log + + const tISO = gitLastCommitISO(tAbs); + const zhISO = zhAbs ? gitLastCommitISO(zhAbs) : null; + + const stale = + Boolean(zhISO && tISO) && + new Date(tISO).getTime() < new Date(zhISO).getTime(); + + data[tRoute] = { + locale, + zhRoute, + translated: true, + stale, + tLastUpdated: tISO, + zhLastUpdated: zhISO, + }; + } +} + +for (const [zhRel, zhAbs] of zhByRel.entries()) { + const zhRoute = toRouteFromDocsRel(zhRel); + const zhISO = gitLastCommitISO(zhAbs); + + for (const locale of LOCALES) { + const tRoute = "/" + locale + (zhRoute === "/" ? "/" : zhRoute); + if (!data[tRoute]) { + data[tRoute] = { + locale, + zhRoute, + translated: false, + stale: true, + tLastUpdated: null, + zhLastUpdated: zhISO, + }; + } + } +} + +fs.mkdirSync(OUT_DIR, { recursive: true }); +fs.writeFileSync(OUT_FILE, JSON.stringify(data, null, 2), "utf8"); +console.log(`Generated i18n status -> ${OUT_FILE}`);
+ {{ text.title }} +
+ + {{ text.body }} + + + This page is not translated yet. Please refer to the Chinese original. + + + + {{ text.go }} + +