Skip to content

Translations update from Hosted Weblate #45

Translations update from Hosted Weblate

Translations update from Hosted Weblate #45

Workflow file for this run

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
});
}