跳至正文
Acecore
目录
只需发布一篇日语文章,即可运营9种语言博客的方法

直接说结论:在这个网站上,只需在 Pages CMS 中发布一篇日语文章,就能逐步让该文章以日语 + 8种其他语言的形式呈现。GitHub Actions 和 GitHub Copilot 负责处理翻译 Issue 创建、翻译 PR 创建、构建、自动合并以及关闭父 Issue。

运营人员日常只需处理日语文章和作者信息。由于不再需要每次手动提交翻译任务或整理 PR,多语言博客的运营负担可以大幅减轻。

此方法的前提条件

此方法假设 Astro 侧已具备以下基础设施。

  • 9种语言路由(ja, en, zh-cn, es, pt, fr, ko, de, ru)
  • 对于没有翻译的页面,可以回退显示日语内容
  • 可以通过 Pages CMS 更新日语文章和作者信息的运营设置

关于如何搭建这套基础设施,请参阅将 Astro 6 网站支持9种语言 — 136篇博客文章的自动翻译与多语言架构。本文仅聚焦于如何在此基础上叠加 Copilot 自动翻译工作流。

能实现什么

从运营角度来看,日常需要操作的界面只有2个。本文直接使用 Pages CMS 的界面,让日常运营中需要操作哪些界面一目了然。

Pages CMS 日语博客列表界面

第一个界面是 Pages CMS 的日语博客列表。在这里可以查看发布日期和作者信息,同时只添加或更新日语文章。关键是保持”只接触翻译源日语”的模式,无需每次都进入每种语言的编辑界面。

Pages CMS 作者信息表单界面

第二个界面是作者信息表单。对于作者数据,只需在 CMS 中更新日语基础字段,翻译用的 i18n 字段由 GitHub 自动化流程处理,这样运营职责的分离就会非常清晰。

此方法最适合的场景

首先,以下类型的团队和网站特别适合使用此方法。

  • 希望以日语作为翻译源
  • 博客基于 Markdown 进行管理
  • 每次手动提交翻译任务很麻烦
  • 可以在一定程度上将翻译质量交给 AI
  • 但希望阻止构建失败或保持草稿状态的 PR

反之,如果各语言有完全独立的编辑体制,其他工作流可能更合适。

Step 1. 将日语文章固定为翻译源

首先需要决定的是”以哪个文件作为翻译源”。这里模糊不清会破坏自动化。

本文中所说的翻译源是指最先编辑、作为各语言文章和派生数据基准的日语文件

在这套配置中,源文件和目标文件的划分如下。

  • 博客文章翻译源:src/content/blog/{slug}.md
  • 博客文章翻译目标:src/content/blog/{locale}/{slug}.md
  • 作者信息翻译源:src/content/authors/{authorId}.json
  • 作者信息翻译目标:src/content/authors/{authorId}.json 中的 i18n 字段
  • 标签定义翻译源:src/content/tags/{tagId}.json
  • 标签定义翻译目标:src/content/tags/{tagId}.json 中的 i18n 字段

目录结构大致如下,比较容易管理。

src/content/blog/
  my-post.md
  another-post.md
  en/
    my-post.md
  zh-cn/
    my-post.md
  fr/
    my-post.md

关键是将翻译文件的 slug 与源日语文章的 slug 保持一致。仅此一点就能轻松从源路径自动识别翻译目标。

在这个仓库中,即使翻译文件尚不存在,每个语言的 URL 也会通过日语回退生成。这意味着可以采用”先发布日语文章,之后让翻译 PR 跟进”的运营方式。

Step 2. 将日语文章的推送转换为翻译 Issue

下一步是使用 GitHub Actions 检测日语文章的变更,并自动创建翻译 Issue。

最低要求如下。

  • 监控对 main 分支的推送
  • 仅将 src/content/blog/*.md 作为常规自动创建 Issue 的目标
  • 只在文章正文发生变化时创建 Issue,而不是仅 frontmatter 变化时
  • 如果存在相同源路径的开放 Issue,则更新而不是创建新 Issue
  • 在 Issue 正文中嵌入源路径作为标记

作者信息和标签定义虽然也是翻译目标,但在普通推送时不自动创建 Issue。仅在必要时通过 workflow_dispatch 明确触发,可以避免不必要的 Issue 积累。

例如,在 Issue 正文中加入这样的注释,可以在后续自动化中复用。

<!-- translation-source:src/content/blog/my-post.md -->
<!-- translation-kind:blog-post -->

workflow 侧的基本过滤配置如下。

on:
  push:
    branches:
      - main
    paths:
      - src/content/blog/*.md

此外,通过仅比较 Markdown 正文来决定是否创建翻译 Issue,可以避免因更新发布日期或标签等小修改而大量生成翻译 Issue 的问题。

这里重要的是不要”直接创建翻译”,而是先创建 Issue。通过插入 Issue,可以将源路径、目标语言和翻译条件以人和 AI 都可见的形式固定下来。

Step 3. 将翻译 Issue 自动分配给 Copilot

仅创建 Issue 还留有手动工作,因此这一步将 Issue 自动分配给 Copilot。

需要做2件事。

  1. COPILOT_AGENT_TOKEN 添加为仓库 secret
  2. 在创建 Issue 后调用分配 API

概念上是通过 patch Issue 将 Copilot 设置为受让人。

{
  "assignees": ["copilot-swe-agent[bot]"],
  "agent_assignment": {
    "target_repo": "OWNER/REPO",
    "base_branch": "main",
    "custom_instructions": "Translate the Japanese source article..."
  }
}

此时,将常规自动创建范围仅限于文章,仅在必要时通过手动 dispatch 处理作者信息和标签定义,可以使运营更加稳定。明确说明规则 — 作者信息的 i18n 字段位于 src/content/authors/{authorId}.json,标签定义的 i18n.name 字段位于 src/content/tags/{tagId}.json,文章则在 src/content/blog/{locale}/ 下创建同名文件 — 可以减少错误。

Step 4. 构建翻译 PR 并自动合并

这里不应无条件自动化。建议仅将满足以下所有条件的 PR 作为合并目标。

  • 由 Copilot 创建的 PR
  • 标题以 [translation] 开头
  • 目标为 main 分支
  • 非草稿状态
  • 构建成功

在这套配置中,流程分为2个阶段。

  1. Translation PR Build
  2. Merge Translation PR

在 PR 变为 ready for review 时构建 PR head,成功后直接进行 squash merge。由于不依赖 GitHub 的分支保护,即使在小型仓库中也易于管理。

自动合并应强制执行的条件

添加自动合并时,以下是最低推荐条件。

  • 排除非翻译 PR
  • 构建失败时停止
  • 草稿状态时停止
  • 排除非 Copilot 创建的 PR

有了这4个条件,就可以大大避免将普通开发 PR 纳入自动合并范围的问题。

Step 5. 合并后自动关闭父翻译 Issue

最后,加入以下机制可以使运营更加整洁:合并后自动关闭父 Issue。

方法很简单,对已合并的翻译 PR 执行以下操作。

  1. 获取 PR 的变更文件
  2. 同时读取 PR 正文中的源路径
  3. 搜索与 translation-source: 标记对应的开放 Issue
  4. 添加评论并关闭

同时查看 PR 正文源路径的原因是,仅依赖 Copilot 创建的 PR 的变更文件,在某些情况下源文件的反向查找可能不够稳定。同时使用变更文件和 PR 正文可以保持稳定性。

补充说明

将 Copilot 创建的 PR 和 Issue 的语言引导为日语

如果希望在 GitHub 侧稳定 Copilot 的输出语言,使用仓库级别的指令是最直接的方法。

即,放置 .github/copilot-instructions.md 文件。

This repository is an Astro static site for Acecore, deployed on Cloudflare Pages.

- For GitHub issues, pull requests, issue comments, pull request descriptions, review summaries, and other user-facing GitHub text, write in Japanese by default unless the task explicitly requires another language.
- For multilingual content work, treat Japanese source files as canonical and keep translated frontmatter aligned with the Japanese source.

仅凭这一个文件,Copilot coding agent 在创建 Issue 和 PR 时的默认语言和上下文就会相当稳定。

总结

这套配置的核心是将翻译从”每次都需要人工请求的工作”转变为从属于日语源文件推送的例行处理

再次总结流程如下。

  1. 只撰写日语文章
  2. 推送时自动创建翻译 Issue
  3. 自动分配给 Copilot
  4. 构建翻译 PR 并自动合并
  5. 自动关闭父 Issue

完成这套配置后,从运营角度来看感觉相当自然。只需推送日语文章,其他语言的文章就会在 GitHub 侧依次生成

当然,实际上会经历 Issue 创建、Copilot 执行、PR 创建、构建和合并等异步步骤,所以并非”瞬间全部完成”。但是,运营人员不再需要每次手动提交翻译任务或忘记关闭 PR。

本文本身也构建成可以以日语版为基准流入这一流程的结构。如果要持续运营多语言网站,首先从这种程度的自动化开始是最合适的。

从1篇日语文章到9种语言运营的流程

更新日语源文件

仅通过 Pages CMS 或 Markdown 编辑日语文章,并推送到 main 分支。

自动创建翻译 Issue

GitHub Actions 创建内嵌了源路径和目标语言的 Issue。

Copilot 创建翻译 PR

收到 Issue 后,Copilot 生成翻译文件并提交翻译 PR。

构建、合并并关闭 Issue

构建成功后自动合并,并自动关闭父翻译 Issue。

手动与自动翻译工作流对比

手动翻译工作流

  • 文章发布后由人工创建翻译任务
  • 按语言分配负责人
  • 构建和合并决策也由人工完成
  • 父 Issue 容易被遗忘,长期未关闭

自动翻译工作流

  • 推送日语文章即触发整个流程
  • 自动分配给 Copilot
  • 翻译 PR 在构建成功后自动合并
  • 合并后父 Issue 也自动关闭
开始前需要准备的内容
  • 已完成: 以日语为翻译源的内容结构
  • 已完成: 如 src/content/blog/{locale}/{slug}.md 的翻译文件布局规则
  • 已完成: 具有 issues write 权限的 GitHub Actions
  • 已完成: 能够调用 Copilot 分配 API 的 COPILOT_AGENT_TOKEN
  • 已完成: 稳定的构建命令,如 npm run build
常见问题
只需推送日语文章,其他语言的文章就会自动生成吗?
是的。当前的 Acecore 网站支持9种语言 — `ja`、`en`、`zh-cn`、`es`、`pt`、`fr`、`ko`、`de`、`ru` — 因此推送日语文章可以触发为其余8种语言创建翻译 Issue、分配给 Copilot、创建翻译 PR、构建、自动合并和关闭 Issue 的完整流程。即使翻译文件尚不存在,每个语言的 URL 也会通过日语回退提供服务,因此可以先发布日语版,之后再替换为真正的翻译内容。
为什么不直接创建 PR,而要先创建 Issue?
因为可以在 Issue 中固定源路径、目标语言和翻译条件。这使得重新运行、历史查看和失败恢复变得更加容易。
自动合并不危险吗?
无条件自动合并是危险的。通过将范围限定为仅翻译 PR — 要求 Copilot 创建的 PR、标题以 [translation] 开头、构建成功且非草稿 — 可以使其相当安全。
G

Gui

Acecore 代表。从系统开发、Web制作、基础设施运维到IT教育,涉猎广泛的工程师。 喜欢用技术解决人和组织的课题。

系统开发 Web制作 基础设施运维 IT教育

想了解更多关于我们的服务?

我们提供系统开发、网站设计、平面设计、IT教育等全方位支持。

相关文章

搜索文章