Skip to content
CORDYTECH Engineered Signal Get in touch
product-engineering

How to build a bilingual marketing site the client can actually run

Interface strings and client content are two different problems, and conflating them is why the French half of a site ends up two services behind the English. Here is the split we build to, and why the front end is server-rendered.

Short answer

A bilingual marketing site is two problems, not one: the interface text your components render, and the content your client writes. Keep them apart. Ship interface text as translation records loaded into every page render, make language a property of each content record so a service can exist in French before its English version is ready, and let the client edit both from one back office. Then server-render the result, so each language arrives as HTML instead of as a promise a crawler has to run JavaScript to collect.

Bilingual sites fail in a quiet, specific way. Launch day is fine — somebody translated every page once, both versions look right, everyone signs off. Six months later the English site lists four services and the French one lists three, because adding the French copy meant editing a file in a repository nobody on the client's side can reach. Nothing is broken. The site is just half true, in the language half the buyers read.

This is how we build them so that does not happen: what belongs in files, what belongs in the database, why the front end is server-rendered even though it is React, and what has to be editable before we consider the thing delivered. The current reference build is CM Metrology, an industrial metrology and calibration company in Tangier whose site and CMS we designed, built and have hosted since November 2025.

01What does "bilingual" actually mean in a marketing site?

It means two categories of text that share nothing except being words on a page.

The first is interface text: "Learn more", "Send message", "All services", the label above a form field, the error underneath it. There are a few hundred of them. They change rarely, they are written by whoever builds the site, and every one of them exists in both languages from the first commit or the build is wrong.

The second is content: service descriptions, product entries, the about page, the testimonial someone added last week. There are as many as the business needs. They change constantly, they are written by people who have never opened a code editor, and a new one is routinely created in one language days or weeks before the other version arrives.

Every bilingual site that decays does so because someone treated those as one problem. Push content into translation files and the client cannot write. Push interface text into the database and you have built a translation admin nobody asked for, with a few hundred rows that will never change again.

02Should the second language live in files or in the database?

Both, and the split is not a compromise — the two kinds of text have opposite properties in every column that matters.

Interface textContent
Who writes itThe people building the siteThe client
How often it changesAlmost never after launchWeekly, unpredictably
How many of themA fixed few hundredHowever many the business has
Where it belongsTranslation records, loaded per requestThe content record itself
What "missing" meansA bug — the raw key renders on the pageAn editorial state — that version is not ready
Who finds the gap firstYour tests, if you wrote themA visitor, in the language you neglected

Read the last two rows together, because they are the design. A missing interface string is a defect and should be impossible to ship: keys are enumerable, so a test can walk every one in every locale and fail the build on a gap. A missing translation of a service is not a defect at all — it is Tuesday. The client wrote the French first because the customer who asked was French. The system's job is to render what exists and stay quiet about what does not, rather than to display an English page with a French heading bolted on top.

That is why language belongs to the record. Not a second table shadowing the first, not a JSON blob that only the developer knows the shape of — a field on the thing itself, editable in the same form, so the person writing the French is looking at the English while they do it.

03Why server-render the front end at all?

Because everything that reads a site on a buyer's behalf reads HTML, and most of it does not run your JavaScript.

Search crawlers are the obvious case, and Google's renderer is the most capable of them — which is exactly why people over-generalise from it. The AI answer engines that increasingly stand between a buyer and a supplier are far less patient: they fetch, they parse, and what is not in the markup is not in the answer. Neither is the preview card that gets generated when somebody pastes the link into LinkedIn or WhatsApp, which for a B2B industrial supplier is a meaningful share of how the site is first seen.

So the stack is React on the front, Laravel behind it, and Inertia's server-side rendering in between. The page arrives as HTML with the service copy, the product entries and the metadata already in it, in whichever language was requested, before a single script executes. The client-side application then takes over for navigation.

The second reason is the one users feel. A maintenance engineer opening your site on a phone from an industrial estate is not on your office fibre. Server-rendering means they read while the JavaScript is still arriving, rather than watching a spinner decide whether their signal is good enough.

04What belongs in the CMS, and what does not?

Our test: if a developer would be embarrassed to bill for the change, it belongs in the CMS. That single rule settles almost every argument, and it settles them in the client's favour.

What the client controlsWhy it is not code
Services — copy, image, order, published or notThe service list is the business, and the business changes
Products — description, specification, price, currency, featured flagA price change must never be a deployment
Client logos and testimonials, each with its own positionSocial proof arrives on the customer's schedule, not the roadmap's
Hero slides and their calls to actionThe campaign changes faster than the codebase should
Contact details and social links, in one placeA phone number hard-coded into a footer is a phone number that will be wrong somewhere
Per-page title and meta descriptionWhoever owns the search results owns the fields that produce them

What stays in code is everything with a failure mode: the layout, the routing, the form handling, the schema, the caching, the deploy. Clients do not want that responsibility and should not be sold it. The line is not "simple things go in the CMS" — it is "reversible things go in the CMS". A wrong price is fixed in ten seconds by the person who noticed. A wrong template is an outage.

05How do you stop a client breaking their own site?

By making the destructive moves unavailable rather than by writing a manual nobody reads. Four mechanisms do most of the work.

  • Publish state on every record. Draft and published, not a delete key. Half-written content simply is not rendered, so nobody has to hold a page hostage while they finish the French.
  • Explicit ordering. An integer the editor controls, not "most recently updated" — otherwise fixing a typo silently promotes a service to the top of the list.
  • Layouts that survive missing data. No testimonials yet means the section is absent, not a bordered box with nothing in it. Every section a client can empty has to be designed empty first.
  • Images handled on upload. Resized, converted, dimensions recorded, alt text required. Otherwise the first 8 MB photograph straight off a phone quietly undoes the performance work.

None of that is clever. All of it is the difference between a site the client uses and a site the client is afraid of — and a client who is afraid of their CMS stops updating, which is the same outcome as not having one.

06What does the contact form owe the business?

More than an email. An enquiry that exists only as a message in an inbox is an enquiry that gets lost the first time a mail relay has a bad afternoon, or the first time the person who owns that inbox is on holiday.

So the submission is stored before anything else happens, and the notification is sent afterwards. If the mail fails, the lead is still there. Beyond that: validate on the server whatever you validated in the browser, keep the honest signals a business needs — name, company, phone, subject — and resist bots without making a customer prove they are human by identifying traffic lights. For an industrial supplier, one lost enquiry can outweigh the entire cost of the site.

07How this shipped for CM Metrology

CM Metrology runs 3D measurement and inspection, 3D digitisation and reverse engineering, calibration, and the training around them, from a laboratory in Tangier serving automotive, aerospace, rail and manufacturing customers. People who work to a tolerance are exacting about software, which is the best kind of client to build for.

What they got is the architecture above: a Laravel back end, an Inertia and React front end rendered on the server, English and French throughout, and a back office where their team controls services, products, hero slides, client logos, testimonials, contact details and the interface strings themselves. We designed it, built it, and have hosted and maintained it since November 2025 — which matters, because the handover is where most agency sites die, and there is no handover when the team that wrote the code also runs the servers.

The pattern generalises past marketing sites. It is the same discipline as the custom web applications we build for other clients, and the reason Ledgerline lets an administrator reconfigure an approval chain without a release. Software you cannot change is software you have already started replacing. If your site needs a developer to change a price, or your second language is two services behind your first, tell us what you are running now and we will tell you which of it is a rebuild and which is an afternoon.

CEO & Principal Engineer

Mohamed Said Ghallam

Mohamed Said runs Cordytech from Morocco and still works in the codebase. He picks the engagements, sets the architecture behind the email-infrastructure work for EMG and Relentia, and owns technical direction across the web-development practice. PHP is the through-line — his public repositories run from a cryptocurrency market-data platform to WHMCS billing and a FreeRADIUS management UI.

Working on deliverability or a product like this?

This is the kind of problem we solve as infrastructure, on the client stacks we run. Tell us what you are building.