بنقرة واحدة
qtpass-localization
QtPass localization workflow - translation files, updating, adding languages
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
QtPass localization workflow - translation files, updating, adding languages
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Documentation guide for QtPass - README, FAQ, localization
QtPass CI/CD workflow - run GitHub Actions locally with act, linters, formatters
Comprehensive guide for QtPass unit testing with Qt Test
QtPass localization audit - structural checks on .ts files (placeholders, HTML balance, mnemonics, mixed-script artifacts)
QtPass GitHub interaction - PRs, issues, branches, merging
Bug fixing workflow for QtPass - find, fix, test, PR
| name | qtpass-localization |
| description | QtPass localization workflow - translation files, updating, adding languages |
| license | GPL-3.0-or-later |
| metadata | {"audience":"developers","workflow":"localization"} |
Location: localization/localization_<lang>.ts
Qt uses Qt Linguist (.ts files) for translations.
Always re-derive the current list at the moment of work, since new locales land regularly:
ls localization/localization_*.ts | sed 's|.*localization_||;s|\.ts$||'
Per-locale completion stats (finished / unfinished-with-text / empty):
python3 -c "
import re, glob, os
for f in sorted(glob.glob('localization/localization_*.ts')):
loc = os.path.basename(f).replace('localization_','').replace('.ts','')
content = open(f).read()
fin=ut=ue=0
for m in re.finditer(r'<translation([^>]*)>(.*?)</translation>', content, re.DOTALL):
a,b = m.group(1), m.group(2)
if 'vanished' in a: continue
if 'unfinished' in a:
if b.strip(): ut += 1
else: ue += 1
else: fin += 1
print(f'{loc:8s} {fin:3d}/{fin+ut+ue} ut={ut:3d} empty={ue}')
"
unfinished-with-text means a translation exists but is flagged for native-speaker review (Weblate convention).
When source files change (strings added, moved, or refactored), run qmake to update translations:
# IMPORTANT: Run distclean first to avoid stale generated files (ui_*.h) in translations
make distclean
# Run qmake to update translations (uses lupdate internally)
qmake6
This updates all .ts files with:
<translation> tags)# Create branch
git checkout -b chore/update-localization
# Add and commit
git add localization/
git commit -m "chore: update localization source references"
# Push and create PR
git push -u origin chore/update-localization
gh pr create --title "chore: update localization source references" --body "## Summary
- Updated translation files with current source texts and line numbers
- Translations marked unfinished where source text changed
"
When you add new user-facing strings in C++:
// Use tr() for translatable strings
ui->label->setText(tr("New text here"));
localization/localization_en_US.ts in Qt Linguisttr() for all user-facing strings%1, %2, %3\n for line breaks// Good
tr("Delete %1?").arg(filename)
tr("Found %n password(s)", count)
// Avoid
tr("Delete " + filename + "?") // Can't be translated properly
Don't copy localization_en_US.ts — that imports stale English-as-translation strings that have to be cleared one by one. Bootstrap an empty skeleton and let lupdate (driven by qmake6) populate it with the current source strings as type="unfinished".
Prefer the language-only code (e.g. fa, ar, de) when the translation is the standard form understood across regions (Modern Standard Arabic, Standard German, Standard Persian).
Qt's locale fallback maps fa_IR, ar_MA, de_AT etc. to the language-only file automatically — one fa.ts covers Iran, Afghanistan (Dari), and every other Persian-speaking user.
Use the xx_YY form only when the translation is genuinely region-specific (Brazilian vs European Portuguese, Simplified vs Traditional Chinese, regional Spanish variants).
Create a 4-line skeleton (lupdate refuses to populate a 0-byte file):
cat > localization/localization_<lang>.ts <<'EOF'
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="<lang>">
</TS>
EOF
Register it in src/src.pro (alphabetically) under TRANSLATIONS +=. Note: qtpass.pro is the parent project file; the actual list lives in src/src.pro.
Run make distclean && qmake6 — the build's lupdate step populates the file with all 304 source strings as type="unfinished". Confirm via:
qmake6 2>&1 | grep "localization_<lang>.ts"
# -> Updating 'localization/localization_<lang>.ts'...
The skeleton ships as-is once the file is populated by lupdate: all entries type="unfinished", ready for Weblate translators to fill. A bootstrap PR does not need pre-filled best-effort translations — empty <translation type="unfinished"> is the deliverable.
The "fill empty entries with best-effort, mark type="unfinished"" guidance applies only when patching an existing locale flagged by reviewers — not to fresh-skeleton bootstrap PRs.
Optionally hand-edit a few high-value strings (window title, About box) via Qt Linguist before pushing, then let Weblate handle the rest.
Adding Persian (fa) for Iranian/Afghan/Tajik users involved exactly: 4-line skeleton, one TRANSLATIONS += line in src/src.pro, one make distclean && qmake6. No copy step. Result: 304 entries marked type="unfinished" (302 empty + 2 numerusform plural skeletons), ready for Weblate.
# For development (or part of build process)
lrelease localization/*.ts
# Or via qmake
make translations
QtPass uses system locale. To test:
# Linux
LANG=nl_NL ./qtpass
# Or set in QtPass settings
// Enable verbose translation debugging
QTranslator translator;
if (translator.load(QLocale(), "qtpass", "_", ":/languages")) {
qDebug() << "Loaded translation for:" << QLocale().name();
}
tr() - fix in sourceWhen merging PRs with translation updates:
# Fetch and rebase
git fetch origin
git pull origin main --rebase
# Resolve conflicts in .ts files (usually just XML merge)
# Test with qmake6
# Format with prettier if needed
npx prettier --write localization/*.ts
Always run make distclean before qmake6 when updating translations:
# Wrong - may include stale generated files in translations
qmake6
# Correct - starts fresh
make distclean
qmake6
If you added new tr() strings but they don't appear:
qmake6 to update the .ts filesWhen source strings change, translations are marked "unfinished" but may still appear correct. Always check:
%1, %2) matchesWhen merging translation PRs that conflict:
# Use theirs strategy for .ts files (they're XML, prefer incoming)
git checkout --theirs localization/localization_*.ts
git add localization/
git commit -m "Resolve merge conflict - use theirs for translations"
Translations are normally managed via Weblate. However, direct .ts edits are appropriate for:
type="unfinished" so Weblate native reviewers can refine them.klipboard/klipbordas/Tabela e kopjimit onto a single term).When in doubt, fix and mark type="unfinished" so Weblate review can iterate. Only run qmake6 to update source strings (English) when code changes — that step is mechanical and affects every locale file.
Qt UI strings use & (rendered as & in .ts XML) before a letter to mark a keyboard accelerator (e.g., &Quit underlines Q so Alt+Q triggers it). Conventions per script family:
&Letter — &File, &Edit, &Quit. Pick a non-conflicting letter; within a single menu, mnemonics must be unique.(&L) text — (&F) 文件, (&Q) 退出. Don't rely on native characters as mnemonics.(&L) text (preferred for portability) or native-letter &letter.&Покажи) work if the user's keyboard supports them, otherwise use (&L) text parens form.Preserve the source mnemonic letter in the translation when feasible, so the same shortcut works across locales. When this is not possible, choose a non-conflicting letter from the translated string.
Audit for missing mnemonics:
python3 -c "
import re, glob
mnemonic_sources = {'&Use pass','Nati&ve Git/GPG','&Show','&Hide','&Restore','&Quit','Mi&nimize','Ma&ximize'}
for f in sorted(glob.glob('localization/localization_*.ts')):
content = open(f).read()
for m in re.finditer(r'<message[^>]*>.*?<source>(.*?)</source>.*?<translation([^>]*)>(.*?)</translation>', content, re.DOTALL):
src,a,t = m.group(1), m.group(2), m.group(3)
if 'vanished' in a or src not in mnemonic_sources or not t.strip(): continue
if not re.search(r'&[^\s&;]', t):
print(f'{f}: {src} -> {t}')
"
Translation review tends to come in batches of ~5 findings at a time (CodeRabbit, GitHub AI quality findings, native-speaker comments on PRs). Standard handling:
.ts.qtpass-localization-audit skill) before pushing — placeholder format and HTML balance are non-obvious bugs that the reviewer's prose description may not catch.type="unfinished" unless a native speaker has confirmed them. Weblate finalises them on review.When static analysis flags translation issues (e.g., filename preservation):
# Fetch the PR branch
git fetch origin refs/pull/<PR>/head:pr/<PR>-fix
git checkout pr/<PR>-fix
# Fix the translation
# Edit the .ts file to preserve exact filename/token
# Commit with signing and push
git add localization/localization_<lang>.ts
git commit -S -m "fix: preserve .gpg-id filename in <lang> translation"
git push -u origin pr/<PR>-fix
Translation PRs often use squash merge:
gh pr merge <PR_NUMBER> --squash --auto --delete-branch