Por que sua aplicação Rust precisa de internacionalização (e como não estragar tudo)
\n\nVocê escreveu um parser perfeito em Rust. Ele voa mais rápido que um raio e consome menos memória que um modesto passarinho. Mas há um problema: seu texto só é exibido em inglês. E usuários de Tóquio, Berlim e São Petersburgo querem ver a interface em seu idioma nativo.
\n\nInternacionalização (i18n) não é apenas "traduzir botões". É uma decisão arquitetural que pode transformar seu código em um construtor elegante ou em um inferno de if-else. Neste artigo, vamos abordar como fazer internacionalização em Rust corretamente: sem dor, com exemplos e com respeito ao cérebro do futuro desenvolvedor (o seu).
\n\nFalaremos sobre duas abordagens poderosas: a biblioteca fluent (recomendação da Mozilla) e o clássico rust-i18n baseado em gettext. Ao final do artigo, você conseguirá adicionar suporte a russo, inglês e qualquer outro idioma em sua ferramenta em 15 minutos.
Plano de batalha: o que são i18n e l10n no mundo Rust
\n\nAntes de mergulhar no código, vamos alinhar os termos:
\n- \n
- Internacionalização (i18n) — preparação do código para tradução. Você extrai todas as strings para arquivos separados e ensina o programa a carregar a localidade correta. \n
- Localização (l10n) — a tradução em si. Criação de arquivos para cada idioma. \n
- Localidade — combinação de idioma e região (por exemplo,
ru-RU,en-US,de-DE). \n
Em Rust, existem vários caminhos. Vamos considerar os dois mais populares:
\n- \n
- Fluent — sistema moderno da Mozilla. Poderoso, flexível, com suporte a pluralização e contexto. \n
- rust-i18n — macros inspiradas no Ruby on Rails. Mais simples, mas menos flexível. \n
A escolha depende da tarefa. Para utilitários CLI, rust-i18n é suficiente. Para aplicações web ou GUIs complexas (por exemplo, com Tauri), é melhor usar Fluent.
Método nº1: Internacionalização Rust com Fluent (abordagem profissional)
\n\nInstalação e configuração do Fluent
\n\nVamos criar um novo projeto e adicionar as dependências:
\n\ncargo new i18n_demo\ncd i18n_demo\ncargo add fluent fluent-bundle fluent-langtag unic-langid\n\n\nA estrutura de arquivos será assim:
\n\nprojeto/\n├── Cargo.toml\n├── locales/\n│ ├── ru/\n│ │ └── main.ftl\n│ └── en/\n│ └── main.ftl\n└── src/\n └── main.rs\n\n\nCriando arquivos de localização (FTL)
\n\nFluent usa o formato FTL (Fluent Translation List). É puro e poderoso:
\n\nlocales/en/main.ftl:
\ngreeting = Hello, { $name }!\ngoodbye = Goodbye\napples = { $count ->\n [one] { $count } apple\n [few] { $count } apples\n *[other] { $count } apples\n}\n\n\nlocales/ru/main.ftl:
\ngreeting = Привет, { $name }!\ngoodbye = До свидания\napples = { $count ->\n [one] { $count } яблоко\n [few] { $count } яблока\n [many] { $count } яблок\n *[other] { $count } яблок\n}\n\n\nObserve a pluralização. Em russo, há quatro formas (um, dois-quatro, cinco-vinte, resto), enquanto em inglês há duas (um, resto). Fluent lida com isso automaticamente.
\n\nEscrevendo código Rust para carregar localidades
\n\nAgora a parte interessante — o código que selecionará o idioma correto com base no sistema do usuário:
\n\nuse fluent_bundle::{FluentBundle, FluentResource};\nuse unic_langid::LanguageIdentifier;\nuse std::fs;\nuse std::path::Path;\n\nfn load_locale(locale: &str) -> FluentBundle<FluentResource> {\n let langid: LanguageIdentifier = locale.parse().expect("Localidade inválida");\n let mut bundle = FluentBundle::new(vec![langid]);\n\n let file_path = format!("locales/{}/main.ftl", locale);\n let source = fs::read_to_string(&file_path)\n .expect(&fo