Skip to content

Why we model translations as rows, not JSON

Published on May 17, 20264 min readWritten by Rani Prameswari
Database schema sketch comparing JSON blobs with translation tables

Storing translations in a JSON column looks like the pragmatic choice. One column, no joins, no migrations when a locale is added. The bill arrives later.

What breaks

Reporting on translation coverage becomes a full table scan with JSON path expressions. Uniqueness on slugs per locale cannot be enforced by the database. Partial indexes on published, localized content are unavailable. And every consumer must agree on the blob shape forever.

What we do instead

Every translatable entity has a sibling table keyed on (entity_id, locale) with a second unique constraint on (locale, slug). Adding Japanese is an insert into languages — no migration, no deploy.

Tags:PostgreSQLArchitecture
Share:

Related articles