diff --git a/.github/workflows/translation-consistency-check.yml b/.github/workflows/translation-consistency-check.yml new file mode 100644 index 0000000..b2ffb85 --- /dev/null +++ b/.github/workflows/translation-consistency-check.yml @@ -0,0 +1,182 @@ +name: Check Locale Consistency + +on: + push: + branches: + - master + +permissions: + contents: read + issues: write + pull-requests: read + +jobs: + check-translations: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 + + - name: Detect changed locale files + id: filter + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + with: + list-files: json + filters: | + de: + - 'docs/de/**/*.md' + en: + - 'docs/en/**/*.md' + es: + - 'docs/es/**/*.md' + fr: + - 'docs/fr/**/*.md' + token: ${{ github.token }} + + - name: Create translation tracking issue + if: steps.filter.outputs.changes != '[]' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: context.sha + }) + + if (prs.length === 0) { + core.setFailed('No pull request found associated with this commit. This workflow only runs for merged PRs.') + return + } + + const pr = prs[0] + + if (!pr.number || !pr.title) { + core.setFailed('Pull request data is incomplete. Missing required fields: number or title.') + return + } + + const prNumber = pr.number + const prTitle = pr.title + + const changedLocales = JSON.parse('${{ steps.filter.outputs.changes }}') + const changes = { + de: { added: [], modified: [], deleted: [] }, + en: { added: [], modified: [], deleted: [] }, + es: { added: [], modified: [], deleted: [] }, + fr: { added: [], modified: [], deleted: [] } + } + + const { data: prFiles } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + per_page: 100 + }) + + if (!prFiles || !Array.isArray(prFiles)) { + core.setFailed('Failed to retrieve PR files from GitHub API.') + return + } + + for (const locale of changedLocales) { + const localePrefix = `docs/${locale}/` + const localeFiles = prFiles.filter(f => + f.filename.startsWith(localePrefix) && f.filename.endsWith('.md') + ) + + for (const file of localeFiles) { + if (file.status === 'added') { + changes[locale].added.push(file.filename) + } else if (file.status === 'removed') { + changes[locale].deleted.push(file.filename) + } else if (file.status === 'modified' || file.status === 'changed') { + changes[locale].modified.push(file.filename) + } + } + } + + const localeNames = { + de: 'German', + en: 'English', + es: 'Spanish', + fr: 'French' + } + + const baseUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}` + const blobUrl = `${baseUrl}/blob/master` + + let changesSummary = '' + for (const [locale, types] of Object.entries(changes)) { + const hasChanges = types.added.length + types.modified.length + types.deleted.length > 0 + if (!hasChanges) continue + + const localeName = localeNames[locale] || locale.toUpperCase() + changesSummary += `#### In ${localeName} locale \`/${locale}/\`\n\n` + + if (types.modified.length > 0) { + changesSummary += '##### Modified:\n' + types.modified.forEach(f => changesSummary += `- [${f}](${blobUrl}/${f})\n`) + changesSummary += '\n' + } + + if (types.added.length > 0) { + changesSummary += '##### Added:\n' + types.added.forEach(f => changesSummary += `- [${f}](${blobUrl}/${f})\n`) + changesSummary += '\n' + } + + if (types.deleted.length > 0) { + changesSummary += '##### Deleted:\n' + types.deleted.forEach(f => changesSummary += `- [${f}](${blobUrl}/${f})\n`) + changesSummary += '\n' + } + } + + if (!changesSummary) { + core.setFailed('No markdown file changes found in locale directories despite paths-filter detecting changes.') + return + } + + const issueTitle = `🔧 [Refactor] Adapt Translations to recent Documentation Changes by #${prNumber}` + const prUrl = `${baseUrl}/pull/${prNumber}` + const commitUrl = `${baseUrl}/commit/${context.sha}` + const prTemplateUrl = `https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/.github/PULL_REQUEST_TEMPLATE.md` + + const buildIssueBody = (issueNum) => `## Translation Update Required + + Pull Request **[#${prNumber} ${prTitle}](${prUrl})** has been merged with documentation changes. + + ### Changes + + ${changesSummary} + --- + ### TODO + - [ ] Carefully view the [changed files](${commitUrl}) + - [ ] Decide whether translation actions need to be performed + - [ ] If not, close this issue with an appropriate comment + - [ ] Else, update the translations + - [ ] Use the [Pull request template](${prTemplateUrl}) + - [ ] When all translation updates are completed, set this very issue to be fixed by the pull request (" - fixes #${issueNum}")` + + try { + const { data: newIssue } = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: issueTitle, + body: buildIssueBody(''), + labels: ['translation', 'documentation'] + }) + + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: newIssue.number, + body: buildIssueBody(newIssue.number) + }) + + console.log(`Created issue #${newIssue.number}`) + } catch (error) { + core.setFailed(`Failed to create issue: ${error.message}`) + }