ブログ

LunariaJS CI/CD統合:ローカライズフローを自動化

LunariaJSをGitHub Actions、GitLab CIなどのCI/CDプラットフォームに統合し、翻訳状態自動チェック、ダッシュボード自動ビルド・公開を実現し、ローカライズフローを完全自動化する方法を学びます。

LibDoc Team 2026年3月6日 LunariaJS 連載 74 分で読める
#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統合

基本ワークフロー設定

.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:
  # ジョブ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 }}

  # ジョブ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

  # ジョブ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
Pagesへのデプロイdeploy-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文字)

お楽しみに!


💡 おすすめ読書