Move translation checks into locales_generator.sh, simplify CI workflow
Use msgcmp instead of diff for base.pot validation to avoid failing on legacy stale entries - the same cascading breakage that killed the original workflow (disabled 2023, removed in #4483).
This commit is contained in:
parent
27ab59e910
commit
2f912482e3
|
|
@ -11,128 +11,12 @@ on:
|
|||
- 'archinstall/locales/**'
|
||||
- '.github/workflows/translation-check.yaml'
|
||||
jobs:
|
||||
validate-po:
|
||||
name: Validate .po files and translation patterns
|
||||
translations:
|
||||
name: Validate translations
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux/archlinux:latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Find changed files
|
||||
id: changed
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
base="${{ github.event.pull_request.base.sha }}"
|
||||
head="${{ github.event.pull_request.head.sha }}"
|
||||
else
|
||||
base="${{ github.event.before }}"
|
||||
head="${{ github.sha }}"
|
||||
fi
|
||||
changed_po=$(git diff --name-only "$base" "$head" 2>/dev/null | grep '\.po$' || true)
|
||||
changed_py=$(git diff --name-only "$base" "$head" 2>/dev/null | grep '\.py$' || true)
|
||||
if [ -n "$changed_po" ]; then
|
||||
echo "has_po=true" >> "$GITHUB_OUTPUT"
|
||||
echo "$changed_po" > /tmp/changed_po.txt
|
||||
else
|
||||
echo "has_po=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
if [ -n "$changed_py" ]; then
|
||||
echo "has_py=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_py=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Install gettext
|
||||
if: steps.changed.outputs.has_po == 'true'
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman --noconfirm -Sy archlinux-keyring
|
||||
pacman --noconfirm -Syyu
|
||||
pacman --noconfirm -S gettext
|
||||
- name: Check changed .po syntax with msgfmt
|
||||
if: steps.changed.outputs.has_po == 'true'
|
||||
run: |
|
||||
failed=0
|
||||
while IFS= read -r po; do
|
||||
if [ -f "$po" ] && ! msgfmt --check --output-file=/dev/null "$po" 2>&1; then
|
||||
echo "FAIL: $po"
|
||||
failed=1
|
||||
fi
|
||||
done < /tmp/changed_po.txt
|
||||
if [ "$failed" -eq 1 ]; then
|
||||
echo "::error::Some .po files have syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
echo "All changed .po files passed syntax check"
|
||||
- name: Warn if changed .mo files are out of sync
|
||||
if: steps.changed.outputs.has_po == 'true'
|
||||
run: |
|
||||
out_of_sync=0
|
||||
while IFS= read -r po; do
|
||||
if [ ! -f "$po" ]; then continue; fi
|
||||
mo="${po%.po}.mo"
|
||||
if [ ! -f "$mo" ]; then
|
||||
echo "::warning::Missing .mo file for $po"
|
||||
out_of_sync=1
|
||||
continue
|
||||
fi
|
||||
tmp_mo=$(mktemp)
|
||||
msgfmt --output-file="$tmp_mo" "$po"
|
||||
if ! cmp -s "$mo" "$tmp_mo"; then
|
||||
echo "::warning::$mo is out of sync with $po - run locales_generator.sh"
|
||||
out_of_sync=1
|
||||
fi
|
||||
rm -f "$tmp_mo"
|
||||
done < /tmp/changed_po.txt
|
||||
if [ "$out_of_sync" -eq 1 ]; then
|
||||
echo "Some .mo files are out of sync (warning only)"
|
||||
fi
|
||||
- name: Check for tr(f-string) anti-pattern
|
||||
if: steps.changed.outputs.has_py == 'true'
|
||||
run: |
|
||||
if grep -rn --include='*.py' "tr(f['\"]" archinstall/; then
|
||||
echo "::error::Found tr(f'...') pattern. Use tr('...{}').format(...) instead"
|
||||
exit 1
|
||||
fi
|
||||
echo "No tr(f-string) anti-pattern found"
|
||||
|
||||
validate-pot:
|
||||
name: Validate base.pot is up to date
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux/archlinux:latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for changed Python files
|
||||
id: check_py
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
base="${{ github.event.pull_request.base.sha }}"
|
||||
head="${{ github.event.pull_request.head.sha }}"
|
||||
else
|
||||
base="${{ github.event.before }}"
|
||||
head="${{ github.sha }}"
|
||||
fi
|
||||
if git diff --name-only "$base" "$head" 2>/dev/null | grep -q '\.py$'; then
|
||||
echo "has_py_changes=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_py_changes=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Install gettext
|
||||
if: steps.check_py.outputs.has_py_changes == 'true'
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman --noconfirm -Sy archlinux-keyring
|
||||
pacman --noconfirm -Syyu
|
||||
pacman --noconfirm -S gettext
|
||||
- name: Check for missing translatable strings
|
||||
if: steps.check_py.outputs.has_py_changes == 'true'
|
||||
run: |
|
||||
cd archinstall
|
||||
find . -type f -name '*.py' | sort | xargs xgettext \
|
||||
--no-location --omit-header --keyword='tr' \
|
||||
-d base -o /tmp/generated.pot
|
||||
msgcmp --use-untranslated locales/base.pot /tmp/generated.pot
|
||||
run: sudo apt-get update && sudo apt-get install -y gettext
|
||||
- name: Run translation checks
|
||||
run: bash archinstall/locales/locales_generator.sh check
|
||||
|
|
|
|||
|
|
@ -1,48 +1,109 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
function update_lang() {
|
||||
file=${1}
|
||||
usage() {
|
||||
echo "Usage: ${0} <command>"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " all Regenerate base.pot and update all languages"
|
||||
echo " <lang> Regenerate base.pot and update a single language"
|
||||
echo " check Run translation validation checks"
|
||||
echo " -h, --help Show this help"
|
||||
}
|
||||
|
||||
generate_pot() {
|
||||
find . -type f -iname '*.py' | sort \
|
||||
| xargs xgettext --no-location --omit-header --keyword='tr' \
|
||||
-d base -o locales/base.pot
|
||||
}
|
||||
|
||||
update_lang() {
|
||||
local file=${1}
|
||||
echo "Updating: ${file}"
|
||||
local path
|
||||
path=$(dirname "${file}")
|
||||
msgmerge --quiet --no-location --width 512 --backup none --update "${file}" locales/base.pot
|
||||
msgfmt -o "${path}/base.mo" "${file}"
|
||||
}
|
||||
|
||||
|
||||
function generate_all() {
|
||||
cmd_generate_all() {
|
||||
generate_pot
|
||||
for file in $(find locales/ -name "base.po"); do
|
||||
update_lang "${file}"
|
||||
done
|
||||
}
|
||||
|
||||
function generate_single_lang() {
|
||||
lang_file="locales/${1}/LC_MESSAGES/base.po"
|
||||
|
||||
cmd_generate_single() {
|
||||
local lang_file="locales/${1}/LC_MESSAGES/base.po"
|
||||
if [ ! -f "${lang_file}" ]; then
|
||||
echo "Language files not found: ${lang_file}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
generate_pot
|
||||
update_lang "${lang_file}"
|
||||
}
|
||||
|
||||
cmd_check_po_syntax() {
|
||||
echo "Checking .po syntax..."
|
||||
local failed=0
|
||||
while IFS= read -r po; do
|
||||
if ! msgfmt --check --output-file=/dev/null "$po" 2>&1; then
|
||||
echo "FAIL: $po"
|
||||
failed=1
|
||||
fi
|
||||
done < <(find locales/ -name '*.po')
|
||||
if [ "$failed" -eq 1 ]; then
|
||||
echo "ERROR: some .po files have syntax errors" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "All .po files passed syntax check."
|
||||
}
|
||||
|
||||
cmd_check_no_tr_fstring() {
|
||||
echo "Checking for tr(f-string) anti-pattern..."
|
||||
if grep -rnE "tr\(\s*f['\"]" . --include='*.py'; then
|
||||
echo "ERROR: use tr('...{}').format(...) instead of tr(f'...')" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "No tr(f-string) anti-pattern found."
|
||||
}
|
||||
|
||||
cmd_check_pot_freshness() {
|
||||
# msgcmp (not diff) because base.pot carries legacy stale entries from
|
||||
# --join-existing; diff would always fail until a full cleanup is done.
|
||||
echo "Checking base.pot for missing strings..."
|
||||
find . -type f -iname '*.py' | sort \
|
||||
| xargs xgettext --no-location --omit-header --keyword='tr' \
|
||||
-d base -o /tmp/generated.pot
|
||||
if ! msgcmp --use-untranslated locales/base.pot /tmp/generated.pot; then
|
||||
echo "ERROR: base.pot is missing strings - run: locales_generator.sh all" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "base.pot contains all translatable strings."
|
||||
}
|
||||
|
||||
cmd_check() {
|
||||
local failed=0
|
||||
cmd_check_po_syntax || failed=1
|
||||
cmd_check_no_tr_fstring || failed=1
|
||||
cmd_check_pot_freshness || failed=1
|
||||
if [ "$failed" -eq 1 ]; then
|
||||
echo "Some translation checks failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "All translation checks passed."
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: ${0} <language_abbr>"
|
||||
echo "Special case 'all' for <language_abbr> builds all languages."
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
lang=${1}
|
||||
|
||||
# Update the base file containing all translatable strings
|
||||
find . -type f -iname "*.py" | xargs xgettext --join-existing --no-location --omit-header --keyword='tr' -d base -o locales/base.pot
|
||||
|
||||
case "${lang}" in
|
||||
"all") generate_all;;
|
||||
*) generate_single_lang "${lang}"
|
||||
case "${1}" in
|
||||
check) cmd_check ;;
|
||||
all) cmd_generate_all ;;
|
||||
-h|--help) usage ;;
|
||||
*) cmd_generate_single "${1}" ;;
|
||||
esac
|
||||
|
|
|
|||
Loading…
Reference in New Issue