← Back to the blog

Multilingual Markdown: one image, every language

Write your post in Markdown, add one ::oim line and the same image shows its text in the reader's language. No exporting one version per language.

If you run a blog in two languages, you know the problem. The text of a post translates in a while. The images do not. The cover has a headline, the diagram has labels, the screenshot has an arrow with a note. All of that is painted into the pixels. For the English version you open the design, change the words, export again and upload another file. Multiply that by every image and every language.

This blog works differently. Every post is written in Markdown, and every image is a single .oim file that serves both versions. This post explains how, with one line.

Markdown, briefly

Markdown is a way to write formatted text with simple marks. A # before a line makes it a heading. Two asterisks around a word make it bold. A dash at the start of a line starts a list.

GitHub, most documentation tools and many blogs use it, because it reads well even unconverted and keeps what you say apart from how it looks.

For images, Markdown has its own syntax: ![description](file.png). It works, but it treats the image as a closed block. It knows nothing about languages.

One line for a living image

On this blog we added a directive of our own. A directive is a Markdown extension that starts with a double colon:

::oim{src="https://embed.oim.media/blog/cover.oim.png" ratio="16/9"}
  • src is the address of the .oim file.
  • ratio is its proportion, so the page reserves the space before it loads and nothing jumps while you read.

On publish, that line becomes an OIM player with the image inside. And here is the key: the player also receives the language of the post. If you are reading the Spanish version, it draws the text in Spanish. Open the English version and it draws the same text in English. The file is the same.

The cover of this post is an example. Switch language with the selector at the top and the headline changes, while the image address does not.

Why it works

An .oim does not store text flattened into the picture. It stores the base image on one side and, on the other, every text in all its languages. We go into more detail in What is .oim and Multilingual images.

That has practical consequences for whoever writes:

  • One file per image, not one per language. The Spanish Markdown and the English Markdown carry exactly the same ::oim line.
  • Fixing a typo means fixing it once. Change the text in the editor, export, and both versions are up to date.
  • Text stays text. It can be selected, screen readers read it, and it stays sharp at any size.
  • If the player does not load, there is still an image. An .oim is a valid PNG.

A complete example

This is the opening of a post in Spanish:

Este trimestre crecimos en tres mercados.

::oim{src="https://embed.oim.media/blog/sales-q3.oim.png" ratio="16/9"}

El gráfico muestra las ventas por región.

And the same opening in English:

This quarter we grew in three markets.

::oim{src="https://embed.oim.media/blog/sales-q3.oim.png" ratio="16/9"}

The chart shows sales by region.

The paragraph is translated, the image line is not. The chart labels appear as «Norte», «Sur», «Ventas» in the first and as "North", "South", "Sales" in the second, because both translations live inside the file.

And regular images

Not everything has to be an .oim. For a photo with no text we use another directive:

::image{src="https://embed.oim.media/blog/photo.webp" alt="Desk with sketches"}

Here the alternative text (alt) is written in each language, because it is part of each version's Markdown. The rule of thumb: if the image has words in it, .oim. If it is just a photo, ::image.

How we prepare the images

The images on this blog are designed in the OIM editor, with every text written in Spanish and English. When exporting to the blog, the tool returns the ::oim line already written, ready to paste into the post. No copying addresses by hand.

If you build your own site, the same idea works outside this blog: the OIM player accepts a lang attribute, and how to embed it has its own post.

FAQ

Do I need a different image for each language?

No. A single .oim file holds the text for all its languages, and the player shows the one that matches the post.

What if a language is not translated inside the image?

If a text is missing in the reader's language, the player falls back to English, then Spanish, then the first language available. That is why it pays to write every text in all of the blog's languages before exporting.

Can I use regular Markdown image syntax for an .oim?

It will show as a static PNG, with no live text or animation. For the image to speak the reader's language, use ::oim.

Does it affect Google rankings?

The post is served already rendered, with its text, and the image reserves its space with ratio, so the page does not jump while loading.

Write the post twice if you must. The image, only once.