Filter a German news site by category and you can get the wrong articles back. The Republic hit that in September 2025. Its German category Politik and its English category Politics were two rows in the database. Every row carries its own id, a number unique to that row, and the filter matched one id at a time.
A multilingual CMS is a content management system that stores a page once and shows it in more than one language. One content tree means the German page and the English page are two views of one thing.
| Measure | Figure | Period |
|---|---|---|
| Languages the content model pairs | 2, German and English | since the Apr 2025 rebuild |
| Content types paired across both | 19 | working tree, 20 Sep 2026 |
| Page sections an editor places alone | 21 | working tree, 20 Sep 2026 |
| Months with no backend work | 18, then 10 more | Oct 2023 to Jul 2026 |
The publisher had outgrown software built for a different product
The Republic is a German civic and political publisher at therepublic.de.
Autonomous Technologies is the engineering firm behind the platform. We build and run custom websites, internal tools and the joins between them. Our clients are founders and operators who lose hours to systems that do not talk to each other. Getting those hours back is the job. The Republic has been our client since November 2021, and its code has always lived in its own account.
By early 2025 the site had run three years on software built for something else. That system was a reader community: sign-ups, comments and voting. The publisher had moved on. The software had not.
German was the weakest part. A locale is the language and region a piece of content belongs to. The old system had none. German was a list of swapped words in the public site. The database never knew that a German article and an English one were the same story.
A site can read as fully translated and still be monolingual underneath. The test is not whether the words change. It is whether a filter or a search can treat both versions as one thing.
We described how the publication works before writing any code
The rebuild landed as one saved change in April 2025. It touched 405 files, added 1,047 lines and deleted 18,607. We removed the old blog, post and user code rather than carry it forward. The blog part alone held 88 migrations, a record of every table change.
Wagtail and Django replaced it as the tools editors log into. A separate Next.js site serves the pages readers see. Between them sits an API, the fixed set of addresses the public site reads content from.
Nineteen content types are now paired across the two languages. A content type is a kind of thing the system knows about, such as an article or an event. A translation key is one shared id that both language versions carry, so Politik and Politics are one category seen twice.
Each phase had one owner. Kevin, one of two engineers on the 2021 build, is on the ground for us in Germany today. Jawad built the 2025 rebuild and the 2026 clean-up, and is who the client got back in July 2026.
The lookups that cross a language boundary are where bilingual sites break
A lookup is the question a site asks its database, such as give me every article in this category. Once the old German articles were imported, the category lookup returned the wrong set. The fix finds the shared key for each category asked for, then matches articles on that key instead of one row’s id.
Search needed the same treatment. It reads the language the reader is in, and returns nothing for a language code it does not know. Showing German readers English results quietly is worse than showing nothing.
Write every cross-language lookup against the shared key, never the row id. That rule separates a site with German content from a site that understands German.
What changed, what stayed the same, and what nobody measured
| Measure | Before the 2025 rebuild | After | Window |
|---|---|---|---|
| Language versions linked in the database | none, a word list | 19 types paired | Oct 2023, then Sep 2026 |
| How readers get content | hand-written lookup code | 8 addresses, one API | one change, 6 Apr 2025 |
| A new page needs a developer | yes, a template each | no, 21 sections | same change onward |
| Months with no backend work | not applicable | 18, then 10 more | Oct 2023 to Jul 2026 |
| Editor time per bilingual article | not measured | not measured | never timed |
| Readers, sessions or article views | not measured | not measured | no analytics here |
| Legacy German articles migrated | not measured | not measured | import job deleted |
The bottom three rows are the honest ones. No analytics runs here, so we cannot say whether readers went up. The import job was deleted after one run, taking the count with it.
A platform that stops needing changes is not a lapsed relationship. Twenty-eight of thirty-three months passed with no work on the parts readers never see, because nothing needed changing. The client called the same firm both times.
Fifty-four minutes on a live domain is the part worth judging
On 5 August 2026 the client asked for a redirect, a rule that sends visitors from one web address to another. They wanted their domain pointed at an event site. The first attempt went out at 17:47 Pakistan time and was wrong twice over. It caught the home page only. It was not limited to the live address either, so the team’s own test sites bounced too.
The engineer undid his own change 16 minutes later. A corrected version went into the live code 54 minutes after the bad one landed, at 18:41. That is 15:41 in Berlin, inside the client’s working afternoon.
The written record explains why a temporary redirect beat a permanent one. It lists six web addresses tested, including a decoy.

What we would do differently, and who this is not for
Three things. We would write tests for both language bugs, because neither is covered. We would agree the editor timing measurement before the rebuild, not after. And we would finish the German admin wording: 217 phrases are ready, none translated.
This is for you if you run a publication or membership site in two languages and cannot say for certain that search and filters return the right rows in the second one.
It is not for you if you want the German copy written; we did not write it. Nor if you need right-to-left languages: Arabic mirrors layouts, numerals and dates, and none of that happened here.
The same pattern elsewhere: a live league platform and an embedded product team, both from custom engineering and integrations.
Questions and answers
What is a multilingual CMS, and when is one structural?
A multilingual CMS stores a page once and shows it in more than one language. It is structural when both versions share one id in the database, so filters, menus and search treat them as one thing. It is cosmetic when only the public site swaps words.
Does publishing in German automatically produce the English copy?
No. Nothing here translates anything. The platform pairs the two versions so editors maintain both in one place.
Why do category filters break on a bilingual site?
Because the same category exists as two rows, one per language, each with its own id. Matching on an id returns only that language’s articles. Matching on the shared key returns the right ones either way.
Can editors build pages without a developer?
They compose a page from 21 ready-made sections, which covers most one-off pages. A new kind of content needs design work.
What should a bilingual replatform settle first?
Three things, before any code: the content types, how the two language versions relate, and what search and filters must return in each.
