博客

LunariaJS CI/CD 集成:自动化你的本地化流程

学习将 LunariaJS 集成到 GitHub Actions、GitLab CI 等 CI/CD 平台,实现翻译状态自动检查、仪表板自动构建发布,让本地化流程完全自动化。

LibDoc Team 2026年3月6日 LunariaJS 专栏 72 分钟阅读
#LunariaJS #CI/CD #GitHub Actions #自动化 #持续集成

LunariaJS CI/CD 集成:自动化你的本地化流程

在前面的文章中,我们学习了 LunariaJS 的核心功能、Git 工作流和 Astro Starlight 集成。今天,让我们将本地化工作流提升到一个新的高度 —— 通过 CI/CD 实现完全自动化。

💡 官方文档LunariaJS 中文文档 - CI/CD

CI/CD 集成的价值

为什么需要自动化?

在没有 CI/CD 集成的情况下,本地化管理存在以下问题:

问题影响
手动检查翻译状态费时费力,容易遗漏
仪表板更新不及时团队看不到最新状态
翻译问题发现滞后发布后才发现翻译缺失
缺乏自动化通知贡献者不知道需要翻译

持续本地化概念

持续本地化(Continuous Localization) 是将翻译工作融入 CI/CD 流水线的实践:

代码提交 → 自动检测翻译变更 → 更新仪表板 → 通知相关人员 → 发布

核心价值

  • 即时反馈:每次提交都知道翻译状态
  • 自动化:减少手动操作,提高效率
  • 可视化:团队随时了解翻译进度
  • 质量保障:发布前确保翻译完整

GitHub Actions 集成

基础 Workflow 配置

创建 .github/workflows/lunaria.yml

name: Lunaria Dashboard

on:
  push:
    branches: [main]
    paths:
      - 'docs/**'
      - 'i18n/**'
      - 'lunaria.config.json'
  pull_request:
    branches: [main]
  workflow_dispatch:  # 手动触发

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 获取完整 Git 历史

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build Lunaria Dashboard
        run: npx lunaria build

      - name: Upload dashboard artifact
        uses: actions/upload-artifact@v4
        with:
          name: lunaria-dashboard
          path: lunaria-dashboard/
          retention-days: 7

自动发布到 GitHub Pages

将仪表板自动部署到 GitHub Pages:

name: Deploy Lunaria Dashboard

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build main site
        run: npm run build

      - name: Build Lunaria Dashboard
        run: npx lunaria build

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: './dist'

      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4

PR 翻译状态检查

在 Pull Request 中自动检查翻译状态:

name: Translation Check

on:
  pull_request:
    branches: [main]

jobs:
  check-translations:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build Lunaria Dashboard
        run: npx lunaria build --silent

      - name: Check for missing translations
        id: check
        run: |
          # 解析仪表板数据,检查是否有新增的缺失翻译
          MISSING=$(node scripts/check-new-missing.js)
          echo "missing=$MISSING" >> $GITHUB_OUTPUT

      - name: Comment on PR
        if: steps.check.outputs.missing != ''
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## 🌙 Translation Status Update\n\n${{ steps.check.outputs.missing }}`
            })

完整的 GitHub Actions 配置

# .github/workflows/i18n.yml
name: i18n Automation

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    # 每周一早上 9 点生成报告
    - cron: '0 9 * * 1'
  workflow_dispatch:

env:
  NODE_VERSION: '20'

jobs:
  # Job 1: 检查翻译状态
  check:
    name: Check Translation Status
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build Lunaria Dashboard
        run: npx lunaria build

      - name: Generate status report
        id: report
        run: |
          # 生成状态报告
          REPORT=$(node scripts/generate-report.js)
          echo "report<<EOF" >> $GITHUB_OUTPUT
          echo "$REPORT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Upload dashboard
        uses: actions/upload-artifact@v4
        with:
          name: lunaria-dashboard
          path: lunaria-dashboard/

    outputs:
      report: ${{ steps.report.outputs.report }}

  # Job 2: 发布仪表板(仅 main 分支)
  deploy:
    name: Deploy Dashboard
    runs-on: ubuntu-latest
    needs: check
    if: github.ref == 'refs/heads/main'

    permissions:
      contents: read
      pages: write
      id-token: write

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - name: Download dashboard
        uses: actions/download-artifact@v4
        with:
          name: lunaria-dashboard
          path: ./dashboard

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Upload to Pages
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./dashboard

      - name: Deploy
        id: deployment
        uses: actions/deploy-pages@v4

  # Job 3: 发送通知(定时任务)
  notify:
    name: Send Weekly Report
    runs-on: ubuntu-latest
    needs: check
    if: github.event_name == 'schedule'

    steps:
      - name: Send Slack notification
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "🌙 Weekly Translation Report",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "${{ needs.check.outputs.report }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

GitLab CI 集成

基础配置

创建 .gitlab-ci.yml

# .gitlab-ci.yml
stages:
  - check
  - build
  - deploy

variables:
  NODE_VERSION: '20'

# 缓存配置
.node-cache:
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/

# 检查翻译状态
lunaria-check:
  stage: check
  extends: .node-cache
  image: node:${NODE_VERSION}
  script:
    - npm ci
    - npx lunaria build
  artifacts:
    paths:
      - lunaria-dashboard/
    expire_in: 1 week
  only:
    - merge_requests
    - main

# 构建并部署
lunaria-deploy:
  stage: deploy
  image: node:${NODE_VERSION}
  needs:
    - lunaria-check
  script:
    - echo "Deploying Lunaria dashboard..."
    # 添加你的部署脚本
  only:
    - main

与 GitLab Pages 集成

# .gitlab-ci.yml
image: node:20

stages:
  - deploy

pages:
  stage: deploy
  script:
    - npm ci
    - npm run build
    - npx lunaria build
    - mv lunaria-dashboard public
  artifacts:
    paths:
      - public
  only:
    - main

GitLab CI 与 GitHub Actions 对比

功能GitHub ActionsGitLab CI
获取完整历史fetch-depth: 0GIT_DEPTH: 0
产物上传upload-artifactartifacts.paths
部署到 Pagesdeploy-pagespages job
Secretssecrets.VARIABLEvariables.VARIABLE
条件执行if:only:/except:

自动化任务设计

翻译完整性检查

创建自定义检查脚本 scripts/check-new-missing.js

// scripts/check-new-missing.js
const fs = require('fs');
const path = require('path');

function checkMissingTranslations() {
  // 读取 LunariaJS 生成的状态数据
  const statusPath = path.join('lunaria-dashboard', 'data', 'status.json');

  if (!fs.existsSync(statusPath)) {
    console.log('No status file found');
    return '';
  }

  const status = JSON.parse(fs.readFileSync(statusPath, 'utf8'));

  // 找出缺失的翻译
  const missing = [];
  for (const [lang, data] of Object.entries(status.languages)) {
    if (data.missing > 0) {
      missing.push({
        language: lang,
        count: data.missing,
        files: data.missingFiles || []
      });
    }
  }

  if (missing.length === 0) {
    return '✅ All translations are up to date!';
  }

  // 生成报告
  let report = '### ⚠️ Missing Translations Found\n\n';
  for (const item of missing) {
    report += `- **${item.language}**: ${item.count} missing files\n`;
  }
  report += '\nPlease check the [Lunaria Dashboard](/i18n-status/) for details.';

  return report;
}

console.log(checkMissingTranslations());

过时翻译告警

检查过时的翻译并发出警告:

// scripts/check-outdated.js
const { execSync } = require('child_process');

function getOutdatedTranslations() {
  // 获取最近 7 天内更新的源文件
  const recentChanges = execSync(
    'git log --since="7 days ago" --name-only --pretty=format: -- docs/en/',
    { encoding: 'utf8' }
  );

  const changedFiles = recentChanges
    .split('\n')
    .filter(f => f.trim())
    .map(f => f.replace('docs/en/', '').replace('.md', ''));

  if (changedFiles.length === 0) {
    return { hasOutdated: false, files: [] };
  }

  return {
    hasOutdated: true,
    files: changedFiles,
    message: `⚠️ The following files were updated in the last 7 days and may need translation updates:\n${changedFiles.map(f => `- ${f}`).join('\n')}`
  };
}

const result = getOutdatedTranslations();
if (result.hasOutdated) {
  console.log(result.message);
  process.exit(1);  // 非 0 退出码会使 CI 失败
} else {
  console.log('✅ No recent source changes requiring translation updates.');
}

仪表板自动发布

在构建后自动发布仪表板:

- name: Publish Dashboard
  run: |
    # 复制仪表板到发布目录
    cp -r lunaria-dashboard ./dist/i18n-status

    # 更新部署状态
    echo "Dashboard published to /i18n-status/"

通知与报告

Slack 通知

- name: Notify Slack
  if: always()
  uses: slackapi/slack-github-action@v1
  with:
    channel-id: 'C0123456789'
    payload: |
      {
        "text": "Lunaria Dashboard Update",
        "attachments": [
          {
            "color": "${{ job.status == 'success' && 'good' || 'danger' }}",
            "fields": [
              {
                "title": "Status",
                "value": "${{ job.status }}",
                "short": true
              },
              {
                "title": "Branch",
                "value": "${{ github.ref_name }}",
                "short": true
              },
              {
                "title": "Dashboard URL",
                "value": "<https://your-org.github.io/your-repo/i18n-status/|View Dashboard>",
                "short": false
              }
            ]
          }
        ]
      }
  env:
    SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

PR 评论机器人

在 PR 中自动评论翻译状态:

- name: Comment on PR
  uses: actions/github-script@v7
  with:
    script: |
      const status = require('./lunaria-dashboard/data/status.json');

      let body = '## 🌙 Translation Status\n\n';
      body += '| Language | Done | Outdated | Missing | Progress |\n';
      body += '|----------|------|----------|---------|----------|\n';

      for (const [lang, data] of Object.entries(status.languages)) {
        const progress = Math.round((data.done / data.total) * 100);
        const emoji = progress >= 90 ? '✅' : progress >= 70 ? '⚠️' : '❌';
        body += `| ${lang} | ${data.done} | ${data.outdated} | ${data.missing} | ${emoji} ${progress}% |\n`;
      }

      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: body
      });

邮件报告

定期发送邮件报告:

- name: Send Email Report
  if: github.event_name == 'schedule'
  uses: dawidd6/action-send-mail@v3
  with:
    server_address: smtp.gmail.com
    server_port: 465
    username: ${{ secrets.EMAIL_USERNAME }}
    password: ${{ secrets.EMAIL_PASSWORD }}
    subject: Weekly Translation Report - ${{ github.repository }}
    to: team@example.com
    from: CI Bot
    body: |
      Hi Team,

      Here's the weekly translation status report:

      ${{ needs.check.outputs.report }}

      View the full dashboard: https://your-org.github.io/your-repo/i18n-status/

      Best regards,
      CI Bot

CI/CD 最佳实践

1. 条件触发

只在相关文件变更时触发:

on:
  push:
    branches: [main]
    paths:
      - 'docs/**'
      - 'i18n/**'
      - 'lunaria.config.json'
      - '.github/workflows/lunaria.yml'

2. 缓存优化

使用缓存加速构建:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # 启用 npm 缓存

- name: Cache Lunaria output
  uses: actions/cache@v4
  with:
    path: .lunaria-cache
    key: lunaria-${{ hashFiles('docs/**', 'i18n/**') }}

3. 失败策略

设置合理的失败策略:

jobs:
  check:
    continue-on-error: true  # 翻译检查失败不阻止部署

  deploy:
    needs: check
    if: always() && needs.check.result == 'success'

4. 并行执行

并行执行多个检查任务:

jobs:
  check-missing:
    # ...

  check-outdated:
    # ...

  report:
    needs: [check-missing, check-outdated]
    # ...

5. 安全最佳实践

# 使用最小权限
permissions:
  contents: read
  pull-requests: write

# 不要暴露 secrets
env:
  DASHBOARD_URL: ${{ secrets.DASHBOARD_URL }}  # ❌ 不要在日志中暴露

总结

通过 CI/CD 集成,LunariaJS 可以实现完全自动化的本地化工作流:

功能实现
自动检测翻译变更GitHub Actions / GitLab CI
自动构建仪表板每次提交触发
自动发布GitHub Pages / GitLab Pages
自动通知Slack / Email / PR 评论
定期报告Scheduled jobs

关键要点

  • 使用 fetch-depth: 0 获取完整 Git 历史
  • 配置条件触发减少不必要的构建
  • 利用缓存加速 CI 运行
  • 设置合理的通知机制

下一步

恭喜你完成了 LunariaJS 入门系列!从下一篇文章开始,我们将进入深度篇,深入探讨:

  • 第 8 篇:@lunariajs/core 源码解析(8,000-15,000 字)
  • 第 9 篇:高级配置与自定义策略(8,000-15,000 字)
  • 第 10 篇:完整企业级实战(8,000-15,000 字)

敬请期待!


💡 推荐阅读