Skip to main content

Multilingual CMS · The Republic

The Republic: a multilingual CMS where German and English are one content tree

A category filter returned the wrong articles because Politik and Politics were two database rows. Here is the content model that fixed it, and the 28 months it then ran without backend work.

How the system works: Editorial content, Paired locales, Delivery API, Reader experience
The language link lives in the content model and the lookups, not the interface labels.
Integrations & Custom Systems5 minute readBy Rizwan QaiserRead the case study

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.

Twenty-eight of the thirty-three months from October 2023 to July 2026 needed no backend work. The site stayed live and the retainer held throughout.
MeasureFigurePeriod
Languages the content model pairs2, German and Englishsince the Apr 2025 rebuild
Content types paired across both19working tree, 20 Sep 2026
Page sections an editor places alone21working tree, 20 Sep 2026
Months with no backend work18, then 10 moreOct 2023 to Jul 2026
Twenty-eight of the thirty-three months from October 2023 to July 2026 needed no backend work. The site stayed live and the retainer held throughout.

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

Three rows were never measured. Editor time is the one before-and-after number this engagement could have produced and did not.
MeasureBefore the 2025 rebuildAfterWindow
Language versions linked in the databasenone, a word list19 types pairedOct 2023, then Sep 2026
How readers get contenthand-written lookup code8 addresses, one APIone change, 6 Apr 2025
A new page needs a developeryes, a template eachno, 21 sectionssame change onward
Months with no backend worknot applicable18, then 10 moreOct 2023 to Jul 2026
Editor time per bilingual articlenot measurednot measurednever timed
Readers, sessions or article viewsnot measurednot measuredno analytics here
Legacy German articles migratednot measurednot measuredimport job deleted
Three rows were never measured. Editor time is the one before-and-after number this engagement could have produced and did not.

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.

Rizwan, founder of Autonomous Technologies and the author of this case-study collection.
Every figure here traces to a saved change, a settings file or a dated founder answer, including the rows we could not measure.

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.