From 26dfc102484def249a1c9f27f051fbb721f7f3c3 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 12:44:17 +0900 Subject: [PATCH 01/25] feat(ci): add untranslated files tracking issue automation - Add --json flag to tools/list-untranslated.ts - Create GitHub Actions workflow for automatic issue updates - Implement issue update script with category grouping - Add pre-filled issue creation links for each file --- .github/scripts/sync-untranslated-issue.js | 194 ++++++++++++++++++ .github/workflows/sync-untranslated-issue.yml | 46 +++++ tools/list-untranslated.ts | 40 +++- 3 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 .github/scripts/sync-untranslated-issue.js create mode 100644 .github/workflows/sync-untranslated-issue.yml diff --git a/.github/scripts/sync-untranslated-issue.js b/.github/scripts/sync-untranslated-issue.js new file mode 100644 index 0000000000..45298e6187 --- /dev/null +++ b/.github/scripts/sync-untranslated-issue.js @@ -0,0 +1,194 @@ +/** + * @fileoverview GitHub Actions script to sync untranslated files tracking issue + */ + +const ISSUE_TITLE = '[自動更新] 未翻訳ドキュメント一覧'; +const LABELS = ['type: translation', '翻訳者募集中']; + +const CATEGORY_EMOJIS = { + guide: '📖 Guide', + tutorial: '🎓 Tutorial', + reference: '📚 Reference', + 'best-practices': '⚡ Best Practices', + cli: '🔧 CLI', + app: '🧩 Components/App', + other: '📦 その他' +}; + +const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'app', 'other']; + +/** + * Generate URLs for a file + */ +function generateLinks(filepath) { + const githubUrl = `https://github.com/angular/angular-ja/blob/main/adev-ja/${filepath}`; + + // タイトル生成: パスから拡張子を除去したシンプルな形式 + const title = filepath + .replace('src/content/', '') + .replace(/\.(md|ts|html|json)$/, ''); + + const issueUrl = `https://github.com/angular/angular-ja/issues/new?template=----.md&title=${encodeURIComponent(title + ' の翻訳')}`; + + // .mdファイルのみプレビューURL生成 + let previewUrl = null; + if (filepath.endsWith('.md')) { + const previewPath = filepath + .replace('src/content/', '') + .replace(/\/README\.md$/, '') // READMEの場合はディレクトリのみ + .replace(/\.md$/, ''); + previewUrl = `https://angular.jp/${previewPath}`; + } + + return { githubUrl, previewUrl, issueUrl }; +} + +/** + * Format a file entry for the issue body + */ +function formatFileEntry(filepath, links) { + const displayName = filepath.replace('src/content/', ''); + + let linksText = `[GitHub](${links.githubUrl})`; + if (links.previewUrl) { + linksText += ` | [プレビュー](${links.previewUrl})`; + } + linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; + + return `- [ ] **${displayName}** (${linksText})`; +} + +/** + * Group files by category + */ +function groupByCategory(files) { + const groups = {}; + for (const file of files) { + const category = file.category; + if (!groups[category]) { + groups[category] = []; + } + groups[category].push(file); + } + return groups; +} + +/** + * Generate issue body + */ +function generateIssueBody(filesData) { + const { count, files } = filesData; + + if (count === 0) { + return `## 🎉 全てのファイルが翻訳されました! + +**最終更新**: ${new Date().toISOString()} + +現在、未翻訳のファイルはありません。素晴らしい貢献をありがとうございます! + +--- + +## 📝 翻訳ガイド + +今後新しい未翻訳ファイルが追加された場合、このIssueが自動的に更新されます。 + +- [翻訳ガイドライン](https://github.com/angular/angular-ja/blob/main/CONTRIBUTING.md) +`; + } + + const groups = groupByCategory(files); + + let body = `## 📋 未翻訳ドキュメント一覧 + +このIssueは自動的に更新されます。翻訳したいファイルの「📝 翻訳宣言」リンクから翻訳宣言Issueを作成してください。 + +**最終更新**: ${new Date().toISOString()} +**未翻訳ファイル数**: ${count}件 + +--- + +`; + + // カテゴリ順にセクションを生成 + for (const category of CATEGORY_ORDER) { + if (!groups[category] || groups[category].length === 0) continue; + + const categoryFiles = groups[category]; + const emoji = CATEGORY_EMOJIS[category] || category; + + body += `### ${emoji} (${categoryFiles.length}件)\n\n`; + + for (const file of categoryFiles) { + const links = generateLinks(file.path); + body += formatFileEntry(file.path, links) + '\n'; + } + + body += '\n'; + } + + body += `--- + +## 📝 翻訳の始め方 + +1. 上記リストから翻訳したいファイルを選ぶ +2. 「📝 翻訳宣言」リンクをクリックしてIssueを作成 +3. [翻訳ガイド](https://github.com/angular/angular-ja/blob/main/CONTRIBUTING.md)に従って作業開始 +`; + + return body; +} + +/** + * Main function + */ +module.exports = async ({github, context, core, filesData}) => { + const owner = context.repo.owner; + const repo = context.repo.repo; + + core.info(`Processing ${filesData.count} untranslated files...`); + + // 既存のトラッキングIssueを検索 (state: all で closed も含む) + const { data: issues } = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'all', + labels: LABELS[0], + creator: 'github-actions[bot]' + }); + + const trackingIssue = issues.find(issue => issue.title === ISSUE_TITLE); + + const issueBody = generateIssueBody(filesData); + + if (trackingIssue) { + core.info(`Found existing tracking issue #${trackingIssue.number}`); + + // Issueを更新 + await github.rest.issues.update({ + owner, + repo, + issue_number: trackingIssue.number, + body: issueBody, + state: 'open' // closed状態の場合はreopen + }); + + core.info(`Updated tracking issue #${trackingIssue.number}`); + + if (trackingIssue.state === 'closed') { + core.info(`Reopened tracking issue #${trackingIssue.number}`); + } + } else { + // 新規Issueを作成 + const { data: newIssue } = await github.rest.issues.create({ + owner, + repo, + title: ISSUE_TITLE, + body: issueBody, + labels: LABELS + }); + + core.info(`Created new tracking issue #${newIssue.number}`); + } + + core.info('Done!'); +}; diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml new file mode 100644 index 0000000000..3d8f382b65 --- /dev/null +++ b/.github/workflows/sync-untranslated-issue.yml @@ -0,0 +1,46 @@ +name: Sync Untranslated Files Issue + +on: + push: + branches: + - main + - feature/untranslated-tracking-issue + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + update-issue: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: '.node-version' + cache: pnpm + + - run: pnpm install + + - name: Get untranslated files + id: files + run: | + FILES_JSON=$(pnpm run list-untranslated --json) + echo "data<> $GITHUB_OUTPUT + echo "$FILES_JSON" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Update tracking issue + uses: actions/github-script@v7 + with: + script: | + const script = require('./.github/scripts/sync-untranslated-issue.js') + const filesData = JSON.parse(`${{ steps.files.outputs.data }}`) + await script({github, context, core, filesData}) diff --git a/tools/list-untranslated.ts b/tools/list-untranslated.ts index 853b1701f9..106740da3a 100755 --- a/tools/list-untranslated.ts +++ b/tools/list-untranslated.ts @@ -10,7 +10,19 @@ import { extname, resolve } from 'node:path'; import { exists, getEnFilePath, glob } from './lib/fsutils'; import { adevJaDir } from './lib/workspace'; +function categorizeFile(filepath: string): string { + if (filepath.startsWith('src/content/guide/')) return 'guide'; + if (filepath.startsWith('src/content/tutorials/')) return 'tutorial'; + if (filepath.startsWith('src/content/reference/')) return 'reference'; + if (filepath.startsWith('src/content/best-practices/')) return 'best-practices'; + if (filepath.startsWith('src/content/cli/')) return 'cli'; + if (filepath.startsWith('src/app/') || filepath.startsWith('src/shared-docs/')) return 'app'; + return 'other'; +} + async function main() { + const jsonOutput = process.argv.includes('--json'); + const files = await glob(['**/*.{md,ts,html,json}', '!**/license.md'], { cwd: adevJaDir, }); @@ -24,14 +36,26 @@ async function main() { } } - untranslated.length - ? consola.info( - `Found ${untranslated.length} untranslated files:\n${untranslated - .sort() - .map((f) => ` ${f}`) - .join('\n')}` - ) - : consola.success('All files translated! 🎉'); + if (jsonOutput) { + const output = { + count: untranslated.length, + files: untranslated.sort().map(file => ({ + path: file, + category: categorizeFile(file), + extension: extname(file).slice(1) + })) + }; + console.log(JSON.stringify(output, null, 2)); + } else { + untranslated.length + ? consola.info( + `Found ${untranslated.length} untranslated files:\n${untranslated + .sort() + .map((f) => ` ${f}`) + .join('\n')}` + ) + : consola.success('All files translated! 🎉'); + } } main().catch((error) => { From c160ea1e35b7cd5ee4d95356d12fcf378ab1fc19 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 12:48:05 +0900 Subject: [PATCH 02/25] chore: trigger workflow From 28cec20048c963f838d2bdf6452ebf340ebbb1ca Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 12:54:07 +0900 Subject: [PATCH 03/25] fix(ci): pin actions to commit SHAs --- .github/workflows/sync-untranslated-issue.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 3d8f382b65..d466fd60ab 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -15,14 +15,14 @@ jobs: update-issue: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: submodules: true - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # 4.1.0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: '.node-version' cache: pnpm @@ -38,7 +38,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Update tracking issue - uses: actions/github-script@v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const script = require('./.github/scripts/sync-untranslated-issue.js') From 25ccc9d33e9362533f525af73719bda67dedcf45 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 12:56:51 +0900 Subject: [PATCH 04/25] fix(ci): suppress pnpm output in JSON parsing --- .github/workflows/sync-untranslated-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index d466fd60ab..331b15e506 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -32,7 +32,7 @@ jobs: - name: Get untranslated files id: files run: | - FILES_JSON=$(pnpm run list-untranslated --json) + FILES_JSON=$(pnpm run list-untranslated --json 2>/dev/null) echo "data<> $GITHUB_OUTPUT echo "$FILES_JSON" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT From ef65e277bcaa8f8c7b1d4ca5dd002641d870163f Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 12:59:24 +0900 Subject: [PATCH 05/25] fix(ci): convert script to ESM format --- ...sync-untranslated-issue.js => sync-untranslated-issue.mjs} | 2 +- .github/workflows/sync-untranslated-issue.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename .github/scripts/{sync-untranslated-issue.js => sync-untranslated-issue.mjs} (98%) diff --git a/.github/scripts/sync-untranslated-issue.js b/.github/scripts/sync-untranslated-issue.mjs similarity index 98% rename from .github/scripts/sync-untranslated-issue.js rename to .github/scripts/sync-untranslated-issue.mjs index 45298e6187..55aa86cc4d 100644 --- a/.github/scripts/sync-untranslated-issue.js +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -141,7 +141,7 @@ function generateIssueBody(filesData) { /** * Main function */ -module.exports = async ({github, context, core, filesData}) => { +export default async ({github, context, core, filesData}) => { const owner = context.repo.owner; const repo = context.repo.repo; diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 331b15e506..b603832e33 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -41,6 +41,6 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - const script = require('./.github/scripts/sync-untranslated-issue.js') + const script = await import('./.github/scripts/sync-untranslated-issue.mjs') const filesData = JSON.parse(`${{ steps.files.outputs.data }}`) - await script({github, context, core, filesData}) + await script.default({github, context, core, filesData}) From 798765a0fcba14e8cc16be6b67878cb814ae876f Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:02:22 +0900 Subject: [PATCH 06/25] fix(ci): use absolute path for ESM import --- .github/workflows/sync-untranslated-issue.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index b603832e33..6d7a7d560f 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -41,6 +41,6 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - const script = await import('./.github/scripts/sync-untranslated-issue.mjs') - const filesData = JSON.parse(`${{ steps.files.outputs.data }}`) - await script.default({github, context, core, filesData}) + const { default: syncIssue } = await import('${{ github.workspace }}/.github/scripts/sync-untranslated-issue.mjs'); + const filesData = JSON.parse(`${{ steps.files.outputs.data }}`); + await syncIssue({github, context, core, filesData}); From 3e5242bcabbba1a050057803c5f53d549f64e97a Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:03:54 +0900 Subject: [PATCH 07/25] fix(ci): use npx tsx directly to avoid pnpm output --- .github/workflows/sync-untranslated-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 6d7a7d560f..15f65e790d 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -32,7 +32,7 @@ jobs: - name: Get untranslated files id: files run: | - FILES_JSON=$(pnpm run list-untranslated --json 2>/dev/null) + FILES_JSON=$(npx tsx tools/list-untranslated.ts --json) echo "data<> $GITHUB_OUTPUT echo "$FILES_JSON" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT From 072a4a9310ea2a659cc7406b6211f60f743f3672 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:09:51 +0900 Subject: [PATCH 08/25] chore(ci): update issue title and remove feature branch trigger --- .github/scripts/sync-untranslated-issue.mjs | 2 +- .github/workflows/sync-untranslated-issue.yml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 55aa86cc4d..fc049a8117 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -2,7 +2,7 @@ * @fileoverview GitHub Actions script to sync untranslated files tracking issue */ -const ISSUE_TITLE = '[自動更新] 未翻訳ドキュメント一覧'; +const ISSUE_TITLE = 'Tracking: 未翻訳ドキュメント一覧'; const LABELS = ['type: translation', '翻訳者募集中']; const CATEGORY_EMOJIS = { diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 15f65e790d..d6b0eece43 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -2,9 +2,7 @@ name: Sync Untranslated Files Issue on: push: - branches: - - main - - feature/untranslated-tracking-issue + branches: [main] workflow_dispatch: permissions: From 76abf92bb38f10c96a84965bb335d586fdb6fac6 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:10:45 +0900 Subject: [PATCH 09/25] chore(ci): re-add feature branch trigger for testing --- .github/workflows/sync-untranslated-issue.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index d6b0eece43..15f65e790d 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -2,7 +2,9 @@ name: Sync Untranslated Files Issue on: push: - branches: [main] + branches: + - main + - feature/untranslated-tracking-issue workflow_dispatch: permissions: From 0078865486541b1e0995e9418438d0d61741f8a2 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:12:49 +0900 Subject: [PATCH 10/25] docs(ci): add JSDoc type annotations to sync script --- .github/scripts/sync-untranslated-issue.mjs | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index fc049a8117..efda562aed 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -2,9 +2,51 @@ * @fileoverview GitHub Actions script to sync untranslated files tracking issue */ +/** + * @typedef {Object} UntranslatedFile + * @property {string} path - File path relative to adev-ja + * @property {string} category - File category (guide, tutorial, etc.) + * @property {string} extension - File extension without dot + */ + +/** + * @typedef {Object} FilesData + * @property {number} count - Total number of untranslated files + * @property {UntranslatedFile[]} files - Array of untranslated files + */ + +/** + * @typedef {Object} FileLinks + * @property {string} githubUrl - GitHub blob URL + * @property {string|null} previewUrl - Preview URL on angular.jp (null for non-md files) + * @property {string} issueUrl - Issue creation URL with pre-filled title + */ + +/** + * @typedef {Object} GitHubContext + * @property {Object} repo + * @property {string} repo.owner - Repository owner + * @property {string} repo.repo - Repository name + */ + +/** + * @typedef {Object} GitHubAPI + * @property {Object} rest + * @property {Object} rest.issues + * @property {Function} rest.issues.listForRepo + * @property {Function} rest.issues.create + * @property {Function} rest.issues.update + */ + +/** + * @typedef {Object} ActionsCore + * @property {Function} info - Log info message + */ + const ISSUE_TITLE = 'Tracking: 未翻訳ドキュメント一覧'; const LABELS = ['type: translation', '翻訳者募集中']; +/** @type {Record} */ const CATEGORY_EMOJIS = { guide: '📖 Guide', tutorial: '🎓 Tutorial', @@ -15,10 +57,13 @@ const CATEGORY_EMOJIS = { other: '📦 その他' }; +/** @type {string[]} */ const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'app', 'other']; /** * Generate URLs for a file + * @param {string} filepath - File path relative to adev-ja + * @returns {FileLinks} Object containing GitHub, preview, and issue URLs */ function generateLinks(filepath) { const githubUrl = `https://github.com/angular/angular-ja/blob/main/adev-ja/${filepath}`; @@ -45,6 +90,9 @@ function generateLinks(filepath) { /** * Format a file entry for the issue body + * @param {string} filepath - File path relative to adev-ja + * @param {FileLinks} links - Object containing URLs for the file + * @returns {string} Markdown formatted list item */ function formatFileEntry(filepath, links) { const displayName = filepath.replace('src/content/', ''); @@ -60,6 +108,8 @@ function formatFileEntry(filepath, links) { /** * Group files by category + * @param {UntranslatedFile[]} files - Array of untranslated files + * @returns {Record} Files grouped by category */ function groupByCategory(files) { const groups = {}; @@ -75,6 +125,8 @@ function groupByCategory(files) { /** * Generate issue body + * @param {FilesData} filesData - Object containing untranslated files data + * @returns {string} Markdown formatted issue body */ function generateIssueBody(filesData) { const { count, files } = filesData; @@ -140,6 +192,12 @@ function generateIssueBody(filesData) { /** * Main function + * @param {Object} params - Parameters + * @param {GitHubAPI} params.github - GitHub API instance + * @param {GitHubContext} params.context - GitHub Actions context + * @param {ActionsCore} params.core - GitHub Actions core utilities + * @param {FilesData} params.filesData - Untranslated files data + * @returns {Promise} */ export default async ({github, context, core, filesData}) => { const owner = context.repo.owner; From b9d102925a6ad278efb5db02d2eb0834f5503d99 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:14:11 +0900 Subject: [PATCH 11/25] style(ci): remove bold formatting from file names --- .github/scripts/sync-untranslated-issue.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index efda562aed..ec96569224 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -103,7 +103,7 @@ function formatFileEntry(filepath, links) { } linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; - return `- [ ] **${displayName}** (${linksText})`; + return `- [ ] ${displayName} (${linksText})`; } /** From 0e1258949b0b2f71e17de0f7eafded24efbcb685 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:16:41 +0900 Subject: [PATCH 12/25] fix(ci): update existing issue with new title and format --- .github/scripts/sync-untranslated-issue.mjs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index ec96569224..6fb4647f1e 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -44,6 +44,7 @@ */ const ISSUE_TITLE = 'Tracking: 未翻訳ドキュメント一覧'; +const OLD_ISSUE_TITLE = '[自動更新] 未翻訳ドキュメント一覧'; // 後方互換性のため const LABELS = ['type: translation', '翻訳者募集中']; /** @type {Record} */ @@ -214,18 +215,21 @@ export default async ({github, context, core, filesData}) => { creator: 'github-actions[bot]' }); - const trackingIssue = issues.find(issue => issue.title === ISSUE_TITLE); + const trackingIssue = issues.find(issue => + issue.title === ISSUE_TITLE || issue.title === OLD_ISSUE_TITLE + ); const issueBody = generateIssueBody(filesData); if (trackingIssue) { core.info(`Found existing tracking issue #${trackingIssue.number}`); - // Issueを更新 + // Issueを更新 (タイトルも更新して新しい形式に移行) await github.rest.issues.update({ owner, repo, issue_number: trackingIssue.number, + title: ISSUE_TITLE, body: issueBody, state: 'open' // closed状態の場合はreopen }); From c4546995e1a2a0e84e68d4686fef2e8cf9a536ee Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:21:50 +0900 Subject: [PATCH 13/25] chore(ci): remove backward compatibility for old issue title --- .github/scripts/sync-untranslated-issue.mjs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 6fb4647f1e..2903f3127e 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -44,7 +44,6 @@ */ const ISSUE_TITLE = 'Tracking: 未翻訳ドキュメント一覧'; -const OLD_ISSUE_TITLE = '[自動更新] 未翻訳ドキュメント一覧'; // 後方互換性のため const LABELS = ['type: translation', '翻訳者募集中']; /** @type {Record} */ @@ -215,9 +214,7 @@ export default async ({github, context, core, filesData}) => { creator: 'github-actions[bot]' }); - const trackingIssue = issues.find(issue => - issue.title === ISSUE_TITLE || issue.title === OLD_ISSUE_TITLE - ); + const trackingIssue = issues.find(issue => issue.title === ISSUE_TITLE); const issueBody = generateIssueBody(filesData); From f93222f19330ed71ec8ea7ca09d08772b6b061f2 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:22:10 +0900 Subject: [PATCH 14/25] feat(ci): open preview links in new tab --- .github/scripts/sync-untranslated-issue.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 2903f3127e..9fbf023441 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -99,7 +99,7 @@ function formatFileEntry(filepath, links) { let linksText = `[GitHub](${links.githubUrl})`; if (links.previewUrl) { - linksText += ` | [プレビュー](${links.previewUrl})`; + linksText += ` | プレビュー`; } linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; From 84916838f0b9cde1cd437370429aafe05e1d164c Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:26:01 +0900 Subject: [PATCH 15/25] revert: remove target=_blank from preview links --- .github/scripts/sync-untranslated-issue.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 9fbf023441..2903f3127e 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -99,7 +99,7 @@ function formatFileEntry(filepath, links) { let linksText = `[GitHub](${links.githubUrl})`; if (links.previewUrl) { - linksText += ` | プレビュー`; + linksText += ` | [プレビュー](${links.previewUrl})`; } linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; From ce1c97fbebd9c3ba24efc4bcf7d807898cdb051b Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:31:40 +0900 Subject: [PATCH 16/25] feat(tools): exclude tutorial config.json from untranslated list --- tools/list-untranslated.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/list-untranslated.ts b/tools/list-untranslated.ts index 106740da3a..e42ba23b44 100755 --- a/tools/list-untranslated.ts +++ b/tools/list-untranslated.ts @@ -31,6 +31,8 @@ async function main() { for (const file of files) { const ext = extname(file); if (file.includes(`.en${ext}`)) continue; + // tutorialのconfig.jsonは除外 + if (file.startsWith('src/content/tutorials/') && file.endsWith('config.json')) continue; if (!(await exists(resolve(adevJaDir, getEnFilePath(file))))) { untranslated.push(file); } From ea54d2ae758d09c0fc2fabfa5c81a338738b6858 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:35:37 +0900 Subject: [PATCH 17/25] feat(ci): add Tools category to untranslated files list --- .github/scripts/sync-untranslated-issue.mjs | 3 ++- tools/list-untranslated.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 2903f3127e..2804780180 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -53,12 +53,13 @@ const CATEGORY_EMOJIS = { reference: '📚 Reference', 'best-practices': '⚡ Best Practices', cli: '🔧 CLI', + tools: '🛠️ Tools', app: '🧩 Components/App', other: '📦 その他' }; /** @type {string[]} */ -const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'app', 'other']; +const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'tools', 'app', 'other']; /** * Generate URLs for a file diff --git a/tools/list-untranslated.ts b/tools/list-untranslated.ts index e42ba23b44..3c084d2670 100755 --- a/tools/list-untranslated.ts +++ b/tools/list-untranslated.ts @@ -16,6 +16,7 @@ function categorizeFile(filepath: string): string { if (filepath.startsWith('src/content/reference/')) return 'reference'; if (filepath.startsWith('src/content/best-practices/')) return 'best-practices'; if (filepath.startsWith('src/content/cli/')) return 'cli'; + if (filepath.startsWith('src/content/tools/')) return 'tools'; if (filepath.startsWith('src/app/') || filepath.startsWith('src/shared-docs/')) return 'app'; return 'other'; } From a6ec12904646ab59108bb3ad88922f8da1730d64 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:37:11 +0900 Subject: [PATCH 18/25] feat(ci): add Ecosystem category to untranslated files list --- .github/scripts/sync-untranslated-issue.mjs | 3 ++- tools/list-untranslated.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 2804780180..60591427e8 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -54,12 +54,13 @@ const CATEGORY_EMOJIS = { 'best-practices': '⚡ Best Practices', cli: '🔧 CLI', tools: '🛠️ Tools', + ecosystem: '🌐 Ecosystem', app: '🧩 Components/App', other: '📦 その他' }; /** @type {string[]} */ -const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'tools', 'app', 'other']; +const CATEGORY_ORDER = ['guide', 'tutorial', 'reference', 'best-practices', 'cli', 'tools', 'ecosystem', 'app', 'other']; /** * Generate URLs for a file diff --git a/tools/list-untranslated.ts b/tools/list-untranslated.ts index 3c084d2670..c15c32b2da 100755 --- a/tools/list-untranslated.ts +++ b/tools/list-untranslated.ts @@ -17,6 +17,7 @@ function categorizeFile(filepath: string): string { if (filepath.startsWith('src/content/best-practices/')) return 'best-practices'; if (filepath.startsWith('src/content/cli/')) return 'cli'; if (filepath.startsWith('src/content/tools/')) return 'tools'; + if (filepath.startsWith('src/content/ecosystem/')) return 'ecosystem'; if (filepath.startsWith('src/app/') || filepath.startsWith('src/shared-docs/')) return 'app'; return 'other'; } From c09bcb1f75ce22eb1e2adcf29466c4a4376f7710 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:46:04 +0900 Subject: [PATCH 19/25] feat(ci): integrate Translation Checkout issues with tracking list --- .github/scripts/sync-untranslated-issue.mjs | 43 ++++++++++++++++--- .github/workflows/sync-untranslated-issue.yml | 2 + 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index 60591427e8..a178249d54 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -94,18 +94,24 @@ function generateLinks(filepath) { * Format a file entry for the issue body * @param {string} filepath - File path relative to adev-ja * @param {FileLinks} links - Object containing URLs for the file + * @param {number|null} checkoutIssueNumber - Translation Checkout issue number if exists * @returns {string} Markdown formatted list item */ -function formatFileEntry(filepath, links) { +function formatFileEntry(filepath, links, checkoutIssueNumber = null) { const displayName = filepath.replace('src/content/', ''); let linksText = `[GitHub](${links.githubUrl})`; if (links.previewUrl) { linksText += ` | [プレビュー](${links.previewUrl})`; } - linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; - return `- [ ] ${displayName} (${linksText})`; + if (checkoutIssueNumber) { + linksText += ` | [#${checkoutIssueNumber}](https://github.com/angular/angular-ja/issues/${checkoutIssueNumber})`; + return `- [x] ${displayName} (${linksText})`; + } else { + linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; + return `- [ ] ${displayName} (${linksText})`; + } } /** @@ -128,9 +134,10 @@ function groupByCategory(files) { /** * Generate issue body * @param {FilesData} filesData - Object containing untranslated files data + * @param {Map} checkoutIssuesMap - Map of file paths to issue numbers * @returns {string} Markdown formatted issue body */ -function generateIssueBody(filesData) { +function generateIssueBody(filesData, checkoutIssuesMap) { const { count, files } = filesData; if (count === 0) { @@ -174,7 +181,8 @@ function generateIssueBody(filesData) { for (const file of categoryFiles) { const links = generateLinks(file.path); - body += formatFileEntry(file.path, links) + '\n'; + const checkoutIssueNumber = checkoutIssuesMap.get(file.path) || null; + body += formatFileEntry(file.path, links, checkoutIssueNumber) + '\n'; } body += '\n'; @@ -207,6 +215,29 @@ export default async ({github, context, core, filesData}) => { core.info(`Processing ${filesData.count} untranslated files...`); + // Translation Checkout ラベルの全Issue (open only) を取得 + const { data: checkoutIssues } = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'type: Translation Checkout' + }); + + core.info(`Found ${checkoutIssues.length} Translation Checkout issues`); + + // Issueタイトルからファイルパスを抽出してマップを作成 + // タイトル形式: "{ファイルパス} の翻訳" + const checkoutIssuesMap = new Map(); + for (const issue of checkoutIssues) { + const match = issue.title.match(/^(.+)\s+の翻訳$/); + if (match) { + const filepath = `src/content/${match[1]}`; + checkoutIssuesMap.set(filepath, issue.number); + } + } + + core.info(`Mapped ${checkoutIssuesMap.size} files to checkout issues`); + // 既存のトラッキングIssueを検索 (state: all で closed も含む) const { data: issues } = await github.rest.issues.listForRepo({ owner, @@ -218,7 +249,7 @@ export default async ({github, context, core, filesData}) => { const trackingIssue = issues.find(issue => issue.title === ISSUE_TITLE); - const issueBody = generateIssueBody(filesData); + const issueBody = generateIssueBody(filesData, checkoutIssuesMap); if (trackingIssue) { core.info(`Found existing tracking issue #${trackingIssue.number}`); diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 15f65e790d..6500a4f1f5 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -5,6 +5,8 @@ on: branches: - main - feature/untranslated-tracking-issue + issues: + types: [opened, closed, reopened, labeled] workflow_dispatch: permissions: From 4f5467befda38a8c889c2a4a7a15573f48b33d90 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 13:58:10 +0900 Subject: [PATCH 20/25] test: trigger workflow to verify Translation Checkout integration From 39f0b21df49e784593e166bfe64cd8c72fc56535 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 14:01:14 +0900 Subject: [PATCH 21/25] test: verify Translation Checkout integration with Issue #5 From 74f6276e7cf31a5c687ceddc4e6bd3e13a7d8af2 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 14:08:14 +0900 Subject: [PATCH 22/25] fix: use prefix matching for Translation Checkout issues to support directory declarations --- .github/scripts/sync-untranslated-issue.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index a178249d54..d792809910 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -227,12 +227,18 @@ export default async ({github, context, core, filesData}) => { // Issueタイトルからファイルパスを抽出してマップを作成 // タイトル形式: "{ファイルパス} の翻訳" + // 前方一致でマッチング(ディレクトリ名での宣言に対応) const checkoutIssuesMap = new Map(); for (const issue of checkoutIssues) { const match = issue.title.match(/^(.+)\s+の翻訳$/); if (match) { - const filepath = `src/content/${match[1]}`; - checkoutIssuesMap.set(filepath, issue.number); + const declaredPath = `src/content/${match[1]}`; + // 各未翻訳ファイルに対して前方一致チェック + for (const file of filesData.files) { + if (file.path.startsWith(declaredPath)) { + checkoutIssuesMap.set(file.path, issue.number); + } + } } } From 9f011647501302bee570507fbe078f3262782266 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 14:10:01 +0900 Subject: [PATCH 23/25] fix: use simple #{num} format for issue links and remove feature branch trigger --- .github/scripts/sync-untranslated-issue.mjs | 2 +- .github/workflows/sync-untranslated-issue.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/scripts/sync-untranslated-issue.mjs b/.github/scripts/sync-untranslated-issue.mjs index d792809910..2fc4e14a58 100644 --- a/.github/scripts/sync-untranslated-issue.mjs +++ b/.github/scripts/sync-untranslated-issue.mjs @@ -106,7 +106,7 @@ function formatFileEntry(filepath, links, checkoutIssueNumber = null) { } if (checkoutIssueNumber) { - linksText += ` | [#${checkoutIssueNumber}](https://github.com/angular/angular-ja/issues/${checkoutIssueNumber})`; + linksText += ` | #${checkoutIssueNumber}`; return `- [x] ${displayName} (${linksText})`; } else { linksText += ` | [📝 翻訳宣言](${links.issueUrl})`; diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 6500a4f1f5..f01494a5af 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - feature/untranslated-tracking-issue issues: types: [opened, closed, reopened, labeled] workflow_dispatch: From 923db891c418c4917660817fdf0a65d6cf9cd893 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 14:12:40 +0900 Subject: [PATCH 24/25] test: re-add feature branch trigger for final verification --- .github/workflows/sync-untranslated-issue.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index f01494a5af..6500a4f1f5 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - feature/untranslated-tracking-issue issues: types: [opened, closed, reopened, labeled] workflow_dispatch: From 1c048115abc903f78d03e0ecd588967497f25dce Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 21 Nov 2025 14:14:42 +0900 Subject: [PATCH 25/25] chore: remove feature branch trigger --- .github/workflows/sync-untranslated-issue.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sync-untranslated-issue.yml b/.github/workflows/sync-untranslated-issue.yml index 6500a4f1f5..f01494a5af 100644 --- a/.github/workflows/sync-untranslated-issue.yml +++ b/.github/workflows/sync-untranslated-issue.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - feature/untranslated-tracking-issue issues: types: [opened, closed, reopened, labeled] workflow_dispatch: