Skip to content

Commit bc2b48a

Browse files
acedatacloud-devGermey
authored andcommitted
fix: read docs from zh-CN subdir, use PlatformBackend translations instead of LLM
- sync.py now reads from PlatformBackend/docs/zh-CN/ (was PlatformBackend/docs/) - Added Step 6b: copies pre-translated content from PlatformBackend/docs/{lang}/ for all 17 target languages (en, ja, ko, etc.) - translate_content.py: added --root-only mode for just root MDX files - docs.json now includes all 18 languages - Workflow uses --root-only for LLM translation (only intro/quickstart/auth) - Eliminates expensive LLM translation calls for PlatformBackend-sourced content
1 parent 5c8b7bc commit bc2b48a

3 files changed

Lines changed: 208 additions & 21 deletions

File tree

.github/workflows/sync.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ on:
66
workflow_dispatch:
77
inputs:
88
force:
9-
description: 'Force full regeneration'
9+
description: "Force full regeneration"
1010
required: false
11-
default: 'false'
11+
default: "false"
1212
type: boolean
1313
skip_translate:
14-
description: 'Skip translation step'
14+
description: "Skip translation step"
1515
required: false
16-
default: 'false'
16+
default: "false"
1717
type: boolean
1818
schedule:
19-
- cron: '0 6 * * *' # Daily at 6:00 UTC
19+
- cron: "0 6 * * *" # Daily at 6:00 UTC
2020

2121
permissions:
2222
contents: write
@@ -45,7 +45,7 @@ jobs:
4545
- name: Set up Python
4646
uses: actions/setup-python@v5
4747
with:
48-
python-version: '3.12'
48+
python-version: "3.12"
4949

5050
- name: Restore cache
5151
uses: actions/cache@v4
@@ -87,7 +87,10 @@ jobs:
8787
run: |
8888
echo "::group::Translation output"
8989
cd Docs
90-
python -u scripts/translate_content.py --output-dir .
90+
# Only translate root-level MDX files (intro, quickstart, auth) and
91+
# generated static pages that don't come from PlatformBackend.
92+
# PlatformBackend translations are already copied by sync.py step 6b.
93+
python -u scripts/translate_content.py --output-dir . --root-only
9194
echo "::endgroup::"
9295
9396
- name: Check for changes

scripts/sync.py

Lines changed: 168 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def log(msg: str = ""):
2828
elapsed = _time.time() - _T0
2929
print(f"[{elapsed:6.1f}s] {msg}", flush=True)
3030

31+
3132
# ---------------------------------------------------------------------------
3233
# Config: display order + icons for categories, docs to skip.
3334
# Everything else is data-driven from service_api_mapping.json.
@@ -159,7 +160,7 @@ def build_doc_service_map(
159160
)
160161

161162
doc_map: dict[str, Optional[str]] = {}
162-
docs_dir = backend_dir / "docs"
163+
docs_dir = backend_dir / "docs" / "zh-CN"
163164

164165
for md_file in sorted(docs_dir.glob("development_*.md")):
165166
doc_key = md_file.stem.removeprefix("development_")
@@ -591,14 +592,19 @@ def _copy_seo_pages(docs_dir: Path, output_dir: Path):
591592
log(f" WARNING: {docs_dir} not found, skipping SEO pages")
592593
return
593594

594-
counts: dict[str, int] = {"tutorials": 0, "comparisons": 0, "use-cases": 0, "blog": 0}
595+
counts: dict[str, int] = {
596+
"tutorials": 0,
597+
"comparisons": 0,
598+
"use-cases": 0,
599+
"blog": 0,
600+
}
595601

596602
for src in sorted(docs_dir.glob("*.md")):
597603
name = src.stem # e.g. tutorial_claude_python
598604

599605
if name.startswith("tutorial_"):
600606
# tutorial_{service}_{lang}.md → tutorials/{service}/{lang}.mdx
601-
rest = name[len("tutorial_"):] # e.g. claude_python or nano-banana_curl
607+
rest = name[len("tutorial_") :] # e.g. claude_python or nano-banana_curl
602608
# Find the last _ that separates service from lang
603609
for lang in ("python", "javascript", "curl"):
604610
suffix = f"_{lang}"
@@ -611,21 +617,21 @@ def _copy_seo_pages(docs_dir: Path, output_dir: Path):
611617
break
612618

613619
elif name.startswith("comparison_"):
614-
slug = name[len("comparison_"):]
620+
slug = name[len("comparison_") :]
615621
dst = output_dir / "comparisons" / f"{slug}.mdx"
616622
dst.parent.mkdir(parents=True, exist_ok=True)
617623
_write_seo_mdx(src, dst)
618624
counts["comparisons"] += 1
619625

620626
elif name.startswith("use_case_"):
621-
slug = name[len("use_case_"):]
627+
slug = name[len("use_case_") :]
622628
dst = output_dir / "use-cases" / f"{slug}.mdx"
623629
dst.parent.mkdir(parents=True, exist_ok=True)
624630
_write_seo_mdx(src, dst)
625631
counts["use-cases"] += 1
626632

627633
elif name.startswith("blog_"):
628-
slug = name[len("blog_"):]
634+
slug = name[len("blog_") :]
629635
dst = output_dir / "blog" / f"{slug}.mdx"
630636
dst.parent.mkdir(parents=True, exist_ok=True)
631637
_write_seo_mdx(src, dst)
@@ -672,8 +678,6 @@ def _write_seo_mdx(src: Path, dst: Path):
672678
dst.write_text(mdx, encoding="utf-8")
673679

674680

675-
676-
677681
def _build_simple_nav(directory: str, output_dir: Path) -> list[str]:
678682
"""Auto-discover MDX pages in a flat directory."""
679683
d = output_dir / directory
@@ -765,12 +769,16 @@ def build_navigation(
765769
# Tab 4: 对比
766770
cmp_pages = _build_simple_nav("comparisons", output_dir)
767771
if cmp_pages:
768-
tabs.append({"tab": "对比", "groups": [{"group": "服务对比", "pages": cmp_pages}]})
772+
tabs.append(
773+
{"tab": "对比", "groups": [{"group": "服务对比", "pages": cmp_pages}]}
774+
)
769775

770776
# Tab 5: 用例
771777
uc_pages = _build_simple_nav("use-cases", output_dir)
772778
if uc_pages:
773-
tabs.append({"tab": "用例", "groups": [{"group": "应用场景", "pages": uc_pages}]})
779+
tabs.append(
780+
{"tab": "用例", "groups": [{"group": "应用场景", "pages": uc_pages}]}
781+
)
774782

775783
# Tab 6: 博客
776784
blog_pages = _build_simple_nav("blog", output_dir)
@@ -803,7 +811,9 @@ def build_navigation(
803811
if (output_dir / "resources" / "support.mdx").exists():
804812
resource_pages.append("resources/support")
805813
if resource_pages:
806-
tabs.append({"tab": "资源", "groups": [{"group": "资源", "pages": resource_pages}]})
814+
tabs.append(
815+
{"tab": "资源", "groups": [{"group": "资源", "pages": resource_pages}]}
816+
)
807817

808818
return {
809819
"tabs": tabs,
@@ -824,6 +834,127 @@ def build_navigation(
824834
}
825835

826836

837+
# Target languages matching PlatformBackend/docs/ subdirectories
838+
TARGET_LANGUAGES = [
839+
"en",
840+
"zh-tw",
841+
"ja",
842+
"ko",
843+
"es",
844+
"fr",
845+
"de",
846+
"pt",
847+
"ru",
848+
"ar",
849+
"it",
850+
"fi",
851+
"sv",
852+
"el",
853+
"uk",
854+
"pl",
855+
"sr",
856+
]
857+
858+
# Mintlify expects specific language codes (may differ from directory names)
859+
_LANG_DIR_TO_MINTLIFY = {
860+
"zh-tw": "zh-TW",
861+
}
862+
863+
864+
def _mintlify_lang(lang_dir: str) -> str:
865+
"""Map PlatformBackend language directory name to Mintlify language code."""
866+
return _LANG_DIR_TO_MINTLIFY.get(lang_dir, lang_dir)
867+
868+
869+
def _sync_translated_content(
870+
backend_dir: Path,
871+
output_dir: Path,
872+
doc_service_map: dict[str, Optional[str]],
873+
service_names: dict[str, str],
874+
categories: dict[str, dict],
875+
dev_docs_by_service: dict[str, list[str]],
876+
):
877+
"""Copy pre-translated content from PlatformBackend/docs/{lang}/ to Mintlify
878+
language directories.
879+
880+
For each target language that has a directory in PlatformBackend/docs/,
881+
apply the same MD→MDX conversion as the zh-CN base, placing output into
882+
{output_dir}/{mintlify_lang}/guides/..., tutorials/..., etc.
883+
"""
884+
total_files = 0
885+
886+
for lang_dir in TARGET_LANGUAGES:
887+
lang_docs = backend_dir / "docs" / lang_dir
888+
if not lang_docs.is_dir():
889+
continue
890+
891+
mlang = _mintlify_lang(lang_dir)
892+
lang_out = output_dir / mlang
893+
lang_count = 0
894+
895+
# --- Development guides ---
896+
for md_file in sorted(lang_docs.glob("development_*.md")):
897+
doc_key = md_file.stem.removeprefix("development_")
898+
if doc_key.endswith("_title"):
899+
continue
900+
service_alias = doc_service_map.get(doc_key)
901+
if service_alias is None:
902+
continue
903+
content = md_file.read_text(encoding="utf-8")
904+
svc_name = service_names.get(service_alias, service_alias)
905+
mdx_content = convert_dev_doc_to_mdx(content, doc_key, svc_name)
906+
svc_dir = lang_out / "guides" / service_alias
907+
svc_dir.mkdir(parents=True, exist_ok=True)
908+
(svc_dir / f"{doc_key}.mdx").write_text(mdx_content, encoding="utf-8")
909+
lang_count += 1
910+
911+
# --- MCP docs ---
912+
for md_file in sorted(lang_docs.glob("mcp_*.md")):
913+
mcp_name = md_file.stem.removeprefix("mcp_")
914+
content = md_file.read_text(encoding="utf-8")
915+
mdx_content = generate_mcp_doc(content, mcp_name)
916+
mcp_out = lang_out / "mcp"
917+
mcp_out.mkdir(parents=True, exist_ok=True)
918+
(mcp_out / f"{mcp_name}.mdx").write_text(mdx_content, encoding="utf-8")
919+
lang_count += 1
920+
921+
# --- Extra docs (privacy, terms, support) ---
922+
for doc_key, out_rel in [
923+
("privacy", "resources/privacy.mdx"),
924+
("terms", "resources/terms.mdx"),
925+
("support", "resources/support.mdx"),
926+
]:
927+
src = lang_docs / f"{doc_key}.md"
928+
if src.exists():
929+
content = src.read_text(encoding="utf-8")
930+
mdx = generate_extra_doc(content, doc_key)
931+
dst = lang_out / out_rel
932+
dst.parent.mkdir(parents=True, exist_ok=True)
933+
dst.write_text(mdx, encoding="utf-8")
934+
lang_count += 1
935+
936+
# --- X402 guide ---
937+
x402_src = lang_docs / "x402_integration_guide.md"
938+
if x402_src.exists():
939+
content = x402_src.read_text(encoding="utf-8")
940+
mdx = convert_dev_doc_to_mdx(content, "x402_integration_guide", "x402")
941+
guides_out = lang_out / "guides"
942+
guides_out.mkdir(parents=True, exist_ok=True)
943+
(guides_out / "x402.mdx").write_text(mdx, encoding="utf-8")
944+
lang_count += 1
945+
946+
# --- SEO pages (tutorials, comparisons, use-cases, blog) ---
947+
_copy_seo_pages(lang_docs, lang_out)
948+
949+
total_files += lang_count
950+
if lang_count > 0:
951+
log(f" {mlang}: {lang_count} guide/mcp/resource pages (+ SEO pages)")
952+
953+
log(
954+
f" Total translated pages: {total_files} (across {len(TARGET_LANGUAGES)} languages)"
955+
)
956+
957+
827958
def main():
828959
parser = argparse.ArgumentParser(
829960
description="Sync PlatformBackend docs to Mintlify"
@@ -900,7 +1031,7 @@ def main():
9001031
log("Step 2/8: Convert development guides to MDX")
9011032
log("=" * 60)
9021033
dev_docs_by_service: dict[str, list[str]] = {} # alias → [doc_key, ...]
903-
docs_dir = backend_dir / "docs"
1034+
docs_dir = backend_dir / "docs" / "zh-CN"
9041035

9051036
for md_file in sorted(docs_dir.glob("development_*.md")):
9061037
doc_key = md_file.stem.removeprefix("development_")
@@ -1237,9 +1368,26 @@ def main():
12371368
log("=" * 60)
12381369
log("Step 6/8: Copy pre-generated SEO pages from PlatformBackend/docs")
12391370
log("=" * 60)
1240-
_copy_seo_pages(backend_dir / "docs", output_dir)
1371+
_copy_seo_pages(backend_dir / "docs" / "zh-CN", output_dir)
12411372
log("Step 6 done")
12421373

1374+
# ---------------------------------------------------------------------------
1375+
# 6b. Copy pre-translated content from PlatformBackend/docs/{lang}/
1376+
# ---------------------------------------------------------------------------
1377+
log()
1378+
log("=" * 60)
1379+
log("Step 6b: Copy pre-translated content from PlatformBackend")
1380+
log("=" * 60)
1381+
_sync_translated_content(
1382+
backend_dir,
1383+
output_dir,
1384+
doc_service_map,
1385+
service_names,
1386+
categories,
1387+
dev_docs_by_service,
1388+
)
1389+
log("Step 6b done")
1390+
12431391
# ---------------------------------------------------------------------------
12441392
# 7. Generate docs.json
12451393
# ---------------------------------------------------------------------------
@@ -1290,6 +1438,13 @@ def main():
12901438
{"language": "ru"},
12911439
{"language": "ar"},
12921440
{"language": "it"},
1441+
{"language": "fi"},
1442+
{"language": "sv"},
1443+
{"language": "el"},
1444+
{"language": "uk"},
1445+
{"language": "pl"},
1446+
{"language": "sr"},
1447+
{"language": "zh-TW"},
12931448
],
12941449
"navigation": navigation,
12951450
"footer": {

scripts/translate_content.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,29 @@ def collect_source_files(docs_dir: Path) -> list[Path]:
232232
return files
233233

234234

235+
def collect_root_files(docs_dir: Path) -> list[Path]:
236+
"""Collect only root-level MDX files (intro, quickstart, auth) plus
237+
generated static pages like api-reference/introduction and mcp/overview
238+
that are not sourced from PlatformBackend translations."""
239+
files: list[Path] = []
240+
for name in ROOT_FILES:
241+
p = docs_dir / name
242+
if p.exists():
243+
files.append(p)
244+
245+
# Also translate api-reference/introduction.mdx and mcp/overview.mdx
246+
# since these are generated by sync.py (not from PlatformBackend translations)
247+
for extra in [
248+
"api-reference/introduction.mdx",
249+
"mcp/overview.mdx",
250+
]:
251+
p = docs_dir / extra
252+
if p.exists():
253+
files.append(p)
254+
255+
return files
256+
257+
235258
def main():
236259
import argparse
237260

@@ -241,6 +264,9 @@ def main():
241264
parser.add_argument("--output-dir", type=Path, default=DOCS_DIR)
242265
parser.add_argument("--languages", nargs="*", default=None, help="Target languages (default: all)")
243266
parser.add_argument("--dry-run", action="store_true")
267+
parser.add_argument("--root-only", action="store_true",
268+
help="Only translate root MDX files (intro, quickstart, auth). "
269+
"Use when PlatformBackend translations handle guides/tutorials.")
244270
args = parser.parse_args()
245271

246272
api_key = get_api_key()
@@ -254,7 +280,10 @@ def main():
254280
print(f"Source: zh-CN (root)", flush=True)
255281
print(f"Targets: {', '.join(langs)}", flush=True)
256282

257-
src_files = collect_source_files(args.output_dir)
283+
if args.root_only:
284+
src_files = collect_root_files(args.output_dir)
285+
else:
286+
src_files = collect_source_files(args.output_dir)
258287
print(f"Source files: {len(src_files)}", flush=True)
259288

260289
if args.dry_run:

0 commit comments

Comments
 (0)