Message catalog
Every translation key and its text for one language, held in one file or split across many: the whole app's wording in German.
See it
What it is
A message catalog is the collection of every translation key and its text for one locale: the app's entire vocabulary in German. It is a logical collection, not necessarily one file, and most real projects spread it across several files or bundles split by feature or route. Every ecosystem has its own container for the same idea. JSON for i18next and FormatJS, PO/MO for gettext, XLIFF for handing work to translation vendors, .strings and .stringsdict on Apple, strings.xml on Android, .properties in Java, .arb in Flutter.
How it reaches the browser matters as much as what is in it. Split catalogs by route or namespace and load only the active locale, otherwise a 30-language app ships 29 languages nobody will read. Server components and static generation can bake the right catalog in at build time; apps that update copy without a release fetch catalogs over the air instead.
Gotcha: catalogs drift. The source catalog gains keys the moment a developer ships a feature, and the translated ones lag by days or weeks, so plan for missing keys with a fallback chain rather than rendering a raw 'checkout.button.pay' to a customer. Add a CI check that reports missing and orphaned keys per locale, and never let a human hand-edit the source catalog that extraction generates.
Ask AI for it
Set up message catalogs for this app. Create one JSON catalog per locale under locales/<locale>/<namespace>.json, keyed by our dot-notation translation keys and split by feature namespace so each route loads only what it renders. Load the active locale's catalogs only, never all languages at once. Configure a fallback chain (pt-BR to pt to en) so a missing key renders the parent locale's text instead of the raw key. Add a CI script that diffs every locale against the source catalog and fails on missing keys, reporting orphaned keys as warnings.