How user bugs shaped Tolgee's AI translation prompt

·

...

Dmitrii Bocharov

Senior Software Developer

Upgrading our production Kubernetes cluster

Tolgee's AI translator never translates a string alone. It also sees the key name, a screenshot, the glossary, similar translations and more. This context is why the translations are good. But every piece of context is also a new way for the model to go wrong.

This article shows five real bugs that our users reported, and how we changed the prompt to fix each one. We did not expect any of them. Some are quite funny, once you know the reason.

One word in curly braces

In June 2026, a user opened issue #3752. Their app showed the English string in {duration}. Tolgee's AI translator turned it into em {duração} for Brazilian Portuguese.

The Portuguese word was correct. The result was still broken. {duration} is a variable, and the app passes a value called duration, not duração. The string stopped working.

The model did not fail at random. Something in the prompt pushed it there. To see what, we need to look at what Tolgee puts into the prompt, and at the bugs that changed it over the last two years.

Context is what makes the translation good

A classic machine translation tool sees one string and nothing else. It does not know if "Save" is a button, a menu item or the name of a pricing plan. A human translator would ask. A model cannot ask, so Tolgee answers the questions before the model needs to. We wrote about this problem in Context: Gamechanger in the Software Localization. Missing context was also the main reason why machine translation of apps used to be so unreliable.

For every key, Tolgee collects context from the project and builds it into the prompt. Each row below is a real part of the default prompt.

Context

Where Tolgee gets it

What the prompt tells the model

Key name

The key itself, for example checkout.button.pay

"You are working with translation key …"

Key description

Written by a developer or translator (how to write good ones)

"User provided additional description of the key"

Related keys

Keys shown near this key on the same page. The SDK reports them when someone edits a key in in-context mode (how it works).

"Here is list of translations used in the same context"

Screenshot

Uploaded screenshots, with the key highlighted

The image itself

Translation memory

Similar strings already translated in the project

"you may reuse them…"

Glossary

The organization's terms, with flags such as forbidden or non-translatable

The term list, plus what each field means

Project description

Project settings (AI customization)

"Here is user defined description for the project"

Language note

Settings of each language

"Here is user defined note"

Plural rules

The plural forms of the target language, with example numbers

"Please include exactly these forms…"

Character limit

A setting on the key

"The translation MUST NOT exceed N characters"

Writing rules

Detected from the language, for example Chinese, Japanese and Korean

"Add space between … characters and latin words"

So the model does not translate "Save". It translates "Save" on a checkout page, next to "Cancel" and "Total", in a product described as a banking app, with "account" fixed by the glossary, in at most 12 characters.

This is why Tolgee's AI translation is good. It is also why the prompt is hard to get right. Every piece of context is something the model can misread. The bugs below show exactly that.

Bug 1: "strictly" was too strong

The report. A user added the term "Test" to the glossary but did not translate it yet. Then they machine-translated "This is a test." The result kept the English word "Test", with a capital letter (#3288).

Why it happened. Tolgee sent every glossary term found in the source, even terms with no translation. The prompt said:

The model got a term with no target word and an order to use it strictly. The only form it had was the English one, so it used that.

The fix (#3705) had two parts. First, the data: Tolgee now skips glossary terms that give the model nothing to work with. A term is sent only if it has at least one of these: a translation into the target language, a description, or a flag that marks it as forbidden or as "do not translate". Second, the words:




The lesson. Context without a rule for using it can do harm. The model needs to know what each field means and what to do when a field is empty.

Bug 2: "inspiration" with no warning

The report. This is the {duration} bug from the start (#3752). The model translated a variable name inside curly braces.

Why it happened. The prompt already had a rule to keep placeholders. But the translation memory part said only this:

Translation memory gives similar strings, not the same ones. Their placeholders can have other names. In some of them, the placeholder even appears translated. The model saw those examples as good work to follow, and they beat the general rule.

The fix (#3755) warns the model right where the risk is:




The lesson. Put the rule next to the context it is about. A general rule at the other end of the prompt is weaker than a warning right beside the examples. The pull request was also honest: a prompt rule makes failures rarer, but it does not make them impossible. A check in code is the only hard guarantee.

Bug 3: one vague line about placeholders

Bug 2 is surprising for one reason: by then, the general placeholder rule was already strong. Until April 2026, it was only one line:

This line leaves a lot open. May the model move a placeholder? Drop one? Translate the words inside a plural? Add a full stop? Models found their own answers, and not always good ones.

This change did not come from one report. We saw placeholder problems many times, so we rewrote the rule (#3597). The new rule has exact instructions, one for each way it went wrong:




The lesson. "Don't modify" is not a full rule. Say what to keep, what may change, and what to do in the special cases. The exception for plural branches matters: without it, a strict "never translate inside {}" would stop the model from translating the plural words.

Bug 4: the example taught the wrong name

The report. A user's plural key used count as its variable name. After machine translation, the other languages used value instead (#3772).

Why it happened. Plurals are hard, because each language has its own set of forms. English has two, Czech has four, Arabic has six. So the prompt shows the model the exact shape of the answer it expects. For Czech, it looked like this:

The word count was fixed in the code. It was the same for every key, whatever the real variable was called. Strong models usually ignored it and kept the name from the source. Weaker models did not. In a test with a small local model, the source {itemCount, plural, …} came back as {count, …}, copied from the example. Sometimes it even came back as {Betrag, …}: the model translated the name into German. On top of that, Tolgee stored whatever name the model returned.

The fix (#3786) had two parts. The example now uses the key's real variable name. And after the model answers, Tolgee puts the right name back into the result in code. The model no longer decides it.

The lesson. An example in a prompt is a very strong signal, often stronger than the rules around it. Every detail of an example must be true for the case at hand. And if something can be fixed in code after the model answers, fix it there.

Bug 5: the model copied what it saw

The report. A user uploaded this plural string:

The AI translation came back with the two characters \n stuck inside it, as plain text:

Why it happened. Tolgee stores plurals on several lines, so they are easy to read in the editor. For ICU, those line breaks mean nothing. But on the way into the prompt, the text was escaped for JSON, and each real line break became the two characters \ and n. That text landed in a normal sentence: Translate "…" from English to Czech. The model saw \n in the source and kept it in the answer. From its point of view, that was correct: keep the source as it is.

The fix (#3830) turns a plural into one line before it goes into the prompt. The model now sees the same string the user wrote.

The lesson. The model cannot tell your formatting from the user's content. Look at the final prompt text, character by character, not at the template. Whatever is there, the model may treat as part of the string.

Make it yours: prompt variables

The default prompt is a good start, but some teams know their content better than any default can. So in the AI Playground, you can write your own prompt. All the context from the table above is available as variables:

Variable

What it holds

{{source.translation}}, {{target.languageName}}

The text to translate and the target language

{{key.name}}, {{key.description}}, {{key.maxCharLimit}}

Facts about the key

{{project.description}}, {{target.languageNote}}

Your project and language notes

{{glossary.json}}, {{translationMemory.json}}, {{relatedKeys.json}}

Glossary terms, memory matches and nearby keys

{{screenshots.first}}, {{screenshots.all}}

Screenshots of the key

{{other.de.translation}}

The key's existing translation in another language, here German

{{fragment.glossary}}, {{fragment.icuInfo}}, …

Parts of the default prompt, so you can keep what works and change the rest

The fragments matter. All the fixes in this article live in the fragments or in the variables themselves. A custom prompt that uses {{fragment.icuInfo}} gets the five placeholder rules, and it gets the next fix too, with no edit.

Users also found uses we did not plan for. One team pointed their custom prompt at their own AI engine wrapper: a proxy service in front of OpenAI. The proxy read Tolgee's context as JSON, changed the prompt, and only then sent it to the model (#3683):




It worked until a key description had a quote or a line break in it. Then the JSON broke. Now there is an escapeJson helper for this: {{escapeJson key.description}}. You can use it on any variable, and it never escapes a value twice (#3799).

This case shows the main idea from a different side. The context Tolgee collects is useful by itself, even when our prompt is not used at all.

What we learned

Context is what makes AI translation good. But each new piece of context is also a new way for the model to go wrong. Five bugs taught us five rules:

  1. Give every piece of context a rule for using it. A glossary term with no translation and the word "strictly" leaked English into the output.

  2. Put the warning next to the risk. A general placeholder rule lost to translation memory examples until the memory section got its own warning.

  3. Spell out the rule completely. "Don't modify" became five exact rules, with an exception for plurals.

  4. Examples are the strongest part of a prompt. One fixed word in an example, count, beat every instruction around it.

  5. Read the final prompt, not the template. An escaped line break became part of the user's string.

One more rule sits under all five: a prompt makes a mistake rarer, but only code makes it impossible. Where Tolgee can check or fix the result after the model answers, as with plural names, it does.

Most of these bugs came from users who took the time to report a strange translation. Each report made the prompt better for everyone. If you see one, please open an issue.

Translate your app without losing your mind!

Translate your app without losing your mind!

Code once. Ship globally.

Code once. Ship globally.

Translate your app without losing your mind!