lingo.dev@0.138.4

lingodotdev/lingo.devlingo.dev@0.138.4Jul 28, 2026by github-actions[bot]

AI Summary

Fixes JSON parsing issues for model responses where quotes break the JSON structure, particularly affecting languages with specific word order like German.

Key Highlights

  • Implements a repair pass to handle unescaped quotes inside strings and misplaced placeholders in values.
  • Improves object extraction by preferring the first brace that actually opens an object.
  • Ensures malformed responses do not cause the entire translation run to fail.

Full Release Notes

### Patch Changes

- [#2176](https://github.com/lingodotdev/lingo.dev/pull/2176) [`6b027c6`](https://github.com/lingodotdev/lingo.dev/commit/6b027c685685ec06beaab4109b8b4dae3f07651d) Thanks [@moygospadin](https://github.com/moygospadin)! - fix(cli): parse model responses that break JSON around a quoted placeholder

  A source string of `"Attach '{title}'?"` becomes `„{title}" anhängen?` in German:
  the language's word order moves the ICU placeholder to the front of the sentence
  and its typography wraps it in quotes. Models emit those quotes unescaped, so the
  JSON string closes early and the placeholder's `{` lands where a value belongs;
  some responses instead drop the value's opening quote entirely. `jsonrepair` gives
  up on both (`Unexpected character "{"` and `Colon expected at position N`) and the
  whole file fails to translate. German took the brunt of it — the same run
  translated every other locale fine, so it looked locale-specific, and it was
  deterministic across retries.

  `parseModelResponse` gains a last-resort repair pass that escapes quotes sitting
  inside a string rather than terminating it (a quote only closes the string when
  the next non-whitespace character could legally follow one), and treats a
  placeholder in a value position as the start of a string rather than an object.
  Extraction now also prefers the first brace that actually opens an object, so a
  preamble like `Hier ist die Übersetzung für {title}:` no longer becomes the start
  of the response.

  The pass runs only after `jsonrepair` itself has failed, so responses that
  already parse — or that `jsonrepair` repairs — are untouched, and a genuine
  nested object or a `"Zeit: {time}"` value is left alone. The nested
  stringified-`data` path goes through the same repair.