Internationalization (i18n) in Rust: gettext-rs and fluent-rs
Internationalization (i18n) is a key aspect of modern application development, allowing software to be adapted for users from different countries and cultures. In the Rust ecosystem, there are several powerful libraries for implementing i18n. In this article, we will look at two of the most popular: gettext-rs (a classic approach based on gettext) and fluent-rs (a modern localization system from Mozilla).
1. The gettext-rs Library
gettext-rs is a Rust wrapper around the classic GNU gettext system. It is ideal for projects that already use gettext, or for those who prefer time-tested solutions.
Installation and Setup
Add the dependency to Cargo.toml:
[dependencies]gettext-rs = "0.7"gettext-sys = "0.21"Basic Usage Example
use gettextrs::*;
fn main() { // Set the locale and translation domain setlocale(LocaleCategory::LcAll, "ru_RU.UTF-8"); bindtextdomain("myapp", "/usr/share/locale").unwrap(); textdomain("myapp").unwrap();
// Use translation let greeting = gettext("Hello, world!"); println!("{}", greeting);
// Plural forms let items = 5; let message = ngettext("{} item", "{} items", items); println!("{}", message.replace("{}", &items.to_string()));}Working with Context
To distinguish identical strings in different contexts, use pgettext:
let menu_item = pgettext("menu", "File");let dialog_title = pgettext("dialog", "File");2. The fluent-rs Library
fluent-rs is a modern, flexible, and powerful localization system developed by Mozilla. It supports complex grammatical structures, plural forms, and contextual translations.
Installation
[dependencies]fluent = "0.16"fluent-bundle = "0.15"fluent-resmgr = "0.5"fluent-langneg = "0.4"unic-langid = "0.9"Creating Translation Files (FTL)
Fluent uses its own .ftl file format. Example for Russian (ru.ftl):
hello-world = Привет, мир!greeting = Здравствуйте, {$name}!items-count = { $count -> [one] {$count} элемент [few] {$count} элемента [many] {$count} элементов *[other] {$count} элементов}Example of Using fluent-rs
use fluent_bundle::{FluentBundle, FluentResource};use unic_langid::langid;use std::fs;
fn main() { // Load the translation file let ftl_string = fs::read_to_string("locales/ru.ftl") .expect("Failed to read localization file"); let resource = FluentResource::try_new(ftl_string) .expect("FTL parsing error");
// Create a bundle for the Russian language let lang_ru = langid!("ru"); let mut bundle = FluentBundle::new(&[lang_ru]); bundle.add_resource(&resource).unwrap();
// Get a translation with parameters let mut errors = vec![]; let msg = bundle.get_message("greeting").unwrap(); let mut args = fluent_bundle::FluentArgs::new(); args.set("name", "Alexey"); let pattern = msg.value().unwrap(); let value = bundle.format_pattern(pattern, Some(&args), &mut errors); println!("{}", value);
// Plural forms let msg = bundle.get_message("items-count").unwrap(); let mut args = fluent_bundle::FluentArgs::new(); args.set("count", 3); let pattern = msg.value().unwrap(); let value = bundle.format_pattern(pattern, Some(&args), &mut errors); println!("{}", value);}3. Comparison of gettext-rs and fluent-rs
- gettext-rs:
-
Also in library
Recommendations
-