Skip to content

content_taxonomies links content and terms with inconsistent locale semantics (term side locale-agnostic, entry side per-locale → tag drift across translations) #1348

Description

@MA2153

Description

content_taxonomies links the two ends of a content↔term assignment with inconsistent locale semantics, and the asymmetry causes per-locale tag drift.

The junction row is (collection, entry_id, taxonomy_id):

  • taxonomy_id is locale-agnostic. Since migration 036_i18n_menus_and_taxonomies, it stores the term's translation_group (not a taxonomies row id). The old FK was deliberately dropped so one junction row covers every locale translation of the term. Reads confirm it — TaxonomyRepository joins taxonomies.translation_group = content_taxonomies.taxonomy_id.
  • entry_id is per-locale. It stores a single content row's id. Because content tables are row-per-locale (migration 019_i18n), each translation of a post is a distinct row with its own id, so a junction row attaches a term to one locale's entry only, not to the content translation_group.

So a content_taxonomies row links one locale's entry to a locale-collapsed term concept. The two ends disagree on what "the same thing across locales" means.

Why this is a bug (observable consequence): term assignments are only synced across content translations once, at creation time, via TaxonomyRepository.copyEntryTerms ("Used when creating a translation of a post so the new translation inherits the source's term assignments"). It's a one-time snapshot. After that the locale entries drift:

  1. Create an English post, tag it News. Create its Arabic translation → it inherits News (copy at creation). Good so far.
  2. Later, edit the English post and add Sports. The Arabic translation does not receive Sports — its entry_id differs and nothing re-syncs.
  3. Symmetrically, removing a tag from one locale leaves it on the sibling locale.

The result is that "the same content in another language" can carry a divergent set of terms with no UI/runtime affordance reconciling them, even though the term side was specifically designed to be locale-agnostic. Either both ends should be group-keyed (assignments shared across the content translation_group), or the divergence should be an explicit, surfaced feature rather than silent drift from a creation-time copy.

This is a design/consistency bug rather than data loss — flagging for a decision on intended semantics.

Steps to reproduce

  1. Enable a second locale (e.g. ar) and create a posts collection with a taxonomy (e.g. Category).
  2. Create an English post, assign term News.
  3. Create an Arabic translation of that post (inherits News via copyEntryTerms).
  4. Edit the English post; add term Sports and save.
  5. Fetch terms for the Arabic translation (getTermsForEntry(collection, arEntryId)).
  6. Observed: Arabic translation still has only News; Sports never propagated.
    Expected (or to be decided): assignments stay consistent across translations of the same content, mirroring the locale-agnostic term side — or per-locale divergence is an explicit, documented capability.

Environment

  • emdash version: main (current packages/core)
  • Runtime: Node + SQLite and Cloudflare D1 (schema-level, dialect-independent)
  • Relevant code:
    • packages/core/src/database/migrations/036_i18n_menus_and_taxonomies.ts (taxonomy_idtranslation_group)
    • packages/core/src/database/repositories/taxonomy.ts (getTermsForEntry, setTermsForEntry, copyEntryTerms)
    • packages/core/src/database/migrations/019_i18n.ts (row-per-locale content)

Logs / error output

N/A — behavioral/design inconsistency, no error thrown.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions