Почему вашему 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 приложений включает форматирование дат, чисел и валют.