Skip to content

v0.1.1 — Fix catalogue corruption from msgid-only entry keying

Latest

Choose a tag to compare

@xNilsson xNilsson released this 27 Jul 18:58
c6fbbca

Patch release fixing data loss in mix gettext_ops.translate. If you are on
v0.1.0 and have ever run translate, upgrade and check your catalogues.

Fixed

  • Catalogue corruption in translate (data loss). Entries were keyed by
    msgid alone, but gettext identifies an entry by the pair {msgctxt, msgid}.
    Any two entries sharing a msgid collapsed onto whichever one won, so a
    contextless msgid "Active" was rewritten to carry msgctxt "token status".
    The resulting file has two identical entries and Expo.PO.parse_file!/1
    refuses to read it, breaking compilation of the whole application.

    This fired on every write, including a single-string call, and on msgids
    that were never in the translation input — translating one unrelated string
    silently collapsed every duplicate-msgid pair in the catalogue.

  • Silent corruption in change_msgid. Renaming a msgid onto one the file
    already used produced an unreadable catalogue and still reported {:ok, ...}.
    The rename is now refused and the file left untouched.

  • Writer.update_translations/2 overwriting the wrong entries. A bare msgid
    overwrote every entry sharing it, including context-carrying siblings that are
    distinct translations.

Added

  • GettextOps.Entry.get_msgctxt/1 and GettextOps.Entry.key/1, exposing the
    {msgctxt, msgid} identity of an entry. nil, [] and "" all normalise to
    "no context", matching Expo.
  • GettextOps.Writer.check_duplicates/1, run before every write so a would-be
    corruption surfaces as a loud error instead of an unreadable .po file.
  • translate reports an :ambiguous list for msgids that exist only under two
    or more different contexts, rather than silently picking one. Without
    --force this is an error; with --force they are skipped and listed.
  • msgctxt is included in text and JSON output, so search and
    list_untranslated results distinguish entries that share a msgid.

Changed

  • translate resolves a bare msgid to the contextless entry when several
    entries share that msgid. Previously the choice was arbitrary.
  • Writer.update_translations/2 accepts {msgctxt, msgid} tuple keys to target
    a context-carrying entry. A bare msgid string now matches only the contextless
    entry — it no longer overwrites context-carrying siblings.

Upgrading

Additive to the result map — existing result.updated / result.not_found
access is unaffected. Two behaviour changes to be aware of:

  • translate without --force now returns {:error, ...} for a msgid that
    exists only under two or more contexts, instead of writing a corrupt file.
  • change_msgid now returns {:error, ...} where it previously returned
    {:ok, ...} and corrupted the file.

Full Changelog: v0.1.0...v0.1.1