Translations update from Hosted Weblate #45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Weblate Translation PR Handler | |
| on: | |
| pull_request: | |
| types: [opened, synchronize] | |
| jobs: | |
| label-and-prepare: | |
| name: Prepare Weblate PR for Review | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.head.ref == 'weblate-translations' | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| steps: | |
| - name: Add translation labels | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| labels: ['translation', 'weblate', 'needs-review'] | |
| }); | |
| - name: Convert to Draft PR | |
| if: github.event.action == 'opened' | |
| uses: actions/github-script@v7 | |
| continue-on-error: true | |
| with: | |
| script: | | |
| // GraphQL required β REST API cannot convert PRs to Draft | |
| const mutation = `mutation($id: ID!) { | |
| convertPullRequestToDraft(input: {pullRequestId: $id}) { | |
| pullRequest { isDraft } | |
| } | |
| }`; | |
| try { | |
| await github.graphql(mutation, { | |
| id: context.payload.pull_request.node_id | |
| }); | |
| core.info('β PR converted to Draft β CI will not run until review'); | |
| } catch (e) { | |
| core.warning('β οΈ Could not convert to Draft (manual action needed): ' + e.message); | |
| } | |
| - uses: actions/checkout@v4 | |
| - name: Analyze translation and post review checklist | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| // Find new/changed language files | |
| const resDir = 'android/app/src/main/res'; | |
| const enFile = path.join(resDir, 'values/strings.xml'); | |
| const enContent = fs.readFileSync(enFile, 'utf8'); | |
| const enCount = (enContent.match(/<string name="/g) || []).length; | |
| // Find all values-XX directories (except values, values-de, values-night) | |
| const dirs = fs.readdirSync(resDir) | |
| .filter(d => d.match(/^values-[a-z]{2}$/) && d !== 'values-de'); | |
| let langStats = ''; | |
| for (const dir of dirs) { | |
| const langFile = path.join(resDir, dir, 'strings.xml'); | |
| if (fs.existsSync(langFile)) { | |
| const content = fs.readFileSync(langFile, 'utf8'); | |
| const count = (content.match(/<string name="/g) || []).length; | |
| const pct = Math.round((count / enCount) * 100); | |
| const lang = dir.replace('values-', '').toUpperCase(); | |
| langStats += `| ${lang} | ${count} / ${enCount} | ${pct}% |\n`; | |
| } | |
| } | |
| const body = | |
| '## π Translation PR β Review Required\n\n' + | |
| 'This PR contains translation updates from [Weblate](https://hosted.weblate.org/projects/simple-notes-sync/).\n\n' + | |
| '### π Translation Coverage (in this PR)\n\n' + | |
| '| Language | Translated | Coverage |\n' + | |
| '|----------|-----------|----------|\n' + | |
| (langStats || '| β | β | PR contains empty file(s) |\n') + '\n' + | |
| '### β Review Checklist\n\n' + | |
| 'Please check the following before approving:\n\n' + | |
| '- [ ] **Coverage**: Is the translation sufficiently complete? (Recommendation: β₯50%)\n' + | |
| '- [ ] **Spot check**: Verify 5β10 random strings for correctness (back-translate via Google Translate)\n' + | |
| '- [ ] **Placeholders**: Are `%s`, `%d`, `%1$s` etc. correctly preserved?\n' + | |
| '- [ ] **App name**: Is `app_name` unchanged ("Simple Notes")?\n' + | |
| '- [ ] **XML validity**: No broken XML tags or encoding errors?\n' + | |
| '- [ ] **Length**: Are translations not excessively long (UI overflow)?\n' + | |
| '- [ ] **Consistency**: Is terminology used consistently?\n' + | |
| '- [ ] **locales_config.xml**: Needs to be updated (add new language)\n\n' + | |
| '### π How to review translations you don\'t speak\n\n' + | |
| '1. **Back-translate**: Copy translated strings β Google Translate / DeepL β compare with English original\n' + | |
| '2. **Weblate checks**: [Open Weblate project](https://hosted.weblate.org/projects/simple-notes-sync/android-app/) β built-in quality checks for placeholders, length, duplicates etc.\n' + | |
| '3. **Community review**: Ask in the PR if another speaker of the language can review\n\n' + | |
| '### βοΈ Next Steps\n\n' + | |
| '1. Review translations (see checklist above)\n' + | |
| '2. If OK: **Approve** the PR or mark it as **Ready for Review**\n' + | |
| '3. CI build starts automatically after approval\n' + | |
| '4. After successful build: Merge\n\n' + | |
| '> β οΈ **Note:** This PR was automatically marked as **Draft** so the CI build doesn\'t run unnecessarily. Mark it as "Ready for Review" when you\'re done reviewing.'; | |
| // Check if a bot comment already exists | |
| const comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number | |
| }); | |
| const existing = comments.data.find(c => | |
| c.body && c.body.includes('Translation PR β Review Required') | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } |