Internationalization (i18n)
The framework supports internationalization based on gettext. Translations are organized by domains, allowing you to separate localization between application modules.
Configuration
Translations are configured in the translations section of config.json:
{
"translations": [
{
"domain": "identity",
"path": "backend/identity/locale"
},
{
"domain": "shop",
"path": "backend/shop/locale"
}
]
}domain string
Translation domain name. Used to reference a specific set of translations in code.
path string
Path to the locale directory.
Translation file structure
Translation files should be located at the following path:
<path>/<lang>/LC_MESSAGES/<domain>.moFor example, for domain identity and language ru:
backend/identity/locale/ru/LC_MESSAGES/identity.mo
backend/identity/locale/en/LC_MESSAGES/identity.moCreating translation files
1. Create PO file
Create an identity.po file for each language:
# English translations for identity module
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: en\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Welcome"
msgstr "Welcome"
msgid "Invalid credentials"
msgstr "Invalid credentials"
# Plural forms
msgid "error"
msgid_plural "errors"
msgstr[0] "error"
msgstr[1] "errors"2. Compile to MO file
msgfmt -o identity.mo identity.poUsage in code
Include the header file:
#include "translation.h"Simple translation
The tr function returns a translation by message identifier:
const char* message = tr(ctx, "identity", "Welcome");
// Result: "Welcome" (for en) or localized string (for other languages)Warning
Do not free the memory returned by tr. The string is managed by the translation system.
Translation with placeholders
The trf function replaces {key} placeholders with provided values:
// PO file: msgid "Hello, {name}!" msgstr "Hello, {name}!"
char* message = trf(ctx, "identity", "Hello, {name}!", "name", username, NULL);
// Result: "Hello, John!"
free(message); // Must free the memoryArgument list
Arguments are passed as "key", "value" pairs and terminated with NULL.
Plural forms
The trn function selects the correct form based on the count:
const char* message = trn(ctx, "identity", "error", "errors", error_count);
// 1 -> "error"
// 2 -> "errors"Plural forms with placeholders
The trnf function combines plural forms and placeholder substitution:
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 error found"
// 3 -> "3 errors found"
free(message);Language detection
Language is determined automatically in the following order of priority:
- Query parameter
lang—?lang=ru Accept-Languageheader — parsed to get the preferred language- Default language —
en
Example
GET /api/users?lang=ru
Accept-Language: en-US,en;q=0.9In this case, Russian (ru) will be used since the query parameter has the highest priority.
API reference
tr
const char* tr(httpctx_t* ctx, const char* domain, const char* msgid);| Parameter | Description |
|---|---|
| ctx | HTTP context for language detection |
| domain | Translation domain |
| msgid | Message identifier |
| Returns | Translated string (do not free) |
trf
char* trf(httpctx_t* ctx, const char* domain, const char* msgid, ...);| Parameter | Description |
|---|---|
| ctx | HTTP context for language detection |
| domain | Translation domain |
| msgid | Message identifier with placeholders |
| ... | "key", "value" pairs terminated with NULL |
| Returns | Translated string (caller must free) |
trn
const char* trn(httpctx_t* ctx, const char* domain, const char* singular,
const char* plural, unsigned long n);| Parameter | Description |
|---|---|
| ctx | HTTP context for language detection |
| domain | Translation domain |
| singular | Singular form |
| plural | Plural form |
| n | Count for form selection |
| Returns | Translated string (do not free) |
trnf
char* trnf(httpctx_t* ctx, const char* domain, const char* singular,
const char* plural, unsigned long n, ...);| Parameter | Description |
|---|---|
| ctx | HTTP context for language detection |
| domain | Translation domain |
| singular | Singular form with placeholders |
| plural | Plural form with placeholders |
| n | Count for form selection |
| ... | "key", "value" pairs terminated with NULL |
| Returns | Translated string (caller must free) |
Usage example
#include "http.h"
#include "translation.h"
void get_profile(httpctx_t* ctx) {
// Simple translation
const char* title = tr(ctx, "identity", "Profile");
// Translation with placeholder
char* greeting = trf(ctx, "identity", "Welcome back, {name}!",
"name", user->name, NULL);
// Plural form
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);
// ... build response ...
free(greeting);
free(notifications);
}