Skip to main content
← Back to Blog
·Oscar Green

Two issues, two maintainer fixes: how NocoDB repaired its broken translations

NocoDB's locale files had translated placeholder names and 182 plurals whose separator had been turned into <unk>. Two issues, two maintainer PRs, and what the fixes say about machine-translation pipelines.

i18ncase-studyopen-sourcevue-i18n

Key takeaway

NocoDB's locale files had translated placeholder names and 182 plurals whose separator had been turned into <unk>. Two issues, two maintainer PRs, and what the fixes say about machine-translation pipelines.

NocoDB is an open-source Airtable alternative with about 65,000 GitHub stars. Its interface ships in 39 languages, synced from Crowdin into packages/nc-gui/lang/*.json and rendered with vue-i18n.

Between September 12 and October 1 I filed two issues against those files. Both were fixed by NocoDB's own maintainers: the second one 89 minutes after I opened it.

The first issue: placeholder names that got translated

vue-i18n only substitutes the exact token the source string uses. If the English says {search}, the French has to say {search} too. In #14573 the token itself had been translated:

localevalue
enSearch {search} field
frRecherche {recherche} colonne
itCerca {Cerca} colonna
ptPesquisar {pesquisar} Coluna

French, Italian and Portuguese users saw the literal {recherche} instead of the field name. Another string dropped all three of its variables ({activePlan}, {limit}, {plan}), so French users were told they were on the free plan whatever plan they actually had.

Because the files come from Crowdin, I filed an issue rather than a PR. A direct edit would be overwritten on the next sync. A NocoDB maintainer opened #14581 two days later. It touched all 39 locale files (300 lines) and merged on September 15.

The second issue: 182 plurals with the separator replaced

vue-i18n plurals are singular | plural. In #14717 the | had been replaced in 182 strings across 13 locales, by <unk>, ●, ・, +, ├, £, -, ww, a full-width |, or a plain space. With the separator gone, vue-i18n sees one form and prints both sentences together:

  • Swedish, 3 records: "3 filter 3 filter"
  • Portuguese: "Você excluiu registros 3 + Você excluiu registros 3"

<unk> is what a machine-translation model emits for a token it cannot represent. The pipe was going through MT as ordinary text.

Three minutes after I filed it, a NocoDB team member confirmed it in their enterprise tree too, with their own scan. They also found something I had missed: two English strings, 'Log In | NocoDB' and 'Join | NocoDB', use a literal pipe. vue-i18n splits on it anyway, so the English page title renders as Log In and drops | NocoDB. My checker had skipped those keys because they have no variable. That was the right call for the translations and the wrong one for the source.

The fix, #14718, was opened 33 minutes after the issue and merged at 10:47 UTC, 89 minutes after filing. All 13 locales were corrected.

What held, and what didn't

  • Held: on 2026-10-11, after four "sync translations with en.json" commits (Oct 2, 5, 7 and 9), a check of develop finds 0 placeholder or plural errors in the corrected locales, and no <unk> in sv.json.
  • Still open: the English title.headLogin / title.headJoin pipes are unchanged. That is a source-string fix, separate from the translation one, and it's NocoDB's call.
  • No CI check yet. I suggested two ways to catch a recurrence after each Crowdin sync. One was a rule in their own check: same number of | as English when the English has a variable. The other was an off-the-shelf command. Neither has been adopted, and that's fine. The fix is theirs either way.

What this says about MT pipelines

Both bugs have one cause. Markup that means something to the runtime ({variable}, |) went through translation as if it were prose. The model translated the variable name in one case and mangled the separator in the other. Neither breaks JSON, and neither changes the key set, so a key-diff check passes both.

The defence is cheap: protect those tokens before MT (Crowdin's placeholder settings), and check after it. The check compares each translation's variables and plural separators with the source string's.

Timeline

date (UTC)event
Sep 12#14573 filed: translated and dropped variables (fr/it/pt/es)
Sep 14maintainer opens #14581 (39 files)
Sep 15#14581 merged
Oct 1 09:18#14717 filed: 182 plural separators, 13 locales
Oct 1 09:21maintainer confirms, finds the English title bug
Oct 1 09:51maintainer opens #14718
Oct 1 10:47#14718 merged
Oct 2–9four translation-sync commits to the locale files
Oct 11re-checked on develop: still clean

I maintain Shipi18n, the open-source checker that found these. Every finding was verified by hand against develop before filing. The full list of repos and strings is at shipi18n.com/oss.

O

Oscar Green

The Shipi18n team builds tools that help developers ship multilingual apps faster. We write about i18n best practices, localization automation, and translation engineering.