Generating Localised Open Graph Images per Language at Build Time
When a multilingual page is shared on LinkedIn, WhatsApp or Slack, the preview image is often the English one, even on the Japanese or German version. It looks careless and it lowers click-through from shares. Localised
When a multilingual page is shared on LinkedIn, WhatsApp or Slack, the preview image is often the English one, even on the Japanese or German version. It looks careless and it lowers click-through from shares. Localised Open Graph images are easy to generate at build time with a template and a small script.
The approach
Keep one SVG template with placeholders for the title and a short tagline. For each page and locale, fill in the translated strings, render to PNG at 1200×630 and reference it in that locale's meta tags.
// og.js — node og.js
const fs = require("fs");
const sharp = require("sharp");
const template = fs.readFileSync("og-template.svg", "utf8");
const pages = JSON.parse(fs.readFileSync("pages.json", "utf8"));
// pages.json: [{ "slug": "pricing", "locales": { "en": {"title": "..."}, "ja": {"title": "..."} } }]
function esc(s) {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
(async () => {
for (const p of pages) {
for (const [loc, t] of Object.entries(p.locales)) {
const svg = template.replace("{{TITLE}}", esc(t.title)).replace("{{TAG}}", esc(t.tagline || ""));
fs.mkdirSync(`public/og/${loc}`, { recursive: true });
await sharp(Buffer.from(svg)).png().toFile(`public/og/${loc}/${p.slug}.png`);
}
}
})();
Fonts are the real problem
SVG rendering uses the fonts installed on the build machine. Chinese, Japanese, Korean, Arabic and Thai need explicit fonts, or you get empty boxes. Install Noto fonts in your CI image and set a font-family stack in the template that covers every script you publish. Test right-to-left languages separately; you may need a mirrored layout for Arabic and Hebrew.
Line breaking
German titles get long and CJK titles have no spaces. A simple approach is to measure character counts per script and pick a smaller font size above a threshold. For CJK, break lines every 14 to 16 characters.
Wire up the meta tags
Each locale page should reference its own image and declare the locale:
<meta property="og:image" content="https://example.com/og/ja/pricing.png">
<meta property="og:locale" content="ja_JP">
<meta property="og:locale:alternate" content="en_US">
Keep hreflang and canonical tags consistent with these URLs; social previews do not affect rankings directly, but mismatched locale signals make audits noisy.
Cache busting
Social platforms cache previews aggressively. Add a content hash to the filename when titles change, and use each platform's debugging tool to refresh.
This is one of the small details that make a multilingual site feel native rather than translated. If your site runs on WordPress, our multilingual WordPress guide covers plugins and URL structure that make per-locale assets easier to manage.
Published by PandaWM, an overseas marketing team based in Hong Kong.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.