管理多语言内容对于全球覆盖至关重要。TinaCMS提供了多种灵活的选项来促进这一点。本指南重点介绍两种主要策略:
使用基于目录的本地化时,您的内容应以目录为基础进行结构化,其中语言环境(例如,en,fr)位于集合根目录(例如,blog,docs)下。例如:
.├── /blog/│ ├── /en/│ │ └── hello-world.md│ └── /fr/│ └── hello-world.md└── /docs/├── /en/│ └── my-doc.md└── /fr/└── my-doc.md
在您的config.ts中,您可能会有一个包含所有语言环境的集合
export const config = {collections: [{label: 'Blog',name: 'blog',path: 'content/blog',// ...其他设置},}
无论您使用的是Next.js还是其他框架,您的路由逻辑都应更新,以根据URL或用户设置选择正确的语言环境。
// 示例:在NextJS中获取页面列表const getStaticPaths = async({ locales }) {// ...})
使用语言环境,您可以根据路径筛选文档
// /pages/post/[filename].tsx// `locale`与`params`一起提供const getStaticProps = async({ params, locale }) {const tinaProps = await client.BlogPostQuery({// 组合`relativePath`,其中`locale`是`post`的子文件夹relativePath: `${locale}/${params.filename}.mdx`,});return {props: {...tinaProps}}}
有关设置NextJS特定实现路由的更多信息,请参阅我们的指南
通过此设置,编辑者将通过文档列表浏览每个集合的语言环境。
如果用户想要创建现有文档的新本地化版本,他们可以从文档列表中点击“复制文档”,并在新文档的文件名中添加所需的语言环境。
在这种方法中,每个本地化字段包含多个语言的嵌套值。例如,单个Markdown文件可能如下所示:
{"title": {"en": "Hello","fr": "Bonjour"}}
您需要修改您的TinaCMS架构以包含本地化字段。
export const pageSchema = {label: 'Page',name: 'page',fields: [{label: 'Title',name: 'title',type: 'object',fields: [{type: 'string',name: 'en',label: 'English',},{type: 'string',name: 'fr',label: 'French',},],},// ...其他字段],};
注意:如果您使用的是markdown/mdx内容,并希望使用markdown正文作为您的内容,您可能更倾向于使用基于目录的本地化方法。
在您网站的组件中,您可以根据当前语言环境选择正确的本地化字段进行显示。
const PageComponent = ({ data, locale }) => {const title = data.title[locale];// ...显示内容};
TinaCMS将所有本地化字段显示为根级字段的子字段。
使用基于目录的本地化时,开发人员需要将相应语言的MDX或JSON文件放置在正确的位置以加载多语言路由。开发人员可以选择手动翻译文本内容并将其放置在正确的位置,或者他们可以使用GitHub Action来自动化此过程。以下是一个解释有效GitHub Action工作流程的示例:
必须为此GitHub Action设置触发条件。通常,触发发生在PR成功合并时。为了减少令牌消耗,计划触发是一个替代方案。下面的示例显示了一个仅在PR合并并包含指定目录中的更改时激活的GitHub Action。
# 仅在PR成功关闭并包含指定目录中的更改后触发on:pull_request:types: [closed]paths:- 'content/docs/**.mdx'jobs:translate-mdx:if: github.event.pull_request.merged == true
为了最小化令牌使用,仅识别修改的文件进行翻译是必要的。下面是一个列出所有修改文件的简单示例。
// 识别当前PR下的修改目标文件async function getChangedFilesFromApi() {try {const response = await axios.get(`https://api.github.com/repos/${OWNER}/${REPO_NAME}/pulls/${PR_NUMBER}/files`,{headers: {Authorization: `token ${GITHUB_TOKEN}`,Accept: 'application/vnd.github.v3+json',},});const mdxFiles = response.data.filter((file) =>file.filename.startsWith('content/docs/') &&file.filename.endsWith('.mdx')).map((file) => file.filename);return mdxFiles;} catch (error) {console.error('从API获取更改文件时出错:', error.message);return [];}}
创建一个单独的分支以区分原始分支和新添加的翻译是必要的。这种方法有助于手动审核。
# 为翻译文件创建一个单独的分支- name: Create translation branchif: env.HAS_CHANGED_FILES == 'true'run: |TIMESTAMP=$(date +%s)PR_NUMBER="${{ github.event.pull_request.number }}"BRANCH_NAME="translate-pr-$PR_NUMBER-$TIMESTAMP"git checkout -b $BRANCH_NAMEecho "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
外部LLM用于自动翻译。翻译质量和格式完整性取决于所选模型和提示。下面是一个将MDX文件翻译成中文的英文提示示例,同时保留文档接口和关键信息。
Please translate the following Markdown content into Chinese, preserving all Markdown formatting, code blocks, and front matter metadata.Only translate the text content, do not modify code, variable names, or other technical content.Do not add ```markdown``` in the newly translated Markdown file.Please note the following special requirements:1. Do not modify URLs, next and preview fields2. Maintain the original format structure, including heading hierarchy, lists, tables, etc.3. Comments in code blocks can be translated, but the code itself and its functionality should not be changed4. Please return the translated markdown source code directly in your reply, without adding any other prompts or explanations{{content}}
由LLM生成的内容不能被视为完全可靠。将所有更改提交到新的PR以进行彻底验证代表了一种更谨慎和系统的方法。
const prTitle = `Chinese translation for PR #${PR_NUMBER}`;const prBody = `This PR contains Chinese translations for the documentation files updated in PR (${SERVER_URL}/${REPO}/pull/${PR_NUMBER}).`;const response = await axios.post(`https://api.github.com/repos/${OWNER}/${REPO_NAME}/pulls`,{title: prTitle,body: prBody,head: BRANCH_NAME,base: 'main',},{headers: {Authorization: `token ${GITHUB_TOKEN}`,Accept: 'application/vnd.github.v3+json',},});