Интернационализация Go приложений: от $ к € без головной боли

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему вашему Go-приложению нужна интернационализация (i18n)?

Вы написали крутой сервис на Go. Он быстрый, конкурентный, компилируется за секунды. Но есть одна проблема: все сообщения, кнопки и подсказки в нём — на русском (или английском). А мир хочет пользоваться вашим продуктом на своём родном языке. Именно здесь на сцену выходит интернационализация Go приложений (i18n).

Многие думают, что локализация Go — это адская боль с JSON-файлами и костылями. Но это не так. Экосистема Go предлагает элегантные решения, которые позволяют добавить поддержку любого количества языков, не раздувая код и не теряя производительность. В этой статье мы разберём, как правильно внедрить i18n в Go, чтобы ваше приложение заговорило на сотне языков.

Что такое i18n и l10n? Разбираемся в терминологии

Прежде чем писать код, давайте наведём порядок в терминах:

  • i18n (Internationalization) — процесс проектирования приложения так, чтобы его можно было адаптировать под разные языки и регионы без переписывания кода. Это про архитектуру.
  • l10n (Localization) — адаптация контента (перевод текста, формат дат, валют) под конкретную локаль (например, en-US, ru-RU, de-DE).

В контексте интернационализации Go приложений, i18n — это подготовка кода (использование ключей вместо строк), а l10n — наполнение этих ключей переводами.

Выбираем библиотеку: почему go-i18n?

В Go есть несколько популярных библиотек для работы с переводами. После тестирования различных вариантов, лучшим выбором для большинства проектов является go-i18n от nicksnyder. Почему?

  • Простота использования — минимальный boilerplate код.
  • Поддержка плюрализации — правильное склонение для "1 сообщение", "2 сообщения", "5 сообщений".
  • Работа с TOML/YAML/JSON — выбирайте формат переводов на свой вкус.
  • Поддержка шаблонов — можно вставлять переменные прямо в строку перевода.
  • Встроенная утилита для извлечения ключей — не нужно писать переводы вручную.

Практический пример: делаем мультиязычный сервис на Go

Давайте создадим простой веб-сервер на Go, который приветствует пользователя на его языке.

Шаг 1: Установка и структура проекта

Установите библиотеку и создайте структуру каталогов:

go get github.com/nicksnyder/go-i18n/v2/i18n
go get golang.org/x/text/language

Структура проекта:

myapp/
├── main.go
└── locales/
    ├── active.en.toml
    ├── active.ru.toml
    └── active.de.toml

Шаг 2: Создаём файлы переводов

locales/active.en.toml:

[greeting]
description = "Приветствие пользователя"
one = "Hello, {{.Name}}!"
other = "Hello, {{.Name}}!"

[inbox]
description = "Количество сообщений в ящике"
one = "You have {{.Count}} new message."
other = "You have {{.Count}} new messages."

locales/active.ru.toml:

[greeting]
description = "Приветствие пользователя"
one = "Привет, {{.Name}}!"
other = "Привет, {{.Name}}!"

[inbox]
description = "Количество сообщений в ящике"
one = "У вас {{.Count}} новое сообщение."
few = "У вас {{.Count}} новых сообщения."
many = "У вас {{.Count}} новых сообщений."
other = "У вас {{.Count}} сообщений."

Обратите внимание на плюральные формы в русском языке: one, few, many, other. Плюрализация Go — это мощная фича, которая корректно обрабатывает сложные правила склонения.

Шаг 3: Пишем код на Go

Теперь самое интересное — интернационализация Go приложений в коде:

package main

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/nicksnyder/go-i18n/v2/i18n"
    "golang.org/x/text/language"
)

func main() {
    // Создаём бандл (хранилище переводов)
    bundle := i18n.NewBundle(language.English)
    bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)

    // Загружаем файлы переводов
    bundle.MustLoadMessageFile("locales/active.en.toml")
    bundle.MustLoadMessageFile("locales/active.ru.toml")
    bundle.MustLoadMessageFile("locales/active.de.toml")

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // Определяем язык пользователя из заголовка Accept-Language
        acceptLang := r.Header.Get("Accept-Language")
        lang := parseAcceptLanguage(acceptLang)

        // Создаём локализатор для конкретного языка
        localizer := i18n.NewLocalizer(bundle, lang)

        // Приветствие
        greeting, _ := localizer.Localize(&i18n.LocalizeConfig{
            MessageID: "greeting",
            TemplateData: map[string]string{
                "Name": "Алексей",
            },
        })

        // Сообщения в ящике
        inboxMsg, _ := localizer.Localize(&i18n.LocalizeConfig{
            MessageID: "inbox",
            TemplateData: map[string]interface{}{
                "Count": 3,
            },
            PluralCount: 3,
        })

        fmt.Fprintf(w, "%s\n%s", greeting, inboxMsg)
    })

    http.ListenAndServe(":8080", nil)
}

// Простой парсер Accept-Language (для примера)
func parseAcceptLanguage(header string) string {
    if strings.Contains(header, "ru") {
        return "ru"
    }
    if strings.Contains(header, "de") {
        return "de"
    }
    return "en"
}

Шаг 4: Запускаем и тестируем

Запустите сервер и проверьте:

# Английский (по умолчанию)
curl http://localhost:8080
# Вывод: Hello, Алексей! You have 3 new messages.

# Русский
curl -H "Accept-Language: ru-RU" http://localhost:8080
# Вывод: Привет, Алексей! У вас 3 новых сообщения.

Магия работает! Локализация Go произошла автоматически на основе заголовка браузера.

Продвинутые техники: даты, валюты и шаблоны

Просто перевести текст — мало. Настоящая интернационализация Go приложений включает форматирование дат, чисел и валют.

Форматирование дат с учётом лока

Блоги

Рекомендаций по книгам