Skip to content

v2.7.0 — Data-safe writes & fast DB updates

Choose a tag to compare

@BrunosCode BrunosCode released this 23 Sep 13:56
· 13 commits to main since this release

Highlights

Writes no longer destroy what they don't own. JSON locale files and the CSV are shared with keys of other groups, plain sentence keys and columns of other locales. Until now every set / sync / import / export rewrote them with the filtered collection and silently deleted everything outside fileNames / locales (e.g. with --file-names=auth). Both handlers now replace only the managed entries and carry the rest over.

Single-key database writes are constant in query count. Changing one translation against 2000 keys × 2 locales:

Before After
Queries ~2000 (one UPDATE per kept key) 4
Time (test suite, SQLite) ~21 s ~0.15 s

set() scopes reads and writes to the groups and locales present in the input; the DB handler loads keys and values once per group and soft-deletes with a single UPDATE; Translation drops the per-instance Validator; collection merges are linear. A regression test guards the query budget.

Fixed

  • Invalid UTF-8 no longer wipes a JSON locale file: the writer throws before touching the file (JSON_THROW_ON_ERROR).
  • Writes outside the configured locales / file names are rejected with an explicit error (surfaced by the MCP tools) instead of being dropped silently with written: 0.
  • keyDelimiter other than . works again: group filters always used the default . separator, so get() returned nothing.
  • A key used both as a value and as a parent (auth.a + auth.a.b) is refused up front with a message naming both keys, instead of crashing the nested PHP/JSON writers or overwriting data depending on order.
  • null, int, float and bool values in PHP/JSON lang files and NULL DB values no longer throw a TypeError; the CSV reader skips locales whose column is missing.
  • csvDelimiter is actually validated against keyDelimiter (the different: rule was given a value instead of a field name).
  • Commands (sync / import / export) restore the shared service options after the run, so a narrowed --file-names / --locales no longer leaks into later calls in long-running processes (Octane, queue workers, Boost MCP server).
  • PHP 8.4 deprecation on fgetcsv / fputcsv without an explicit $escape.

Changed

  • The database handler only manages the configured locales, like the file handlers: values of other locales are never soft-deleted, and a key is only retired when no active value of another locale still depends on it. Previously a key whose only values were in an unconfigured locale was soft-deleted on every write to its group.
  • JSON files are written with JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES (è instead of è). Existing files are rewritten in the new form on their next change.
  • Translation::__construct() accepts a nullable value, matching its property and validation rule.
  • DatabaseHandler::handleUpdate() / handleSoftDelete() accept an optional pre-loaded $dbValues; new public getCurrentValues(). Existing calls keep working.

No configuration changes are required. Verified against brunoscode/twill-translation-handler v2.3.0 (tests and PHPStan green).

Full Changelog: v2.6.1...v2.7.0