Field notes

July 22, 2026

How to build a Simple English version of your website with LLMs and word-frequency data

If your site has a global audience, some of your readers are not fluent in the language you wrote it in. A proper localized site is the best answer, but there is always a long tail of languages you can't translate and maintain. Here is how we build a plain, Simple English version instead: quickly, with reasoning LLMs, and grounded in real word-frequency data so it's actually readable.

Transcreating into Plain English, Simple English or Easy English

Almost every website makes one quiet assumption: that the person reading it is fluent in the language it is written in. For an English site, that means fluent in English. It's a fair assumption for a lot of your visitors and wrong for a lot of others, and you usually never hear from the second group, because they bounce.

We build Reelang, a language-learning app, so that second group isn't an edge case for us. A big part of our audience lands on our English pages while still learning English: a couple of years of Duolingo behind them, not much real practice. They know good and fast and watch. They stumble on awesome, browse, curated, seamless.

Our preferred answer to this is a proper localized site, and we ship those: a growing list of full languages, each with its own domain, that we grow over time based on demand. But translating and then maintaining a language forever is real, ongoing work, so there's always a long tail we won't get to, plus visitors whose language we don't serve at all. The default we were handing all of them was the same: full, confident, idiomatic English. The hardest version of the site, given to the readers least equipped for it.

So we built another version: Simple English (some teams call this "plain English"). It's the same product, rewritten so a real beginner can follow it. It lives at simple-english.reelang.com, shows up in our language picker as "Simple English," and is wired into our internationalization system as a first-class locale.

What Simple English does

hover a word

Listening practice with real native1859 speakers2007.

transcreate

Listen to real people talk.

CORE · rank ≤ 1000
BORDERLINE · 1001 to 2000
AVOID · rarer than 2000

This is a guide for anyone thinking about doing the same, whether you build a language app or any site with a global audience. It's in two halves: how to get it done quickly, and how to ground it in real data so it's actually understandable. Here are the things you need to consider.

Simplification is translation

The decision that makes everything else fall into place: don't build a "plain language mode," a toggle, or a post-processor that dumbs down your English at runtime. Treat Simple English as a locale: to your code, indistinguishable from French or Indonesian.

That matters because a mature site already has a lot of machinery for locales, and you want to reuse all of it. Every user-facing string in Reelang lives in a plain-JSON catalog with English as the source. Adding Simple English just means adding another target catalog and filling it in. The instant we did that, it inherited, for free:

  • Routing and serving. It gets a host (simple-english.reelang.com) through the same registry that gives German reelang.de. No new plumbing.
  • The language picker and switcher. It appears in the footer picker and the in-app switcher automatically, because those render from the locale registry.
  • The "you might want this" suggestion banner. Same component, one new branch (more on that at the end).
  • The fallback contract. Any key you haven't written yet transparently serves the English source. A half-built locale isn't a broken locale. It just reads a little less simple where the work isn't done.

The framing that unlocks all of this: simplifying English is a kind of transcreation, rewriting an idea for a new audience rather than translating it word for word. That's an established localization term, not something we coined; it just happens to describe this exactly. Same shape as translation: a source string in, a target string out, in a different variety of language with its own rules. Once you see it that way, you stop reaching for a special mechanism and reuse the one you already trust.

There's one honest complication with treating "Simple English" as a locale, and it's a good example of the kind of thing that bites you.

The invalid-language-tag gotcha

simple-english is not a valid BCP-47 language tag. Real tags look like en, de-DE, or es-419: a two or three letter primary subtag, optionally region-qualified. Your code is happy to treat simple-english as an opaque locale id for routing and catalog lookup. But the moment you hand that string to the platform's internationalization primitives (Intl.PluralRules, Intl.NumberFormat, Intl.DateTimeFormat), they throw a RangeError, because simple is not a valid primary subtag. Our compiled message functions call Intl.PluralRules for any string with a plural in it, and the language picker renders on every page. So the first build 500'd every page under the new locale.

The fix is small once you understand it: coerce the locale to something Intl can parse at the single boundary where those calls happen, falling back to en for anything ICU rejects, so a plural or a formatted number degrades to English formatting instead of crashing. You also hardcode the human label "Simple English" (it's Wikipedia's term for exactly this register), because Intl.DisplayNames can't derive a name from a tag it considers invalid.

Simple English, Plain English, or Easy English? Know who you're for

These words get thrown around loosely, but they point at slightly different audiences, and the audience decides your target. Worth being clear which one you're building for before you write a line.

  • Plain English traditionally means clear writing for a general, mostly-fluent audience: the style governments and banks are pushed toward. Cut jargon, shorten sentences, but it's still written for fluent adults.
  • Easy English / Easy Read (like German Leichte Sprache) is an accessibility standard aimed at readers with cognitive or learning disabilities and low literacy. It's stricter, often paired with pictures, and follows formal rulebooks.
  • Our case is a third thing: adult second-language speakers, people learning English. Not primarily readers with disabilities, and not children.

That last point sets the tone, and it's the part we'd put in any guideline. We cut cleverness and rare words hard, but we do not talk down. Our reader is an adult reading in a second language, so the register stays adult: simple words, full respect, never a worksheet. It's also why we lean on international words, which is a language-learner move rather than an accessibility one. The overlap with accessibility is real and welcome. Simpler copy also helps readers with dyslexia, low literacy, high cognitive load, or just a small screen in bright sun. But we design for the adult second-language reader, and that's who every rule below is pointed at.

Write for one real person

A locale needs a standard, and "simpler" isn't one. Ours is a real person: my mum. German speaker, a couple of years of Duolingo, very little real practice. She knows simple words. She stumbles on awesome and wanna. The entire test for a Simple English string is: would she get it on the first read? If a sentence makes her stop and work it out, it failed.

Concretely, that turns into a few rules:

  1. Target the ~500 most common English words, and at most ~1000. That's the one number to hold onto.
  2. Always pick the simplest grammar and tense. Present simple. Active voice, never passive. One idea per sentence, five to ten words. No clause stacked inside a clause.
  3. Between two words that both work, the commoner one always wins. Great beats awesome. Use beats utilize. Find beats discover.
  4. When a part of a sentence is hard to say simply, cut it. A shorter sentence that lands 80% of the meaning beats a complete one the reader can't finish.

That last rule is the important one, and it's why this is transcreation, not translation: you're not preserving the English sentence, you're re-writing the idea from scratch in common words, with full permission to drop the clever aside, the precise caveat, the sharper joke. Losing the flavor is fine. Losing the reader is not.

A frame that helped: explain it like you would to a child, but never talk down to the reader. "Explain like I'm 8" gets the simplicity right: short sentences, plainest words, describe the thing instead of naming it. But the reader is a grown adult reading in a second language, which is a completely different thing from being a child. Simple words, adult tone.

Here's what that looks like on real strings from our homepage. Left is what we say to a fluent reader; right is the Simple English rewrite.

Fluent EnglishSimple English
Listening practice with real native speakersListen to real people talk.
Learn {language} from videos you can't stop watchingLearn {language} with videos you love.
Browse videosSee all videos.
Built on comprehensible inputVideos you can understand.
Human speech. Not AI slop.Real people talking. Not bad AI videos.

Every swap has a reason. Native (frequency rank 1859) and speaker (2007) are both too rare, and a beginner has no word for the concept, so "real people." Browse is rank 6626, far too rare for a button, so "see all," two of the commonest words there are. "Comprehensible input" is our actual methodology and pure jargon, so we always unpack it. Slop is a special case: "AI slop" is a phrase the tech world coined in just the last couple of years, so a non-native reader outside that scene has almost certainly never met it. Which raises the question: where do those rank numbers come from, and how do you keep this from being one person's taste?

Make "is this word easy enough?" a number

The risk with a project like this is that "simple" becomes a vibe, and quality drifts string to string. The fix is to ground it in a word-frequency list: for every English word, roughly how common is it? Turn "is this word easy enough?" into a lookup instead of an argument.

Here we had a head start, and it's worth calling out because it shaped the approach. Reelang's core product already runs on word-frequency data: features like our Smart Subtitles highlight the commonest words in a clip so a learner knows which to focus on first. So we already maintain well-kept frequency lists for many languages, and for Simple English we just pointed the tooling at the exact same data the product uses. You don't need that to start: public frequency lists (wordfreq, OpenSubtitles, SUBTLEX) are easy to find and good enough. And if you'd rather build on the lists we maintain, get in touch, we're glad to share.

The lookup is a small command-line tool. Crucially it's mechanized: a human can run it, but so can an agent, and that's the point. Nobody is eyeballing a frequency list by hand.

# rank some candidates, take the commonest one that still works
node freq-check.mjs awesome great good nice
  awesome   great(114)   good(41)   nice   -> use "great"

# scan a finished draft, flag every word that is too rare
node freq-check.mjs --text "Browse the catalog and discover creators"
  browse   catalog   discover   creators   -> rewrite it

The buckets are blunt on purpose: CORE (rank ≤ 1000, safe), BORDERLINE (1001 to 2000, prefer a simpler word), AVOID (rarer than 2000, or not in the list, swap it). It judges each word on the better of its written and spoken frequency, because a word can be rare in print and common in speech.

A second tool scans a whole batch of strings and reports each one's median word rank, plus the specific rare words still pulling it up. Aim for a median at or below 70 per string. Higher is a prompt, not a failure. It also separates fixable rare words (a common idea wearing a rare word: founder becomes "person who started the company"; homework becomes "work to do at home") from unavoidable keeps (names and brands like ChatGPT or Buenos Aires, which it detects and sets aside so nobody wastes time "simplifying" a proper noun).

Two things worth stealing for any readability tooling:

  • It's a guide, not a gate. Frequency lists rank abstract function words above concrete beginner words, so a few genuinely-basic words score badly (slow comes out at rank 1204). If a word is obviously basic and the corpus under-ranks it, keep it. The rule the tool enforces well is the comparative one: if a word is rare and you have a common alternative, there's no argument: swap it.
  • Prefer international words over "plain" ones. Your readers are second-language learners, so a Latin- or Greek-rooted word that shows up across many languages (video, music, photo, minute, problem, hotel, taxi, internet) is often easier for them than a plainer but distinctly-English one, because they already know it from their own language. Raw frequency doesn't capture that; judge it together with the rank.

The guide, in short

Here's the whole authoring standard, condensed, small enough to paste into a system prompt or hand to a new writer. This is a stripped-down version of the voice guide we actually use:

Fill the rest with LLMs, not by hand

The homepage and core chrome are worth hand-authoring carefully. But a real site has thousands of strings, and hand-writing every one against the guide isn't a good use of anyone's week. So we filled the rest with a reasoning LLM.

This is the part to get right, because it's where "quickly" and "actually readable" have to meet. A reasoning model does the rewriting, one small batch of keys at a time, working against the guide above. But a model rewriting from taste alone drifts, so we hand it the mechanized frequency tool as an actual tool it can call: it rewrites a string, runs the frequency check on its own output, and revises anything still flagged rare. The data-grounded step isn't a human with the tool open. It's the model calling a script against our frequency database and reacting to the numbers.

We ran this inside a local coding agent (Kimi Code) rather than as a call to an external translation API, and the reason is exactly that tool. A translation API takes text in and gives text back; there's nowhere to bolt in a reasoning loop that shells out to your own frequency database mid-rewrite. A coding harness has that natively. To be clear about what "local" means here: the strings still go to the model provider (Kimi Code sends them to its models like any coding agent), but the frequency-lookup step, the part that would be awkward to attach to a translation API, runs in our own environment inside the harness. That's the practical difference, and it's why simplification runs on a different path from the foreign-language localization pipeline.

The one mechanism worth copying is how the job knows it's done. A lot of a site's strings are already simple: "Reelang." "See all videos." "Log in." Rewriting those is pointless, and worse, it makes the job look infinite. There's always another already-fine string to "improve." So we keep a durable, committed list of keys judged already simple, and those keys just fall back to the English source. That list is what lets the work terminate and resume: every string is either rewritten or explicitly marked "already fine, inherit English," and when the set of un-decided keys hits zero, the locale is complete.

Roughly two-thirds of the ~5,700 strings in our catalog needed a real rewrite; the rest were already simple enough to leave alone, inheriting the English source untouched. That inherit-for-free property is the quiet upside of treating an English variant as a locale: a large fraction of the site needs no work at all.

How do you avoid duplicate-content SEO problems for a plain-English version?

This is the question that will actually bite you, so here's the head-on answer. A Simple English page is, to a search engine, a near-duplicate of your English page. Search engines don't reward near-duplicate content, they cluster it: index both and you don't get two ranking pages, you get one canonical winner and one that gets quietly folded into it, plus an ongoing risk of the two competing for the same query. There's no upside and a real downside.

For us, that surfaced a distinction we'd had welded together without noticing: "do we offer this to users?" and "do we want search engines to index it?" are two different questions. Our locale registry historically had one master switch, advertised, and flipping it on lit up everything at once: the language picker, the sitemap, reciprocal hreflang tags, per-locale social images, search-engine ping-on-publish. For every real language, that bundle is right. For a near-duplicate variant, it's wrong.

So we split the switch. advertised now means only "offer this to users": show it in the picker, serve it, let people choose it. A separate flag, indexable (defaults on), controls the crawler surface. Simple English is the one locale where they differ:

{ code: 'simple-english', label: 'Simple English', bcp47: 'en-US',
  advertised: true, indexable: false }

Offered to humans, hidden from robots. Concretely, an unindexed locale is left out of hreflang, out of the sitemap, out of per-locale social images and the search-engine ping, and its host serves robots: Disallow: / plus a noindex meta tag and an <html lang="en">. All of that derives from a single set (INDEXED = advertised ∩ indexable) rather than from advertised directly, so there's one place to reason about it.

How do you point non-native readers to the simple version?

Two questions people actually ask here: how should I suggest a different language version? and how should I guide someone to the plain, simple version without being pushy? Here's what we do.

First, the mechanism: a small, dismissible banner ("this is also available in your language"), and importantly, we suggest, we never auto-redirect. Silently teleporting someone to a different URL based on their browser is a dark pattern, and search engines dislike it too. We surface the option and let the reader decide.

Then, how we decide someone might want the simple version, i.e. how we guess you might not be a confident English reader. The signal is the language your device already reports:

  • Logged-in visitors: your saved interface language wins, since it follows you across devices.
  • Anonymous visitors on the web: we read the ordered language preferences your browser sends (navigator.languages / the Accept-Language your user-agent advertises) and offer the first one we actually serve.
  • In the native app: we read the phone's system language.

The Simple English nudge is a specific branch on top of that: if you're an anonymous visitor, we serve none of your languages, and your primary language isn't English, we offer you Simple English instead of leaving you on full English. (English on its own doesn't count as a match. The whole point is that you're not a confident English reader.) In the app, a phone set to a language we don't serve starts on the Simple English interface; an English-set phone gets normal English.

It's a softer nudge than a real language match, so it nags less: dismissing it suppresses it for four weeks instead of the usual one. None of it is forced. It's offered. That distinction is the whole ethic of the feature: make the easy version reachable, and let the reader choose it.

Losing the flavor is fine. Losing the reader is not.