Emaili18nTemplatesLocalizationSaaSEmail Marketing

Multi-Language Email Templates for SaaS

Design and implement multi-language email templates for SaaS — locale-aware content, template engines, fallback strategies, and testing across languages.

Sam Rivera
Sam Rivera
June 13, 202613 min read

TL;DR: Multi-language email templates require careful design — locale-aware content, template inheritance, dynamic subject lines, and robust fallback chains. This guide covers architecture and implementation. Patterns from tanstackship.com.

Introduction

Email is a critical communication channel for SaaS, and sending emails in the wrong language damages trust. A proper multi-language email system handles translations, locale-specific formatting, and content variations across 10+ languages without duplicating templates. For broader i18n strategy, see our i18n for SaaS: Architecture Patterns Compared.

Email Template Architecture

ComponentResponsibilityExample
Template engineRender HTML with variablesHandlebars, MJML, React Email
Translation serviceLocale-aware contentParaglide.js JSON files
Formatting serviceLocale-aware numbers/datesIntl API
Image serviceLocale-specific imagesCDN with locale path
Fallback chainMissing translation handlingtarget → en → key

Template Structure

emails/
├── en/
│   ├── welcome.html        # English version
│   ├── welcome.subject     # Subject line only
│   ├── invoice.html
│   └── invoice.subject
├── de/
│   ├── welcome.html
│   ├── welcome.subject
│   ├── invoice.html
│   └── invoice.subject
└── shared/
    ├── header.html          # Shared components
    ├── footer.html
    └── styles.css

Implementation with React Email

tsx
// React Email component with i18n
import * as m from '~/paraglide/messages.js'
import {
  Body, Container, Head, Heading, Html,
  Preview, Section, Text, Button,
} from '@react-email/components'

interface WelcomeEmailProps {
  userName: string
  locale: string
  dashboardUrl: string
}

export function WelcomeEmail({ userName, locale, dashboardUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>{m.email_welcome_preview({ name: userName })}</Preview>
      <Body>
        <Container>
          <Heading>{m.email_welcome_title({ name: userName })}</Heading>
          
          <Text>{m.email_welcome_body()}</Text>
          
          <Section>
            <Button href={dashboardUrl}>
              {m.email_welcome_cta()}
            </Button>
          </Section>
          
          <Text style={{ fontSize: '12px', color: '#666' }}>
            {m.email_welcome_footer()}
          </Text>
        </Container>
      </Body>
    </Html>
  )
}

Dynamic Subject Lines

ts
// Subject line per locale, with variables
const subjectTemplates = {
  en: 'Welcome to TanStack Ship, {name}!',
  de: 'Willkommen bei TanStack Ship, {name}!',
  zh: '{name},欢迎使用 TanStack Ship!',
  ja: '{name}さん、TanStack Shipへようこそ!',
  ar: 'مرحبًا {name} في TanStack Ship!',
}

function getSubject(template: string, locale: string, vars: Record<string, string>) {
  let subject = subjectTemplates[locale] || subjectTemplates['en']
  Object.entries(vars).forEach(([key, value]) => {
    subject = subject.replace(`{${key}}`, value)
  })
  return subject
}

Sending with Fallback

ts
// Email sending with locale fallback
async function sendEmail({
  to,
  locale,
  template,
  data,
}: {
  to: string
  locale: string
  template: string
  data: Record<string, any>
}) {
  const effectiveLocale = SUPPORTED_LOCALES.includes(locale) ? locale : 'en'
  
  // Load template with fallback chain
  const html = await loadTemplate(template, effectiveLocale, data)
  const subject = await loadSubject(template, effectiveLocale, data)
  
  await sendEmailClient({
    to,
    subject,
    html,
    headers: {
      // Track language for analytics
      'X-Email-Locale': effectiveLocale,
    },
  })
}

Testing Multi-Language Emails

Test CaseMethodWhat to Check
All locales renderAutomated render testNo missing keys, no errors
Subject line lengthCharacter countArabic/Chinese subjects may be shorter
RTL renderingVisual inspectionArabic emails read right-to-left
Date/number formatSnapshot testLocale-correct formatting
Fallback behaviorTest with unsupported localeGraceful fallback to en

Conclusion

Use a single template system with locale-specific content files. React Email components with Paraglide.js provide type-safe translations. Always render a fallback locale when a translation is missing. Test every email template in all supported locales as part of your CI pipeline.

For locale-aware formatting in emails, see Localizing Date, Time, Number, and Currency Formats. Our SaaS Email Deliverability Guide covers SPF, DKIM, and DMARC setup. For building locale-specific marketing pages, check out Building a Language-Specific Landing Page Strategy.