Skip to content

Commit 944eead

Browse files
authored
Merge pull request #558 from uju09/feature/translation-checker-workflow
feat: Added automated translation checker workflow
2 parents e204dd5 + 0fc2847 commit 944eead

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
name: Translation notifier (post-merge)
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- dev
8+
paths:
9+
- 'untranslated.txt'
10+
11+
permissions:
12+
contents: read
13+
pull-requests: write
14+
issues: read
15+
16+
jobs:
17+
notify:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Resolve merged PR number for this commit
25+
id: pr
26+
uses: actions/github-script@v7
27+
with:
28+
script: |
29+
const commitSha = context.sha;
30+
const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
commit_sha: commitSha
34+
});
35+
if (!prs.data.length) {
36+
throw new Error('No PR associated with this commit');
37+
}
38+
core.setOutput('number', prs.data[0].number);
39+
40+
- name: Read untranslated.txt (if present)
41+
id: txt
42+
shell: bash
43+
run: |
44+
FILE="untranslated.txt"
45+
if [ ! -f "$FILE" ]; then
46+
{
47+
echo 'list<<COMMENT_EOF'
48+
echo ''
49+
echo 'COMMENT_EOF'
50+
} >> "$GITHUB_OUTPUT"
51+
exit 0
52+
fi
53+
content="$(cat "$FILE")"
54+
{
55+
echo 'list<<COMMENT_EOF'
56+
printf '%s\n' "$content"
57+
echo 'COMMENT_EOF'
58+
} >> "$GITHUB_OUTPUT"
59+
60+
- name: Fetch translator mapping issue body
61+
id: issue
62+
uses: actions/github-script@v7
63+
env:
64+
ISSUE_NUMBER: "488" # Replace with your mapping issue number
65+
with:
66+
script: |
67+
const issue_number = Number(process.env.ISSUE_NUMBER);
68+
const { data } = await github.rest.issues.get({
69+
owner: context.repo.owner,
70+
repo: context.repo.repo,
71+
issue_number
72+
});
73+
core.setOutput('body', data.body || '');
74+
75+
- name: Map languages to maintainers (code from issue)
76+
id: mapping
77+
uses: actions/github-script@v7
78+
env:
79+
LANGUAGES: ${{ steps.txt.outputs.list }} # content of untranslated.txt
80+
ISSUE_BODY: ${{ steps.issue.outputs.body }} # issue text with "Language (xx) by ..."
81+
TESTING: ${{ vars.TRANSLATION_TESTING || 'false' }} # 'true' to avoid tagging real users
82+
with:
83+
script: |
84+
const body = process.env.ISSUE_BODY || '';
85+
const langPayload = process.env.LANGUAGES || '';
86+
87+
// Parse lines like:
88+
// - English (en) by @user1 @user2
89+
// - Marathi (mr) by @user
90+
// Groups: 1=name, 2=code, 3=handles
91+
const listRegex = /-?\s*([\w\s]+?)\s*\(\s*([a-z]{2,3})\s*\)\s+by\s+(@[A-Za-z0-9_-]+(?:\s+@[A-Za-z0-9_-]+)*)/gi;
92+
93+
const codeToHandles = {};
94+
const codeToName = {};
95+
let match;
96+
while ((match = listRegex.exec(body)) !== null) {
97+
const name = match[1].trim();
98+
const code = match[2].toLowerCase();
99+
const handles = match[3].trim().split(/\s+/);
100+
codeToHandles[code] = handles;
101+
codeToName[code] = name; // use the canonical name from the issue line
102+
}
103+
104+
// Parse untranslated.txt as JSON object keys or newline-separated codes
105+
let untranslatedCodes = [];
106+
try {
107+
const parsed = JSON.parse(langPayload);
108+
untranslatedCodes = Object.keys(parsed).map(k => k.trim().toLowerCase());
109+
} catch {
110+
untranslatedCodes = (langPayload || '')
111+
.split(/\r?\n/)
112+
.map(s => s.trim().toLowerCase())
113+
.filter(Boolean);
114+
}
115+
116+
const testing = (process.env.TESTING || '').toLowerCase() === 'true';
117+
const dummy = '@translation-bot';
118+
const linesCodeOnly = [];
119+
const friendlyLines = [];
120+
121+
for (const code of untranslatedCodes) {
122+
const handles = codeToHandles[code] || [];
123+
const notify = testing && handles.length ? [dummy] : handles;
124+
const fullName = codeToName[code] || code.toUpperCase();
125+
if (notify.length) {
126+
linesCodeOnly.push(`${code}: ${notify.join(' ')}`);
127+
friendlyLines.push(`${fullName} (${code}): ${notify.join(' ')}`);
128+
} else {
129+
linesCodeOnly.push(`${code}: (no maintainer mapped)`);
130+
friendlyLines.push(`${fullName} (${code}): (no maintainer mapped)`);
131+
}
132+
}
133+
134+
core.setOutput('handles', linesCodeOnly.join('\n')); // code-only mapping
135+
core.setOutput('handles_friendly', friendlyLines.join('\n')); // Full Name (code)
136+
137+
- name: Build PR comment body
138+
id: comment
139+
shell: bash
140+
run: |
141+
UNTRANS="${{ steps.txt.outputs.list }}"
142+
HANDLES_FRIENDLY="${{ steps.mapping.outputs.handles_friendly }}"
143+
144+
{
145+
echo 'body<<COMMENT_EOF'
146+
echo '🔔 **Translation Check Notice**'
147+
echo
148+
echo 'The following untranslated language codes were found in `untranslated.txt`:'
149+
echo 'Please translate these keys:'
150+
if [ -n "$UNTRANS" ]; then
151+
echo '```'
152+
printf '%s\n' "$UNTRANS"
153+
echo '```'
154+
else
155+
echo '- No entries found'
156+
fi
157+
echo
158+
echo "📣 Notifying maintainers per language:"
159+
printf '%s\n' "$HANDLES_FRIENDLY"
160+
echo
161+
echo '_Automated post-merge notice by Translation Notifier_'
162+
echo 'COMMENT_EOF'
163+
} >> "$GITHUB_OUTPUT"
164+
165+
- name: Post comment to merged PR
166+
uses: actions/github-script@v7
167+
env:
168+
PR_NUMBER: ${{ steps.pr.outputs.number }}
169+
COMMENT_BODY: ${{ steps.comment.outputs.body }}
170+
with:
171+
script: |
172+
const issueNumber = Number(process.env.PR_NUMBER);
173+
if (!issueNumber || isNaN(issueNumber)) {
174+
throw new Error('PR_NUMBER not set or invalid');
175+
}
176+
const body = process.env.COMMENT_BODY || 'No comment generated.';
177+
await github.rest.issues.createComment({
178+
owner: context.repo.owner,
179+
repo: context.repo.repo,
180+
issue_number: issueNumber,
181+
body
182+
});

0 commit comments

Comments
 (0)