LunariaJS CI/CD統合:ローカライズフローを自動化
LunariaJSをGitHub Actions、GitLab CIなどのCI/CDプラットフォームに統合し、翻訳状態自動チェック、ダッシュボード自動ビルド・公開を実現し、ローカライズフローを完全自動化する方法を学びます。
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 Actions | GitLab CI |
|---|---|---|
| 完全な履歴を取得 | fetch-depth: 0 | GIT_DEPTH: 0 |
| アーティファクトのアップロード | upload-artifact | artifacts.paths |
| Pagesへのデプロイ | deploy-pages | pages job |
| Secrets | secrets.VARIABLE | variables.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文字)
お楽しみに!
💡 おすすめ読書: