Accessibility

Spell check and language tags in a rich text editor

A French phrase flagged as an English misspelling, a code block full of squiggles, and a screen reader pronouncing Spanish with an English voice. One attribute fixes most of it.

6 min read

Spell check is one of the few editor features that works without any code from the editor: the browser turns it on for any editable element. The results can still be wrong. A French phrase inside an English document gets underlined in red. A code sample is full of squiggles. A screen reader reads a Spanish quotation with English pronunciation. The fix for all three is a language tag, and the tag also decides which dictionary and hyphenation rules apply.

Declare the language of the page and of each part

The lang attribute names the language of an element and everything inside it. Set it on the htmlelement for the document’s main language, then override it on the spans or blocks that differ:

Code example
<html lang="en-GB">
  ...
  <p>
    The committee met on Tuesday. Its report, <span lang="fr-FR">Rapport d’étape</span>,
    was published in French.
  </p>
  <blockquote lang="de-DE">Dies ist ein Zitat auf Deutsch.</blockquote>
</html>

The browser uses that tag to pick a spell-check dictionary, and screen readers use it to choose a voice and pronunciation. WCAG success criterion 3.1.2 (Language of Parts) asks for exactly this: a part of the page in a different language should say so in the markup. CSS can use the same information. hyphens: auto needs a language to know where a word may be split, and it does nothing without one.

Keep lang inside the editor

The language of the content is set by the writer, not by the page, so the editor must store it. Two places lose it most often. The first is a sanitiser that allows lang on some elements and not on spans. The second is paste, because Word and Google Docs put their own language codes into the markup, often in a form that is not valid:

Code example
// Word writes codes such as "EN-US" or "x-none". Accept only valid BCP 47 tags,
// and write them back in the canonical form, e.g. "en-US".
function cleanLang(value: string | null): string | null {
  if (!value) return null;
  try {
    return Intl.getCanonicalLocales(value)[0] ?? null;
  } catch {
    return null;   // not a valid language tag: drop it rather than store it
  }
}

Intl.getCanonicalLocales throws for malformed tags, so the try handles the bad input cleanly. A tag such as x-noneis valid syntax but tells the browser nothing, so decide on a policy for it: drop it, or map it to the document’s main language.

Switch spell check off where it does not belong

Spell check is useful for prose and noise for code, identifiers, and fields where a word is a name or a command. The attribute spellcheck controls it per element. Turn it off for code blocks and for any field that holds data rather than writing:

Code example
<pre spellcheck="false">const total = itemCount * unitPrice;</pre>
<input type="text" spellcheck="false" autocomplete="off" />

Set the attribute on the element itself, not only on a parent, and check it after the editor re-renders. Some editors rebuild their code blocks on each change, and the attribute is lost in the rebuild. A quick test is to type a deliberate misspelling inside a code block and confirm nothing underlines.

What you cannot control

The editor does not ship a spell-check dictionary, and it cannot make a user’s custom words appear in another browser. The dictionary comes from the browser or the operating system, and users add words to it in their own settings. Do not build a second spell checker into the page unless you have a clear need: the browser’s version already matches what users see in their other applications, and a custom one will disagree with it.

What the editor can do is avoid making the result worse. Keep the language tags accurate, turn off checking where it does not help, and do not strip the attributes that the browser uses to decide.

A short check before release


RichTextEditor is a perpetual-licence JavaScript editor — one purchase, self-hosted, no metered editor loads. Download the evaluation or see how it compares.