Table of Contents
Email is still the highest-ROI channel for most SaaS companies. But most teams localize their product UI and completely forget about their emails — sending English welcome emails to users in Brazil, billing alerts to Japanese customers in a language they may not read fluently, and onboarding sequences that ignore cultural context entirely.
This guide covers everything you need to know about email template localization: what to translate, how to handle dynamic variables, RTL language support, and how to build a workflow that scales without turning your engineering team into professional translators.
Why Email Localization Is Harder Than UI Localization
Localizing a button label is straightforward. Localizing an email is a different beast:
- Rich formatting — HTML emails have complex layouts that break with longer text in German or shorter text in Chinese
- Dynamic variables —
Hello {{name}}, your {{plan}} subscription renews on {{date}}needs to handle variable ordering that differs by language - Plural forms — "You have 1 message" vs "You have 3 messages" — ICU MessageFormat handles this, but most email tools don't
- RTL languages — Arabic and Hebrew require mirrored layouts, right-aligned text, and directional HTML attributes
- Date and number formatting — December 3 in the US is 3. Dezember in Germany and ٣ ديسمبر in Arabic
- Tone and formality — German business emails use formal "Sie", Japanese has honorifics, Brazilian Portuguese is warmer than European Portuguese
These aren't reasons to avoid email localization — they're reasons to build it properly from the start.
Step 1: Audit Your Email Templates
Before writing a single line of code, inventory every email your product sends:
Transactional emails (highest priority):
- Welcome / account created
- Email verification
- Password reset
- Payment confirmation / receipt
- Subscription renewal reminder
- Invoice / billing
- Account suspension / deletion warning
Engagement emails (medium priority):
- Onboarding sequences (day 1, day 3, day 7)
- Feature announcement
- Usage milestones ("You've reached 1,000 translations!")
- Re-engagement campaigns
Operational emails (lower priority, but still important for enterprise):
- Team invitation
- Role change notification
- API key expiration warning
- Maintenance alerts
For each email, note: does it contain dynamic variables? Plural forms? Dates? User-generated content? This informs your localization approach.
Step 2: Separate Content from Template Structure
The most common mistake is embedding translated text directly in HTML templates. Instead, treat email content like you treat your UI — use a key-value translation system.
The wrong approach
<!-- en/welcome.html -->
<h1>Welcome to Better i18n, {{firstName}}!</h1>
<p>You're all set to start localizing your app.</p>
<!-- de/welcome.html -->
<h1>Willkommen bei Better i18n, {{firstName}}!</h1>
<p>Sie können jetzt mit der Lokalisierung Ihrer App beginnen.</p>
Maintaining 8 copies of every email template for 8 languages is a nightmare. Change the layout once and you have 8 files to update.
The right approach
<!-- welcome.html (single template) -->
<h1>{{t "email.welcome.heading" firstName=firstName}}</h1>
<p>{{t "email.welcome.body"}}</p>
// en.json
{
"email": {
"welcome": {
"heading": "Welcome to Better i18n, {firstName}!",
"body": "You're all set to start localizing your app."
}
}
}
// de.json
{
"email": {
"welcome": {
"heading": "Willkommen bei Better i18n, {firstName}!",
"body": "Sie können jetzt mit der Lokalisierung Ihrer App beginnen."
}
}
}
This gives you one template, N translation files.
Step 3: Use ICU MessageFormat for Variables and Plurals
ICU MessageFormat is the industry standard for handling complex localization cases in strings. Most modern i18n libraries support it — and you should use it for email copy too.
Basic variable substitution
"email.welcome.heading" = "Welcome to Better i18n, {firstName}!"
Plural forms
"email.usage.messages" = "{count, plural, one {You have # unread message.} other {You have # unread messages.}}"
This automatically handles "1 message" vs "3 messages" — and Russian, which has four plural forms.
Date formatting
Rather than passing pre-formatted dates as strings, pass ISO timestamps and format them in the template engine with the locale context:
// Instead of:
t('email.renewal', { date: 'December 3, 2025' }) // wrong locale format
// Do:
t('email.renewal', { date: new Date('2025-12-03') })
// Result in en: "December 3, 2025"
// Result in de: "3. Dezember 2025"
// Result in ja: "2025年12月3日"
Step 4: Handle RTL Languages
Arabic and Hebrew are right-to-left, which affects your entire email layout. Add dir attribute based on locale:
<!DOCTYPE html>
<html dir="{{direction}}" lang="{{locale}}">
const direction = ['ar', 'he', 'fa', 'ur'].includes(locale) ? 'rtl' : 'ltr';
For RTL emails:
- Text aligns right by default
- Use logical CSS properties:
padding-inline-start: 24pxinstead ofpadding-left - Icons and arrows that indicate direction need mirrored versions
- Multi-column layouts may need to flip column order
Test your RTL emails in Gmail, Apple Mail, and Outlook — each handles dir differently.
Step 5: Build a Translation Workflow for Email Content
Option A: Manual process (doesn't scale)
Export string files → send to translation vendor → wait → import back → hope nothing broke. Works for 2 languages. Falls apart at 8.
Option B: Automated with Better i18n
Better i18n treats email translation keys the same as UI translation keys:
{
"email.welcome.subject": "Welcome to Better i18n!",
"email.welcome.heading": "Welcome, {firstName}!",
"email.welcome.body": "Your account is ready. Let's get started.",
"email.welcome.cta": "Open Dashboard"
}
Sync via CLI or GitHub integration, use AI translation for speed, review for quality, then access at send time:
import { loadTranslations } from '@better-i18n/core';
async function sendWelcomeEmail(user: User) {
const t = await loadTranslations(user.locale, 'email');
await emailProvider.send({
to: user.email,
subject: t('email.welcome.subject'),
html: renderTemplate('welcome', {
heading: t('email.welcome.heading', { firstName: user.firstName }),
body: t('email.welcome.body'),
cta: t('email.welcome.cta'),
direction: getDirection(user.locale),
locale: user.locale,
}),
});
}
Step 6: Test Your Localized Emails
Content testing:
- All variables render correctly in each locale
- Pluralization works for edge cases (0, 1, 2, 11, 100)
- Long German/Finnish text doesn't break layout
- Short Chinese/Japanese text doesn't look sparse
Visual testing:
- RTL layout renders correctly in Gmail, Outlook, Apple Mail
- Images have localized alt text
- CTA button text fits correctly in all languages
Use a tool like Litmus or Email on Acid for cross-client rendering, and test at least 3 representative languages (Western Latin, RTL, and Asian).
Common Mistakes to Avoid
Hardcoding locale-specific assumptions — Don't assume all users have first + last names, that dates use MM/DD/YYYY, or that phone numbers have 10 digits.
Translating emails last — Email localization requires different expertise than UI localization. Plan for it from the start.
Ignoring email client limitations — Some clients strip <style> tags. Test your RTL layout in Outlook specifically.
Not updating translations when copy changes — Better i18n's translation key system flags untranslated or outdated keys automatically.
The Business Case for Email Localization
Numbers from SaaS companies that have invested in email localization:
- 35-50% higher open rates for emails in the recipient's native language
- 20-30% higher click-through rates on localized CTAs
- Reduced churn in non-English markets where product value was communicated in English
- Enterprise deals unlocked when procurement teams can review billing communications in their language
The ROI on email localization is typically 3-6 months for SaaS companies targeting multiple language markets.
Getting Started
- Audit your 3 highest-impact transactional emails (welcome, billing, password reset)
- Extract all strings into a translation system (Better i18n or equivalent)
- Translate to your top 2-3 non-English markets
- Set up automated sync so new email copy gets flagged for translation
- Expand from there
Better i18n makes steps 2-4 dramatically faster — your email strings live alongside your UI strings, AI translation handles the first pass, and GitHub sync keeps everything in sync automatically.