Features
Nine chapters, and the first one is why people switch.
Mergua is a translation management platform for teams shipping web apps — and the only one that treats a locale file the way you already treat code: on a branch, reviewed, merged.
Branches
A shared pool of keys is fine until two features touch the same screen. Then a rename on one branch breaks the other team's build, and the only fix is to stop translating until the merge lands. Mergua's branches remove that trade-off.
A graph of the branch checkout-redesign leaving the main branch, gaining 12 keys, then translations in de, fr, es, then a review, before merging back into main.
The diff comes before the merge
Added, changed and removed keys, per locale, listed the way a code review lists them. Nothing lands on main that you have not seen.
Protected branches
A protected branch cannot be renamed or deleted, and a merge cannot tidy it away afterwards. The merge target is protected for you, and cannot lose it while it is the target.
main is a default, not a rule
Point a project at develop and every merge goes there instead. A single merge can also target another open branch, for the release branch that is not ready for main yet.
Sync in, whenever you like
A branch that has been open for a week can pull the target's newer translations in at any point — from main, or from another branch. Where both sides changed the same value, the sync says how many and asks you to settle them before the merge.
Branch-scoped downloads
Add ?branch= to any export and your review deployment gets that branch's translations, not production's.
Named however you like
Mergua does not read your Git remote. The sync script passes the branch it is running on, which is why they line up in practice.
Translation editor
Three views over the same keys, because reviewing 400 strings and writing one careful sentence are not the same job. Panel for depth, inline for context, table for bulk. The source language stays on screen in all three.
Status per key — draft, translated, reviewed — as a dot in the key tree, so you can see where a locale stands without opening it. Arrow keys walk the list, Enter drops into the field, and a value saves itself half a second after you stop typing — so a long locale is a rhythm rather than a scroll. Translation memory suggests what you wrote for a near-identical key last month. Words that stay in the original — a product name, a slogan — are marked in the source text, so a translator sees which of them to leave alone before translating rather than after a review.
The other two views, and the filter that makes a locale finishable
Inline — for context
Every key of the language under the next, the source beside the field you type in, grouped by file. Writing a whole screen in one pass, where the string before matters.
Table — for bulk
Every language side by side, one row per key, editable in the cell. The key column and the header stay put while you scroll either way.
Missing — for what is left
One filter narrows all three views to the keys with no value yet. The arrow keys walk that list, so the rest of a locale is a sequence rather than a search.
AI translation
Machine translation fails on i18n for one reason: a key arrives with no sentence around it. Mergua sends the keys either side of it, so short strings land in the right sense. Translate one key or a whole locale, then review what came back.
AI output is never auto-approved — it lands unreviewed and waits for a human to sign it off. Placeholders like{count}survive the round trip, and a validator says so if they don't. Your product names go to the model as a list rather than as a wish, and the round checks its own answer against them — a translation that dropped one is written as a draft, not thrown away. Gold is reserved for AI across the whole product, so you always know what a machine wrote.
context sent with the key
- cart.summary.title
- Your cart
- cart.summary.open
- Ouvrir
- cart.summary.items
- {count} items
Because its neighbours are about a cart, open comes back as the verb, not the adjective.
Team & review
Invite a freelance translator without handing them the project. Roles restrict what someone can reach, reviewers approve or reject per key, and the discussion sits on the key it is about rather than in a thread nobody can find later.
Invitations go out by email and expire — no shared login. A rejected translation says why, and goes back to the person who wrote it. One page for what is waiting on you, across every project — branches to review, and your own work that came back. A review request, a mention and your own work coming back also reach you by email, one by one or as a daily summary, and each can be switched off. Every change is in the history with a name and a timestamp.
checkout.pay.label
Payer maintenant
checkout.pay.spinner
Traitement en cours…
Sofia — the button is 90 px wide, this will clip. Shorter?
The three emails it sends, and the switch that stops them
There is no notification centre and no unread count to work through: the queue is where you look, and an email is a shortcut back to it. Three occasions get one — somebody asking you to review a branch, somebody naming you in a comment, and a reviewer sending a change of yours back — and the two that come in bursts are one message per person per window rather than one per event.
Sent for review
Asking for a review mails everyone in the organization who could answer it, minus you. It names the project, the branch and how many changes are waiting, and lands on the queue rather than on one key. Withdrawing the request sends nothing, and asking twice sends nothing the second time.
Named in a comment
Mentions collect for five minutes and travel together, so being named four times in one editing session is one email. The window opens with the first mention and is never pushed back by the next, and a mention you have already read in the app before it closes stays out of the mail. The comment itself does not travel — the thread is where the words stay.
Sent back to you
A rejection goes to the person the change is credited to, and an approval sends nothing — one asks for something and the other is a receipt. A reviewer working down a branch collects into one email over the same five minutes, and a change you have already reworked in the meantime stays out of it. The reviewer's note does not travel either; it stays beside the change.
Yours to tune, off without signing in
One switch covers all three and a switch per kind sits under it, and whichever you keep can arrive as it happens or as one summary at 18:00 your time. The link at the foot of any of them turns the main switch off, and it does not ask you to log in first. Account email is untouched: verifying an address and resetting a password arrive whatever the switches say. What you lose is the nudge, not the work — every request, every mention and every change that came back still stands in the queue.
Multiple JSON files per language
Transloco, i18next and vue-i18n all split a locale across files — common.json, errors.json, one per area — so a screen loads only the strings it needs. Mergua keeps that split the whole way through: the files go in as they are, one editor works across them, and they come back in the same shape. A project says once, in its settings, whether it holds one file per language or several.
(The CLI and the API call one of these files a namespace.)
In your repo
common.json148 keys
errors.json36 keys
checkout.json72 keys
Every language carries the same set of files, and the import takes all of them in one pass.
In the editor
- checkout.summary.title checkout
- nav.title common
The badge is the file a key belongs to, so title in checkout is never taken for title in common.
Upload them all at once
Pick the whole folder. The import previews each file on its own — new keys, conflicts, and what it will leave alone — before anything lands, and a file that should carry a different name gets it corrected right there.
One editor, filtered by file
The key tree, the inline view and the table all take the same file filter, and every key carries the file it belongs to. Turn the filter off and you are back to the whole project.
Download one file, or all of them
Choose a file and you get that file. Choose all of them and the ZIP is laid out {locale}/{file}.json — the shape your loader already reads, with nothing to unpack by hand.
Rename and merge, without losing keys
A rename changes the file a key sits in and nothing else — the translations and the history come with it. A merge asks, per clashing key, which version survives, and records the answer in that key's history.
Pipeline sync
One shell script, fetched by curl, no package added to your project. It reads the locale files already in your repo, pushes keys Mergua has not seen, pulls finished translations back, and notices when a branch was merged.
GitLab, GitHub and Gitea — the branch name comes from the CI variable each one already sets. Or skip the script: the REST API is project-scoped, branch-aware, and returns nested or flat JSON.checkcompares the keys your code uses with the ones Mergua holds — with--strict, a key the code uses and Mergua lacks fails the build; unused keys are only reported.
$ curl -sf https://api.mergua.com/v1/mergua.sh | sh -s -- sync
• branch checkout-redesign (from CI_COMMIT_REF_NAME)
• scanned src/assets/i18n/en.json — 412 keys
↑ pushed 12 new, 0 changed
# merge detected — keys promoted to main
$ curl -sf https://api.mergua.com/v1/mergua.sh | sh -s -- check --key-manifest i18n-keys/en.json --strict
• compared 409 keys with Mergua branch checkout-redesign
WARNING: 3 key(s) unused — in Mergua, not in the code
Formats & validation
Import what you have, export what your build needs. These four are the whole list, both JSON layouts round-trip, and placeholders are checked everywhere a value arrives — in the editor while someone is still there to fix them, in the import preview before a file lands, on a CI push, and on whatever the AI hands back.
The fourth one leaves. XLIFF 2.0 is what a translation agency and its tools speak, so a language goes out as one document and comes back as one — and what stays in your repository is still JSON.
- JSON
- JSON · flat
- ARB
- XLIFF 2.0
The key cart.checkout, German value Zur Kasse, in the four formats Mergua downloads. Nested and flat JSON carry only the value. ARB adds the description, the character limit and the English original. XLIFF 2.0 adds the review state as well.
The two JSON files carry the value and nothing else, which is what your app loads. ARB adds the description, the limit and the original beside it; XLIFF adds the review state too, because it is the document a translator’s tool works in.
Placeholder validation
Single and double braces, printf (%1$s), i18next references ($t(key)) and ICU plurals. If the source has {count} and the translation doesn't — or renamed it — you hear about it here, not in a runtime error.
Placeholder missing
cart.summary.items · fr · expected {count}
Progress per locale
The bar changes colour as a locale fills up, so “are we ready to ship French?” is a glance, not a query.
- de100%
- fr82%
- es44%
Seven more checks, same rule
Each one compares the translation with its original, in the editor, on every upload and on a CI push. They report and never block — and a finding that is right for this string can be ignored for exactly this text, until either side changes.
Character limit Protected terms Leading or trailing whitespace Double spaces Empty but marked translated Final punctuation ICU syntax
Webhooks
The chapter above is your pipeline calling Mergua. This is Mergua calling your pipeline: one HTTPS POST to a URL of yours when a project's translations change or a branch is merged. The build starts on the change rather than on a schedule, and nothing has to poll to find out.
A branch filter, and it starts at main — a release pipeline hears about main, not about every keystroke on somebody's feature branch. An import of four hundred keys is one delivery, not four hundred — changes are grouped per language and file over a short window before it fires. Every delivery is signed with a secret only your endpoint and Mergua hold, and the body says what moved instead of carrying the text — the pull that runs next fetches the files.
X-Mergua-Event: translations.changed
X-Mergua-Signature: sha256=4f1c9b2e…
{
"branch": "main",
"locale": "de",
"counts": { "added": 12, "updated": 3, "deleted": 0 }
}Your coding agent
Mergua speaks the Model Context Protocol, so Claude Code, Cursor or Codex can work on the translations from inside your repository. One URL and a project API key — nothing to install. The agent that just wrote the component adds the key, writes the German, and tells you the branch is waiting for review.
Six tools: the languages and what is still missing, a search across keys and translated text, the files, a branch's readiness — and two that write. The one call that can delete a key is refused on the main line and needs a branch, so an agent cannot reshape the file everyone is already loading. Values take a branch too — point the round at one and it arrives as something a person reviews. The key names the project, so no tool takes one as an argument. An agent cannot address the wrong project, and a second project is a second key.
→ list_locales
de 184/184 · fr 172/184 · es 0/184
→ sync_keys branch: checkout-redesign
created 3 · deleted 1
→ set_translations locale: de · branch: checkout-redesign
This page is the tour. The docs are the manual.
Already using Mergua and looking for the exact behaviour of a setting? /docs/features covers the same ground field by field.
Try it on one branch.
The free tier is enough to run a real feature through the whole loop.
Create your account