l10n-2.56.0-v1

* tag 'l10n-2.56.0-v1' of https://github.com/git-l10n/git-po:
  l10n: af: fix review comments
  l10n: af: add Afrikaans translation
  l10n: pt_BR: add Brazilian Portuguese translation
  l10n: restore translations after upstream revert
  l10n: uk: add 2.56 translation
  l10n: Update Catalan Translation
  l10n: zh_CN: adopt review suggestions
  l10n: zh_CN: updated translation for 2.56
  l10n: ga.po: update for Git 2.56
  l10n: tr: Update Turkish translations
  l10n: fr: update translation for git 2.56.0
  l10n: sv.po: Update Swedish translation
  l10n: po-id for 2.56
  l10n: AGENTS.md: zsh-safe review result cleanup
  l10n: AGENTS.md: use JSON intermediates in workflow
  l10n: AGENTS.md: count PO entries with git-po-helper stat -c
  l10n: AGENTS.md: require git-po-helper >= 0.9.1
  l10n: AGENTS.md: fix counter fallbacks
This commit is contained in:
Junio C Hamano committed 2026-09-27 12:33:43 -07:00
commit 34f06850c1
12 files changed
+61415 -2898

No files matched your search

+88 -126
View File
@@ -93,17 +93,9 @@ msgstr ""
**CRITICAL**: Do **not** use `grep '^msgstr ""'` to find untranslated entries;
multi-line `msgstr` blocks use the same opening line, so grep gives false
positives. Use `msgattrib` (next section).
### Locating untranslated, fuzzy, and obsolete entries
Use `msgattrib` to list untranslated, fuzzy, and obsolete entries. Task 3
(translating `po/XX.po`) uses these commands.
- **Untranslated**: `msgattrib --untranslated --no-obsolete po/XX.po`
- **Fuzzy**: `msgattrib --only-fuzzy --no-obsolete po/XX.po`
- **Obsolete** (`#~`): `msgattrib --obsolete --no-wrap po/XX.po`
positives. Use `git-po-helper msg-select` with `--untranslated` / `--fuzzy`
(see Task 3), or `msgattrib` (e.g. `--untranslated`, `--only-fuzzy`,
`--obsolete`).
### Translating fuzzy entries
@@ -238,8 +230,19 @@ On failure, `msgfmt` prints the line number; fix the PO at that line.
[git-po-helper](https://github.com/git-l10n/git-po-helper) supports Git l10n with
**quality checking** (git-l10n PR conventions) and **AI-assisted translation**
(subcommands for automated workflows). Housekeeping tasks in this document use
it when available; otherwise rely on gettext tools.
(subcommands for automated workflows). Task 3 (translating) and Task 4
(reviewing) **require** `git-po-helper` **>= 0.9.1**. Task 1 and Task 2 use
`make` / gettext tooling instead.
**Version check**: Before Task 3 or Task 4, run:
```shell
git-po-helper version --ge 0.9.1
```
If the command is missing or the exit status is non-zero, print the error (use
the command's own output when available) and **refuse** the task; do not
continue.
#### Splitting large PO files
@@ -288,10 +291,10 @@ changed entries; otherwise it contains a valid PO header.
git-po-helper compare po/XX.po -o po/out.po
# Get full context of changes in a specific commit (parent vs commit)
git-po-helper compare --commit <commit> po/XX.po -o po/out.po
git-po-helper compare --json --commit <commit> po/XX.po -o po/out.json
# Get full context of changes since a commit (commit vs working tree)
git-po-helper compare --since <commit> po/XX.po -o po/out.po
git-po-helper compare --json --since <commit> po/XX.po -o po/out.json
# Get full context between two commits
git-po-helper compare -r <commit1>..<commit2> po/XX.po -o po/out.po
@@ -476,42 +479,32 @@ When asked to update `po/XX.po` (or the like):
### Task 3: Translating po/XX.po
To translate `po/XX.po`, use the steps below. The script uses gettext or
`git-po-helper` depending on what is installed; JSON export (when available)
supports batch translation rather than per-entry work.
To translate `po/XX.po`, use the steps below. **Require** `git-po-helper` (see
"Using git-po-helper" for the version check); without a passing check, refuse
the task. Batches are GETTEXT JSON for translation.
**Workflow loop**: Steps 1→2→3→4→5→6→7 form a loop. After step 6 succeeds,
**always** go to step 7, which returns to step 1. The **only** exit to step 8
is when step 2 finds `po/l10n-pending.po` empty. Do not skip step 7 or jump to
is when step 2 finds `po/l10n-pending.json` empty. Do not skip step 7 or jump to
step 8 after step 6.
1. **Extract entries to translate**: **Directly execute** the script below—it is
authoritative; do not reimplement. It generates `po/l10n-pending.po` with
authoritative; do not reimplement. It generates `po/l10n-pending.json` with
messages that need translation.
```shell
l10n_extract_pending () {
test $# -ge 1 || { echo "Usage: l10n_extract_pending <po-file>" >&2; return 1; }
PO_FILE="$1"
PENDING="po/l10n-pending.po"
PENDING_FUZZY="${PENDING}.fuzzy"
PENDING_REFER="${PENDING}.fuzzy.reference"
PENDING_UNTRANS="${PENDING}.untranslated"
PENDING="po/l10n-pending.json"
rm -f "$PENDING"
if command -v git-po-helper >/dev/null 2>&1
git-po-helper msg-select --json --untranslated --fuzzy --no-obsolete -o "$PENDING" "$PO_FILE"
ENTRY_COUNT=$(git-po-helper stat -c "$PENDING" 2>/dev/null || true)
ENTRY_COUNT=${ENTRY_COUNT:-0}
if test "$ENTRY_COUNT" -gt 0
then
git-po-helper msg-select --untranslated --fuzzy --no-obsolete -o "$PENDING" "$PO_FILE"
else
msgattrib --untranslated --no-obsolete "$PO_FILE" >"${PENDING_UNTRANS}"
msgattrib --only-fuzzy --no-obsolete --clear-fuzzy --empty "$PO_FILE" >"${PENDING_FUZZY}"
msgattrib --only-fuzzy --no-obsolete "$PO_FILE" >"${PENDING_REFER}"
msgcat --use-first "${PENDING_UNTRANS}" "${PENDING_FUZZY}" >"$PENDING"
rm -f "${PENDING_UNTRANS}" "${PENDING_FUZZY}"
fi
if test -s "$PENDING"
then
msgfmt --stat -o /dev/null "$PENDING" || true
git-po-helper stat "$PENDING" || true
echo "Pending file is not empty; there are still entries to translate."
else
echo "No entries need translation."
@@ -522,30 +515,28 @@ step 8 after step 6.
l10n_extract_pending po/XX.po
```
2. **Check generated file**: If `po/l10n-pending.po` is empty or does not exist,
translation is complete; go to step 8. Otherwise proceed to step 3.
2. **Check generated file**: If `po/l10n-pending.json` has no content entries
(or does not exist), translation is complete; go to step 8. Otherwise proceed
to step 3.
3. **Prepare one batch for translation**: Batching keeps each run small so the
model can complete translation within limited context. **BEFORE translating**,
**directly execute** the script below—it is authoritative; do not reimplement.
Based on which file the script produces: if `po/l10n-todo.json` exists, go to
step 4a; if `po/l10n-todo.po` exists, go to step 4b.
It writes `po/l10n-todo.json`; then go to step 4.
```shell
l10n_one_batch () {
test $# -ge 1 || { echo "Usage: l10n_one_batch <po-file> [min_batch_size]" >&2; return 1; }
PO_FILE="$1"
min_batch_size=${2:-100}
PENDING="po/l10n-pending.po"
PENDING="po/l10n-pending.json"
TODO_JSON="po/l10n-todo.json"
TODO_PO="po/l10n-todo.po"
DONE_JSON="po/l10n-done.json"
DONE_PO="po/l10n-done.po"
rm -f "$TODO_JSON" "$TODO_PO" "$DONE_JSON" "$DONE_PO"
ENTRY_COUNT=$(grep -c '^msgid ' "$PENDING" 2>/dev/null || echo 0)
ENTRY_COUNT=$((ENTRY_COUNT > 0 ? ENTRY_COUNT - 1 : 0))
DONE_PO_TMP="po/l10n-done.po.tmp"
rm -f "$TODO_JSON" "$DONE_JSON" "$DONE_PO_TMP"
ENTRY_COUNT=$(git-po-helper stat -c "$PENDING" 2>/dev/null || true)
ENTRY_COUNT=${ENTRY_COUNT:-0}
if test "$ENTRY_COUNT" -gt $min_batch_size
then
if test "$ENTRY_COUNT" -gt $((min_batch_size * 8))
@@ -557,39 +548,18 @@ step 8 after step 6.
else
NUM=$min_batch_size
fi
BATCHING=1
git-po-helper msg-select --json --head "$NUM" -o "$TODO_JSON" "$PENDING"
echo "Processing batch of $NUM entries (out of $ENTRY_COUNT remaining)"
else
NUM=$ENTRY_COUNT
BATCHING=
fi
if command -v git-po-helper >/dev/null 2>&1
then
if test -n "$BATCHING"
then
git-po-helper msg-select --json --head "$NUM" -o "$TODO_JSON" "$PENDING"
echo "Processing batch of $NUM entries (out of $ENTRY_COUNT remaining)"
else
git-po-helper msg-select --json -o "$TODO_JSON" "$PENDING"
echo "Processing all $ENTRY_COUNT entries at once"
fi
else
if test -n "$BATCHING"
then
awk -v num="$NUM" '/^msgid / && count++ > num {exit} 1' "$PENDING" |
tac | awk '/^$/ {found=1} found' | tac >"$TODO_PO"
echo "Processing batch of $NUM entries (out of $ENTRY_COUNT remaining)"
else
cp "$PENDING" "$TODO_PO"
echo "Processing all $ENTRY_COUNT entries at once"
fi
git-po-helper msg-select --json -o "$TODO_JSON" "$PENDING"
echo "Processing all $ENTRY_COUNT entries at once"
fi
}
# Prepare one batch; shrink 2nd arg when batches exceed agent capacity.
l10n_one_batch po/XX.po 100
```
4a. **Translate JSON batch** (`po/l10n-todo.json` → `po/l10n-done.json`):
4. **Translate JSON batch** (`po/l10n-todo.json` → `po/l10n-done.json`):
- **Task**: Translate `po/l10n-todo.json` (input, GETTEXT JSON) into
`po/l10n-done.json` (output, GETTEXT JSON). See the "GETTEXT JSON format"
@@ -604,32 +574,20 @@ step 8 after step 6.
(for plural entries); clear the fuzzy flag (omit or set `fuzzy` to `false`).
Do **not** modify `msgid` or `msgid_plural`.
4b. **Translate PO batch** (`po/l10n-todo.po` → `po/l10n-done.po`):
- **Task**: Translate `po/l10n-todo.po` (input, GETTEXT PO) into
`po/l10n-done.po` (output, GETTEXT PO).
- **Reference glossary**: Read the glossary from the pending file header
(see "Glossary Section" above) and use it for consistent terminology.
- **When translating**: Follow the "Quality checklist" above for correctness
and quality. Preserve escape sequences (`\n`, `\"`, `\\`, `\t`), placeholders,
and quotes as in `msgid`. Modify `msgstr` and `msgstr[n]` (for plural
entries); remove the `#, fuzzy` tag from comments when done. Do **not**
modify `msgid` or `msgid_plural`.
5. **Validate `po/l10n-done.po`**:
5. **Validate `po/l10n-done.po.tmp`**:
Run the validation script below. If it fails, fix per the errors and notes,
re-run until it succeeds.
```shell
l10n_validate_done () {
DONE_PO="po/l10n-done.po"
DONE_PO_TMP="po/l10n-done.po.tmp"
DONE_JSON="po/l10n-done.json"
PENDING="po/l10n-pending.po"
PENDING="po/l10n-pending.json"
if test -f "$DONE_JSON" && { ! test -f "$DONE_PO" || test "$DONE_JSON" -nt "$DONE_PO"; }
if test -f "$DONE_JSON" && { ! test -f "$DONE_PO_TMP" || test "$DONE_JSON" -nt "$DONE_PO_TMP"; }
then
git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO" "$DONE_JSON" || {
git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO_TMP" "$DONE_JSON" || {
echo "ERROR [JSON to PO conversion]: Fix $DONE_JSON and re-run." >&2
return 1
}
@@ -637,22 +595,22 @@ step 8 after step 6.
# Check 1: msgid should not be modified
MSGID_OUT=$(git-po-helper compare -q --msgid --assert-no-changes \
"$PENDING" "$DONE_PO" 2>&1)
"$PENDING" "$DONE_PO_TMP" 2>&1)
MSGID_RC=$?
if test $MSGID_RC -ne 0 || test -n "$MSGID_OUT"
then
echo "ERROR [msgid modified]: The following entries appeared after" >&2
echo "translation because msgid was altered. Fix in $DONE_PO." >&2
echo "translation because msgid was altered. Fix in $DONE_PO_TMP." >&2
echo "$MSGID_OUT" >&2
return 1
fi
# Check 2: PO format (see "Validating PO File Format" for error handling)
MSGFMT_OUT=$(msgfmt --check -o /dev/null "$DONE_PO" 2>&1)
MSGFMT_OUT=$(msgfmt --check -o /dev/null "$DONE_PO_TMP" 2>&1)
MSGFMT_RC=$?
if test $MSGFMT_RC -ne 0
then
echo "ERROR [PO format]: Fix errors in $DONE_PO." >&2
echo "ERROR [PO format]: Fix errors in $DONE_PO_TMP." >&2
echo "$MSGFMT_OUT" >&2
return 1
fi
@@ -662,46 +620,43 @@ step 8 after step 6.
l10n_validate_done
```
If the script fails, fix **directly in `po/l10n-done.po`**. Re-run
If the script fails, fix **directly in `po/l10n-done.po.tmp`**. Re-run
`l10n_validate_done` until it succeeds. Editing `po/l10n-done.json` is not
recommended because it adds an extra JSON-to-PO conversion step. Use the
error message to decide:
- **`[msgid modified]`**: The listed entries have altered `msgid`; restore
them to match `po/l10n-pending.po`.
them to match `po/l10n-pending.json`.
- **`[PO format]`**: `msgfmt` reports line numbers; fix the errors in place.
See "Validating PO File Format" for common issues.
6. **Merge translation results into `po/XX.po`**: Run the script below. If it
fails, fix the file the error names: **`[JSON to PO conversion]`** →
`po/l10n-done.json`; **`[msgcat merge]`** → `po/l10n-done.po`. Re-run until
`po/l10n-done.json`; **`[msgcat merge]`** → `po/l10n-done.po.tmp`. Re-run until
it succeeds.
```shell
l10n_merge_batch () {
test $# -ge 1 || { echo "Usage: l10n_merge_batch <po-file>" >&2; return 1; }
PO_FILE="$1"
DONE_PO="po/l10n-done.po"
DONE_PO_TMP="po/l10n-done.po.tmp"
DONE_JSON="po/l10n-done.json"
MERGED="po/l10n-done.merged"
PENDING="po/l10n-pending.po"
PENDING_REFER="${PENDING}.fuzzy.reference"
TODO_JSON="po/l10n-todo.json"
TODO_PO="po/l10n-todo.po"
if test -f "$DONE_JSON" && { ! test -f "$DONE_PO" || test "$DONE_JSON" -nt "$DONE_PO"; }
if test -f "$DONE_JSON" && { ! test -f "$DONE_PO_TMP" || test "$DONE_JSON" -nt "$DONE_PO_TMP"; }
then
git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO" "$DONE_JSON" || {
git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO_TMP" "$DONE_JSON" || {
echo "ERROR [JSON to PO conversion]: Fix $DONE_JSON and re-run." >&2
return 1
}
fi
msgcat --use-first "$DONE_PO" "$PO_FILE" >"$MERGED" || {
echo "ERROR [msgcat merge]: Fix errors in $DONE_PO and re-run." >&2
msgcat --use-first "$DONE_PO_TMP" "$PO_FILE" >"$MERGED" || {
echo "ERROR [msgcat merge]: Fix errors in $DONE_PO_TMP and re-run." >&2
return 1
}
mv "$MERGED" "$PO_FILE"
rm -f "$TODO_JSON" "$TODO_PO" "$DONE_JSON" "$DONE_PO" "$PENDING_REFER"
rm -f "$TODO_JSON" "$DONE_JSON" "$DONE_PO_TMP"
}
# Run the merge. Example: l10n_merge_batch po/zh_CN.po
l10n_merge_batch po/XX.po
@@ -726,15 +681,16 @@ commit. When asked to review, follow the steps below.
**Workflow**: Follow steps in order. Do **NOT** use `git show`, `git diff`,
`git format-patch`, or similar to get changes—they break PO context; use **only**
`git-po-helper compare` for extraction. Without `git-po-helper`, refuse the task.
Steps 3→4→5→6→7 loop: after step 6, **always** go to step 7 (back to step 3).
The **only** ways to step 8 are when step 4 finds `po/review-todo.json` missing
or empty (no batch left to review), or when step 1 finds `po/review-result.json`
already present.
`git-po-helper compare` for extraction. **Require** `git-po-helper` (see
"Using git-po-helper" for the version check); without a passing check, refuse
the task. Steps 3→4→5→6→7 loop: after step 6, **always** go to step 7 (back to
step 3). The **only** ways to step 8 are when step 4 finds `po/review-todo.json`
missing or empty (no batch left to review), or when step 1 finds
`po/review-result.json` already present.
1. **Check for existing review (resume support)**: Evaluate the following in order:
- If `po/review-input.po` does **not** exist, proceed to step 2 (Extract
- If `po/review-input.json` does **not** exist, proceed to step 2 (Extract
entries) for a fresh start.
- Else If `po/review-result.json` exists, go to step 8 (only after loop exits).
- Else If `po/review-done.json` exists, go to step 6 (Rename result).
@@ -742,9 +698,9 @@ already present.
batch).
- Else go to step 3 (Prepare one batch).
2. **Extract entries**: Run `git-po-helper compare` with the desired range and
redirect the output to `po/review-input.po`. See "Comparing PO files for
translation and review" under git-po-helper for options.
2. **Extract entries**: Run `git-po-helper compare --json` with the desired range
and write the output to `po/review-input.json` (use `-o`). See "Comparing PO
files for translation and review" under git-po-helper for options.
3. **Prepare one batch**: Batching keeps each run small so the model can
complete review within limited context. **Directly execute** the script
@@ -753,27 +709,32 @@ already present.
```shell
review_one_batch () {
min_batch_size=${1:-100}
INPUT_PO="po/review-input.po"
PENDING="po/review-pending.po"
INPUT_JSON="po/review-input.json"
PENDING="po/review-pending.json"
TODO="po/review-todo.json"
DONE="po/review-done.json"
BATCH_FILE="po/review-batch.txt"
if test ! -f "$INPUT_PO"
if test ! -f "$INPUT_JSON"
then
rm -f "$TODO"
echo >&2 "cannot find $INPUT_PO, nothing for review"
echo >&2 "cannot find $INPUT_JSON, nothing for review"
return 1
fi
if test ! -f "$PENDING" || test "$INPUT_PO" -nt "$PENDING"
if test ! -f "$PENDING" || test "$INPUT_JSON" -nt "$PENDING"
then
rm -f "$BATCH_FILE" "$TODO" "$DONE"
# zsh aborts on unmatched globs; allow empty expansion here.
if test -n "$ZSH_VERSION"
then
setopt local_options null_glob
fi
rm -f po/review-result*.json
cp "$INPUT_PO" "$PENDING"
rm -f "$BATCH_FILE" "$TODO" "$DONE"
cp "$INPUT_JSON" "$PENDING"
fi
ENTRY_COUNT=$(grep -c '^msgid ' "$PENDING" 2>/dev/null || echo 0)
ENTRY_COUNT=$((ENTRY_COUNT > 0 ? ENTRY_COUNT - 1 : 0))
ENTRY_COUNT=$(git-po-helper stat -c "$PENDING" 2>/dev/null || true)
ENTRY_COUNT=${ENTRY_COUNT:-0}
if test "$ENTRY_COUNT" -eq 0
then
rm -f "$TODO"
@@ -796,12 +757,13 @@ already present.
NUM=$ENTRY_COUNT
fi
BATCH=$(cat "$BATCH_FILE" 2>/dev/null || echo 0)
BATCH=$(cat "$BATCH_FILE" 2>/dev/null || true)
BATCH=${BATCH:-0}
BATCH=$((BATCH + 1))
echo "$BATCH" >"$BATCH_FILE"
git-po-helper msg-select --json --head "$NUM" -o "$TODO" "$PENDING"
git-po-helper msg-select --since "$((NUM + 1))" -o "${PENDING}.tmp" "$PENDING"
git-po-helper msg-select --json --since "$((NUM + 1))" -o "${PENDING}.tmp" "$PENDING"
mv "${PENDING}.tmp" "$PENDING"
echo "Processing batch $BATCH ($NUM entries out of $ENTRY_COUNT)"
}
+9 -1
View File
@@ -1,6 +1,10 @@
Core Git translation language teams
(please keep the list sorted alphabetically on language field)
Language: af (Afrikaans)
Repository: https://github.com/Topsy2003Turvey/git-po
Leader: Stéfan Driaan Turvey <stefanturvey1912@gmail.com>
Language: bg (Bulgarian)
Repository: https://github.com/git-l10n/git-po
Leader: Alexander Shopov <ash@kambanaria.org>
@@ -31,7 +35,7 @@ Members: Sébastien Helleu <flashcode@flashtux.org>
Language: ga (Irish)
Repository: https://github.com/aindriu80/git-po
Members: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Leader: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Language: id (Indonesian)
Repository: https://github.com/bagasme/git-po
@@ -54,6 +58,10 @@ Language: pl (Polish)
Repository: https://github.com/Arusekk/git-po
Members: Arusekk <arek_koz@o2.pl>
Language: pt_BR (Portuguese - Brazil)
Repository: https://github.com/Daniel-Pereira-Linux/git-po
Leader: Daniel Pereira <danielmaraboo@gmail.com>
Language: pt_PT (Portuguese - Portugal)
Repository: https://gitlab.com/alexandre1985/git-pt/
Members: Daniel Santos <dacs.git@brilhante.top>
+26353
View File
File diff suppressed because it is too large. Load diff
+1713 -780
View File
File diff suppressed because it is too large. Load diff
+1054 -388
View File
File diff suppressed because it is too large. Load diff
+882 -241
View File
File diff suppressed because it is too large. Load diff
+1072 -302
View File
File diff suppressed because it is too large. Load diff
+26593
View File
File diff suppressed because it is too large. Load diff
+850 -245
View File
File diff suppressed because it is too large. Load diff
+903 -285
View File
File diff suppressed because it is too large. Load diff
+863 -236
View File
File diff suppressed because it is too large. Load diff
+1035 -294
View File
File diff suppressed because it is too large. Load diff