Choosing Weblate's Write-Back Strategy: Direct Push vs. Pull-Request Flow
A decision guide for engineering teams adopting self-hosted Weblate: compare direct-push vs. pull-request write-back, merge-style trade-offs, and a staging validation plan.
07 Jul 2026, 15:02 UTC

The Decision You Face
When self-hosting Weblate, the primary architectural choice is how translations return to version control. Weblate can push commits directly to a branch or export changes through a pull-request/merge-request (PR/MR) pipeline. The right choice depends on your code-review policy, CI gating requirements, and how much latency your localization workflow can tolerate.
Constraints That Shape the Choice
- Branch protection rules: If
mainor release branches require PR reviews and status checks, direct push to those branches is impossible. - Reviewer capacity: High-volume locales (dozens of translators, hundreds of strings per day) can overwhelm a manual PR queue.
- CI feedback loop: Direct push demands that CI catches malformed placeholders, broken plural forms, and syntax errors before they reach production.
- Legal/CLA requirements: Some projects mandate a contributor-license-agreement trail that only a PR/MR workflow provides.
Option Comparison
| Dimension | Direct Push to Branch | Pull-Request / Merge-Request Export |
|---|---|---|
| Latency (save → commit) | Seconds to minutes (configurable) | Minutes to hours (review + merge) |
| Human review of files | None at VCS level; relies on Weblate internal reviews | Required by branch policy |
| Merge-conflict risk | Low if Weblate owns a dedicated branch | Moderate on long-lived PRs |
| Maintainer toil | Minimal after CI is tuned | Ongoing PR triage and merge |
| CI load | High frequency; needs debouncing | Lower frequency; one run per PR |
| Audit trail / CLA | Commit authorship only | PR metadata and sign-offs |
Trade-Offs in Depth
Direct Push
Weblate commits changes to a designated branch—commonly l10n/main. The commit message template is configurable via COMMIT_MESSAGE in component settings, and you can set AGE_BEFORE_COMMIT (default 24 hours) to batch changes and reduce VCS noise.
Risk: A translator saves a string with a missing %s placeholder. Without a PR review, that commit lands on the branch. Your CI must run msgfmt --check and placeholder linters on every push to prevent production breakage.
Mitigation: Enable Weblate's built-in quality checks (placeholders, plural forms, XML tags) and the review workflow (suggestions + reviewer approval) in Component → Quality checks.
Pull-Request / Merge-Request Flow
When enabled (via Component → Version control → Push on commit set to Create pull request), Weblate opens a PR/MR for each batch of changes. This integrates with GitHub, GitLab, Gitea, Bitbucket, Gerrit, and Pagure.
Risk: Long-lived PRs diverge from upstream. If developers modify the same .po or .xliff files, Weblate's next sync will either rebase or create a merge commit, depending on the Merge style setting.
Mitigation: Keep PR lifetime short. Set a low AGE_BEFORE_COMMIT (e.g., 1–2 hours) so batches are small. Use Merge style: merge on shared PR branches to avoid history rewriting.
Merge Style: Upstream Synchronization
The Merge style (Component → Version control → Merge style) determines how Weblate incorporates upstream changes:
- Merge (default): Creates a merge commit. Safe, preserves history, but adds noise.
- Rebase: Replays Weblate's commits on top of upstream. Linear history, but requires force-push permission. Dangerous if other humans commit to the same branch.
- Fast-forward only: Aborts if a merge commit would be needed, forcing manual intervention.
Hybrid Pattern: Direct Push to l10n Branch + Gated Promotion
Many teams adopt a middle ground to balance speed and safety:
- Weblate pushes directly to a dedicated
l10n/mainbranch. - CI runs on every push:
msgfmt --checkand placeholder validation. - An automated job or weekly human action opens a PR from
l10n/main→mainafter CI passes. - The promotion PR undergoes the standard code review and CLA checks.
Concrete Validation Plan
Run this sequence on a throwaway repository to test your chosen strategy.
1. Spin Up Staging Weblate
# docker-compose.yml (minimal)
version: '3.8'
services:
weblate:
image: weblate/weblate:latest
environment:
- WEBLATE_ADMIN_EMAIL=[contact removed]
- WEBLATE_ADMIN_PASSWORD=changeme
- POSTGRES_HOST=db
- REDIS_HOST=redis
ports:
- "8000:8080"
volumes:
- weblate-data:/app/data
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=weblate
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis:7
volumes:
- redis-data:/data
volumes:
weblate-data:
pg-data:
redis-data:
Run docker compose up -d and access http://localhost:8000.
2. Create Test Components
- Component A: Direct push to branch
l10n-direct. SetAGE_BEFORE_COMMIT=300(5 min). - Component B: PR export to test repo. Set
AGE_BEFORE_COMMIT=300, Merge style: merge.
3. Simulate and Inspect
Translate 5 strings in each component. Introduce one intentional error (e.g., a missing %s). Wait for the commit delay, then inspect the VCS:
# Clone the test repo
git clone <test-repo-url> test-clone
cd test-clone
git log --oneline --all --graph -20
# Verify the broken translation fails CI
git checkout l10n-direct
msgfmt --check -o /dev/null locale/*/LC_MESSAGES/app.po
4. Simulate Upstream Conflict
Modify the same .po file on main, push it, and then trigger Repository → Update in Weblate. Observe if the Merge style (Rebase vs Merge) produces the expected history shape.
Limitations and Version Notes
- Configuration keys (
AGE_BEFORE_COMMIT,COMMIT_MESSAGE) may vary between Weblate 4.x and 5.x. Check the API schema for your specific version. - Force-push rebases can erase commits from other contributors. Restrict rebase to branches Weblate exclusively owns.
- High-frequency pushes can hammer CI. Implement a quiet-period debounce in your pipeline (e.g., using a scheduled trigger instead of a push trigger).
How to Verify in Production
- Enable the Audit log (Admin → Audit log) to track write-back events.
- Add a CI job that posts translation validation results back to the PR or as a commit status.
- Monitor commit volume via
git log --since="1 week ago" --oneline l10n-direct | wc -land adjustAGE_BEFORE_COMMITaccordingly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.