Интернационализация (i18n)
Фреймворк поддерживает интернационализацию на основе gettext. Переводы организованы по доменам, что позволяет разделять локализацию между модулями приложения.
Конфигурация
Переводы настраиваются в секции translations файла config.json:
{
"translations": [
{
"domain": "identity",
"path": "backend/identity/locale"
},
{
"domain": "shop",
"path": "backend/shop/locale"
}
]
}domain строка
Имя домена переводов. Используется для обращения к конкретному набору переводов в коде.
path строка
Путь до директории с файлами локализации.
Структура файлов переводов
Файлы переводов должны располагаться по следующему пути:
<path>/<lang>/LC_MESSAGES/<domain>.moНапример, для домена identity и языка ru:
backend/identity/locale/ru/LC_MESSAGES/identity.mo
backend/identity/locale/en/LC_MESSAGES/identity.moСоздание файлов переводов
1. Создание PO-файла
Создайте файл identity.po для каждого языка:
# Russian translations for identity module
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: ru\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "Welcome"
msgstr "Добро пожаловать"
msgid "Invalid credentials"
msgstr "Неверные учетные данные"
# Plural forms
msgid "error"
msgid_plural "errors"
msgstr[0] "ошибка"
msgstr[1] "ошибки"
msgstr[2] "ошибок"2. Компиляция в MO-файл
msgfmt -o identity.mo identity.poИспользование в коде
Подключите заголовочный файл:
#include "translation.h"Простой перевод
Функция tr возвращает перевод по идентификатору сообщения:
const char* message = tr(ctx, "identity", "Welcome");
// Результат: "Добро пожаловать" (для ru) или "Welcome" (для en)Внимание
Не освобождайте память, возвращаемую функцией tr. Строка управляется системой переводов.
Перевод с подстановкой
Функция trf заменяет плейсхолдеры {key} на переданные значения:
// PO-файл: msgid "Hello, {name}!" msgstr "Привет, {name}!"
char* message = trf(ctx, "identity", "Hello, {name}!", "name", username, NULL);
// Результат: "Привет, Иван!"
free(message); // Необходимо освободить памятьСписок аргументов
Аргументы передаются парами "ключ", "значение" и завершаются NULL.
Множественные формы
Функция trn выбирает правильную форму в зависимости от числа:
const char* message = trn(ctx, "identity", "error", "errors", error_count);
// 1 -> "ошибка"
// 2 -> "ошибки"
// 5 -> "ошибок"Множественные формы с подстановкой
Функция trnf комбинирует множественные формы и подстановку:
char count_str[16];
snprintf(count_str, sizeof(count_str), "%d", count);
char* message = trnf(ctx, "identity", "{n} error found", "{n} errors found",
count, "n", count_str, NULL);
// 1 -> "1 ошибка найдена"
// 3 -> "3 ошибки найдено"
free(message);Определение языка
Язык определяется автоматически в следующем порядке приоритета:
- Query-параметр
lang—?lang=ru - Заголовок
Accept-Language— парсится для получения предпочтительного языка - Язык по умолчанию —
en
Пример
GET /api/users?lang=ru
Accept-Language: en-US,en;q=0.9В этом случае будет использован русский язык (ru), так как query-параметр имеет наивысший приоритет.
API-справочник
tr
const char* tr(httpctx_t* ctx, const char* domain, const char* msgid);| Параметр | Описание |
|---|---|
| ctx | HTTP-контекст для определения языка |
| domain | Домен переводов |
| msgid | Идентификатор сообщения |
| Возврат | Переведенная строка (не освобождать) |
trf
char* trf(httpctx_t* ctx, const char* domain, const char* msgid, ...);| Параметр | Описание |
|---|---|
| ctx | HTTP-контекст для определения языка |
| domain | Домен переводов |
| msgid | Идентификатор сообщения с плейсхолдерами |
| ... | Пары "ключ", "значение", завершенные NULL |
| Возврат | Переведенная строка (вызывающий должен освободить) |
trn
const char* trn(httpctx_t* ctx, const char* domain, const char* singular,
const char* plural, unsigned long n);| Параметр | Описание |
|---|---|
| ctx | HTTP-контекст для определения языка |
| domain | Домен переводов |
| singular | Форма единственного числа |
| plural | Форма множественного числа |
| n | Число для выбора формы |
| Возврат | Переведенная строка (не освобождать) |
trnf
char* trnf(httpctx_t* ctx, const char* domain, const char* singular,
const char* plural, unsigned long n, ...);| Параметр | Описание |
|---|---|
| ctx | HTTP-контекст для определения языка |
| domain | Домен переводов |
| singular | Форма единственного числа с плейсхолдерами |
| plural | Форма множественного числа с плейсхолдерами |
| n | Число для выбора формы |
| ... | Пары "ключ", "значение", завершенные NULL |
| Возврат | Переведенная строка (вызывающий должен освободить) |
Пример использования
#include "http.h"
#include "translation.h"
void get_profile(httpctx_t* ctx) {
// Простой перевод
const char* title = tr(ctx, "identity", "Profile");
// Перевод с подстановкой
char* greeting = trf(ctx, "identity", "Welcome back, {name}!",
"name", user->name, NULL);
// Множественное число
char count_str[16];
snprintf(count_str, sizeof(count_str), "%d", notification_count);
char* notifications = trnf(ctx, "identity",
"You have {n} new notification",
"You have {n} new notifications",
notification_count, "n", count_str, NULL);
// ... формирование ответа ...
free(greeting);
free(notifications);
}