Suppose you have an Internet Of Things device and you need to make a professional product with it. The first interface the final user will see will be the WIFI configuration page or the companion application.

How do you serve such webpage so it's as small as possible yet professional ?

A single file webpage

Since the flash size in the microcontroller is limited and you probably want to use Over-The-Air (OTA), your assets must be very small. It's not realistic to use jQuery or any other large javascript library (which you can't serve from outside your microcontroller because it's not connected to the internet yet).

So, you're limited to HTML + CSS and probably some super minimal javascript. Typically, you might want a functional website without javascript since that might be disabled on the browser.

So next, how do you deal with multiple languages for your webpage ?

You could have as many webpages as you are supporting different languages and use the Accept-Language HTTP's header to decide which one to serve, but this means that your website's page is duplicated for all different languages. This is not acceptable in space limited device like a microcontroller.

There are Javascript based solutions, like Polyglot that requires you store a JSON with all translations and then AJAX load them and apply the changes. Theses solutions are, IMHO, ugly since you'd get a page that'll load with one language and then flash to another language. It's a bit like the FOUC we had with CSS. They also fails if the user does not allow javascript.

The technique I'm showing here is based on CSS only (less likely to be disabled).

The HTML markup

The basic idea for this technique is to store any translatable text in <var></var> elements. These elements are sparsely used on the web but they are part of HTML specification since version 4.

They are unlikely to appear anywhere, so dedicating them for translation seems like a good idea.

Please notice that you'll need to add var { font-style: normal } to your main style.

You'll store the elements with either an id set (like <var id='title'>Title here in English</var>) if they are unique, or a class if not.

So, for example, your HTML markup will look like:

<h1><var id='title'>My wonderful project</var></h1>
<li><var id='MenuItem1'>Setup</var></li>
<li><var id='MenuItem2'>Firmware download</var></li>
<div><a href='aboutUs'><var class='aboutUs'>About us</var></a>


This is were the trick is going. There is no -non javascript- way to trigger the lang of the document automatically.

This means that either you set <html lang='fr'> for the root element and then you can select on this in the CSS, but you can't, as far as I'm aware of, remove the attribute and have different :lang(fr) selector working in your CSS. However, the browser will send an Accept-Language header withing the HTTP request, so we can still solve this server side by returning a different content based on the queried language.

Typically, in the embedded CSS in your webpage, you'll add a @import 'trans.css'; at the top (or use the longer <link> node in the header), but since we want to save space the former is smaller & better.

Then, your webserver in the microcontroller will send a specific trans.css file based on the browser's accepted language.

Typically, it'll send nothing for English (since it's the default language), but for another language, it'll send something like:

@charset 'utf-8';
var { visibility: hidden; } var:before { visibility: visible; } // Or any other content substitution technique
#title:before { content:'Titre attendu'; }
#MenuItem1:before { content:'Installation';  }
#MenuItem2:before { content:'Téléchargement du firmware';  }
var.aboutUs:before { content:'À propos'; }

The browser will automatically download this CSS and will replace the elements. This method does not work with screen readers that'll still read the English text, but neither does the javascript version.


This method does not require Javascript. Because you use a single web page, and most browsers emit 2 requests in parallel, it should download the translation before the main web page, so no FOUC to expect.

This method requires a translation table that's written in CSS, thus the overhead is a bit larger than a JSON's dictionary object.

Typically, for each key to translate, you'll have an overhead of 18 bytes (or 17 if using :after), before compression. Since the overhead is mainly due to same CSS terms :before and content:, they'll compress darn well via gzip and you can serve them directly compressed.


  • The elements that can't contain other elements (inline) should not contain <var> either. So you'll have to take this into account when designing your website.
  • The <var> initial content is still here (but not visible) so it'll be used by screen readers, and search indexer. Since the webpage is unlikely to be served on the main internet, I'm not sure it's a big loss.
  • Everyone says that CSS is not for the updating the content, but honestly, why did they add content rule then ?

Previous Post

Related Posts