Email шаблоны на разных языках

Email-уведомления в многоязычных приложениях требуют строгой организации строк интерфейса, поскольку письма часто содержат динамические данные, зависят от контекста пользователя и должны сохранять одинаковую семантику во всех локалях. Библиотека i18next позволяет выстроить систему, в которой email-шаблоны становятся частью единого слоя локализации, а не отдельной подсистемой.

Ключевая идея: email — это тот же набор переводов, что и UI, но с отдельным namespace и расширенными правилами форматирования.


Разделение email-шаблонов по namespace

В i18next email-шаблоны обычно выделяются в отдельный namespace, например:

{
  "welcomeEmail": {
    "subject": "Добро пожаловать, {{name}}!",
    "title": "Регистрация завершена",
    "body": "Спасибо за регистрацию в сервисе {{appName}}.",
    "cta": "Перейти в аккаунт"
  }
}

Структура namespace позволяет:

  • изолировать письма от UI-строк
  • упрощать поддержку нескольких типов уведомлений
  • подключать email-локализацию только в нужных модулях

Пример подключения:

import i18next from "i18next";

i18next.init({
  lng: "ru",
  fallbackLng: "en",
  resources: {
    ru: {
      email: {
        welcomeEmail: {
          subject: "Добро пожаловать, {{name}}!"
        }
      }
    }
  }
});

Интерполяция динамических данных в письмах

Email-шаблоны почти всегда содержат динамические значения: имя пользователя, ссылки, номера заказов, даты.

i18next поддерживает интерполяцию через {{ }}:

i18next.t("email:welcomeEmail.subject", {
  name: "Алексей"
});

Результат:

Добро пожаловать, Алексей!

Особенности интерполяции в email-контексте

  • данные должны быть экранированы автоматически
  • HTML-содержимое требует отдельного режима
  • ссылки лучше передавать как переменные, а не конкатенацию

Пример HTML-письма:

{
  "welcomeEmail": {
    "html": "<h1>Привет, {{name}}</h1><p>Перейдите по ссылке: {{link}}</p>"
  }
}

HTML-шаблоны и безопасная подстановка

Email часто содержит HTML-разметку. Это требует разделения:

  • текстовых строк
  • HTML-строк
  • безопасных переменных

Пример:

i18next.t("email:welcomeEmail.html", {
  name: "Алексей",
  link: "https://example.com/dashboard"
});

Важно учитывать:

  • HTML должен храниться в переводах, а не собираться в коде
  • переменные не должны содержать невалидную разметку
  • желательно ограничивать набор допустимых тегов

Работа с вложенными структурами

Email-шаблоны часто имеют сложную структуру: заголовок, тело, кнопки, подписи.

Пример вложенного JSON:

{
  "passwordReset": {
    "subject": "Сброс пароля",
    "content": {
      "greeting": "Здравствуйте, {{name}}",
      "instruction": "Для сброса пароля нажмите кнопку ниже",
      "button": "Сбросить пароль",
      "footer": "Если это были не вы — проигнорируйте письмо"
    }
  }
}

Использование:

i18next.t("email:passwordReset.content.instruction");

Плюрализация в email-уведомлениях

В письмах часто встречаются количества: число заказов, уведомлений, сообщений.

i18next поддерживает plural rules:

{
  "orders": {
    "one": "У вас {{count}} заказ",
    "few": "У вас {{count}} заказа",
    "many": "У вас {{count}} заказов"
  }
}

Использование:

i18next.t("email:orders", { count: 5 });

Система автоматически выбирает нужную форму для языка.


Контекстные варианты (gender / status / role)

Email-сообщения часто зависят от роли пользователя или статуса операции.

{
  "invite": {
    "admin": "Вы приглашены как администратор",
    "user": "Вы приглашены как пользователь"
  }
}

Вызов:

i18next.t("email:invite", { context: "admin" });

Это позволяет избежать дублирования шаблонов.


Формирование шаблонов писем через сервисный слой

Обычно email не генерируется напрямую из UI-кода. Используется отдельный слой:

function buildWelcomeEmail(user) {
  return {
    subject: i18next.t("email:welcomeEmail.subject", {
      name: user.name
    }),
    html: i18next.t("email:welcomeEmail.html", {
      name: user.name,
      appName: "MyApp"
    })
  };
}

Такой подход:

  • отделяет локализацию от бизнес-логики
  • упрощает тестирование
  • позволяет использовать единый формат для разных каналов (email, push, SMS)

Интеграция с серверной отправкой писем

Email-шаблоны часто используются вместе с nodemailer или аналогичными системами:

import nodemailer from "nodemailer";

async function sendEmail(user) {
  const email = buildWelcomeEmail(user);

  await transporter.sendMail({
    to: user.email,
    subject: email.subject,
    html: email.html
  });
}

Локализация полностью отделена от транспорта отправки.


Поддержка fallback-языков

При отсутствии перевода в нужной локали система использует fallback:

i18next.init({
  lng: "kk",
  fallbackLng: "en"
});

Для email это критично, поскольку отсутствие строки не должно приводить к отправке “пустого” письма.


Структура масштабируемых email-переводов

При росте проекта email-локализация требует строгой архитектуры:

email/
  welcomeEmail.json
  passwordReset.json
  orderConfirmation.json
  invoice.json

Каждый файл содержит:

  • subject
  • html
  • text fallback
  • structured blocks

Разделение text и HTML версий

Практика поддержки двух форматов:

{
  "welcomeEmail": {
    "text": "Добро пожаловать, {{name}}",
    "html": "<h1>Добро пожаловать, {{name}}</h1>"
  }
}

Это важно для:

  • клиентов без HTML
  • антиспам-фильтров
  • корпоративных почтовых систем

Работа с датами и форматированием

Email часто содержит даты и суммы. i18next интегрируется с форматированием через плагины:

i18next.t("email:orderConfirmation.date", {
  date: new Date()
});

В переводах:

{
  "date": "Дата заказа: {{date, datetime}}"
}

Многоязычные ссылки и динамические URL

Email часто включает ссылки, зависящие от языка:

{
  "cta": "https://example.com/{{lng}}/dashboard"
}

Использование:

i18next.t("email:welcomeEmail.cta", {
  lng: i18next.language
});

Кэширование и производительность

При генерации большого количества писем важно:

  • кэшировать переводы
  • избегать повторного init i18next
  • использовать preload языков
i18next.init({
  preload: ["ru", "en", "kk"],
  ns: ["email"]
});

Ошибки типичной архитектуры email-локализации

  • хранение текста писем в коде вместо ресурсов
  • отсутствие namespace разделения
  • конкатенация строк вместо интерполяции
  • отсутствие fallback-языков
  • смешивание UI и email переводов
  • дублирование HTML в коде

Стандартизация шаблонов в крупных системах

При масштабировании применяется единый контракт email-шаблона:

type EmailTemplate = {
  subject: string;
  text: string;
  html: string;
};

И функция-адаптер:

function tEmail(key, params) {
  return {
    subject: i18next.t(`${key}.subject`, params),
    text: i18next.t(`${key}.text`, params),
    html: i18next.t(`${key}.html`, params)
  };
}

Многоязычные уведомления как единая система

Email становится частью общей системы интернационализации:

  • UI использует те же ресурсы
  • backend использует те же ключи
  • письма строятся из тех же переводов
  • поведение определяется языковыми правилами i18next