{"id":22826850,"url":"https://github.com/longbridge/rust-i18n","last_synced_at":"2026-01-15T22:18:03.493Z","repository":{"id":40468582,"uuid":"434122319","full_name":"longbridge/rust-i18n","owner":"longbridge","description":"A better and simply I18n crate for Rust.","archived":false,"fork":false,"pushed_at":"2025-11-14T10:28:09.000Z","size":362,"stargazers_count":541,"open_issues_count":19,"forks_count":42,"subscribers_count":12,"default_branch":"main","last_synced_at":"2026-01-13T04:07:45.879Z","etag":null,"topics":["crate","i18n","internationalization","localization","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/longbridge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-12-02T07:26:14.000Z","updated_at":"2026-01-11T21:43:02.000Z","dependencies_parsed_at":"2026-01-13T04:08:49.135Z","dependency_job_id":null,"html_url":"https://github.com/longbridge/rust-i18n","commit_stats":{"total_commits":144,"total_committers":7,"mean_commits":"20.571428571428573","dds":0.0625,"last_synced_commit":"5daffb1316eff4209fdcb30a5313dac9dae9b80f"},"previous_names":["longbridge/rust-i18n","longbridgeapp/rust-i18n"],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/longbridge/rust-i18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longbridge%2Frust-i18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longbridge%2Frust-i18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longbridge%2Frust-i18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longbridge%2Frust-i18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/longbridge","download_url":"https://codeload.github.com/longbridge/rust-i18n/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longbridge%2Frust-i18n/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28472625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T22:13:38.078Z","status":"ssl_error","status_checked_at":"2026-01-15T22:12:11.737Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crate","i18n","internationalization","localization","rust"],"created_at":"2024-12-12T18:02:07.482Z","updated_at":"2026-01-15T22:18:03.470Z","avatar_url":"https://github.com/longbridge.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Rust I18n\n\n[![CI](https://github.com/longbridge/rust-i18n/actions/workflows/ci.yml/badge.svg)](https://github.com/longbridge/rust-i18n/actions/workflows/ci.yml) [![Docs](https://docs.rs/rust-i18n/badge.svg)](https://docs.rs/rust-i18n/) [![Crates.io](https://img.shields.io/crates/v/rust-i18n.svg)](https://crates.io/crates/rust-i18n)\n\n\u003e 🎯 Let's make I18n things to easy!\n\nRust I18n is a crate for loading localized text from a set of (YAML, JSON or TOML) mapping files. The mappings are converted into data readable by Rust programs at compile time, and then localized text can be loaded by simply calling the provided [`t!`] macro.\n\nUnlike other I18n libraries, Rust I18n's goal is to provide a simple and easy-to-use API.\n\nThe API of this crate is inspired by [ruby-i18n](https://github.com/ruby-i18n/i18n) and [Rails I18n](https://guides.rubyonrails.org/i18n.html).\n\n## Features\n\n- Codegen on compile time for includes translations into binary.\n- Global [`t!`] macro for loading localized text in everywhere.\n- Use YAML (default), JSON or TOML format for mapping localized text, and support mutiple files merging.\n- `cargo i18n` Command line tool for checking and extract untranslated texts into YAML files.\n- Support all localized texts in one file, or split into difference files by locale.\n- Supports specifying a chain of fallback locales for missing translations.\n- Supports automatic lookup of language territory for fallback locale. For instance, if `zh-CN` is not available, it will fallback to `zh`. (Since v2.4.0)\n- Support short hashed keys for optimize memory usage and lookup speed. (Since v3.1.0)\n- Support format variables in [`t!`], and support format variables with [`std::fmt`](https://doc.rust-lang.org/std/fmt/) syntax. (Since v3.1.0)\n- Support for log missing translations at the warning level with `log-miss-tr` feature, the feature requires the `log` crate. (Since v3.1.0)\n\n## Usage\n\nAdd crate dependencies in your Cargo.toml and setup I18n config:\n\n```toml\n[dependencies]\nrust-i18n = \"3\"\n```\n\nLoad macro and init translations in `lib.rs` or `main.rs`:\n\n```rust,compile_fail,no_run\n// Load I18n macro, for allow you use `t!` macro in anywhere.\n#[macro_use]\nextern crate rust_i18n;\n\n// Init translations for current crate.\n// This will load Configuration using the `[package.metadata.i18n]` section in `Cargo.toml` if exists.\n// Or you can pass arguments by `i18n!` to override it.\ni18n!(\"locales\");\n\n// Config fallback missing translations to \"en\" locale.\n// Use `fallback` option to set fallback locale.\n//\ni18n!(\"locales\", fallback = \"en\");\n\n// Or more than one fallback with priority.\n//\ni18n!(\"locales\", fallback = [\"en\", \"es\"]);\n\n// Use a short hashed key as an identifier for long string literals\n// to optimize memory usage and lookup speed.\n// The key generation algorithm is `${Prefix}${Base62(SipHash13(\"msg\"))}`.\ni18n!(\"locales\", minify_key = true);\n//\n// Alternatively, you can customize the key length, prefix,\n// and threshold for the short hashed key.\ni18n!(\"locales\",\n      minify_key = true,\n      minify_key_len = 12,\n      minify_key_prefix = \"t_\",\n      minify_key_thresh = 64\n);\n// Now, if the message length exceeds 64, the `t!` macro will automatically generate\n// a 12-byte short hashed key with a \"t_\" prefix for it, if not, it will use the original.\n\n// If no any argument, use config from Cargo.toml or default.\ni18n!();\n```\n\nOr you can import by use directly:\n\n```rust,no_run\n// You must import in each files when you wants use `t!` macro.\nuse rust_i18n::t;\n\nrust_i18n::i18n!(\"locales\");\n\nfn main() {\n    // Find the translation for the string literal `Hello` using the manually provided key `hello`.\n    println!(\"{}\", t!(\"hello\"));\n\n    // Use `available_locales!` method to get all available locales.\n    println!(\"{:?}\", rust_i18n::available_locales!());\n}\n```\n\n## Locale file\n\nYou can use `_version` key to specify the version (This version is the locale file version, not the rust-i18n version) of the locale file, and the default value is `1`.\n\nrust-i18n supports two style of config file, and those versions will always be keeping.\n\n- `_version: 1` - Split each locale into difference files, it is useful when your project wants to split to translate work.\n- `_version: 2` - Put all localized text into same file, it is easy to translate quickly by AI (e.g.: GitHub Copilot). When you write original text, just press Enter key, then AI will suggest you the translation text for other languages.\n\nYou can choose as you like.\n\n### Split Localized Texts into Difference Files\n\n\u003e \\_version: 1\n\nYou can also split the each language into difference files, and you can choose (YAML, JSON, TOML), for example: `en.json`:\n\n```bash\n.\n├── Cargo.lock\n├── Cargo.toml\n├── locales\n│   ├── zh-CN.yml\n│   ├── en.yml\n└── src\n│   └── main.rs\n```\n\n```yml\n_version: 1\nhello: \"Hello world\"\nmessages.hello: \"Hello, %{name}\"\nt_4Cct6Q289b12SkvF47dXIx: \"Hello, %{name}\"\n```\n\nOr use JSON or TOML format, just rename the file to `en.json` or `en.toml`, and the content is like this:\n\n```json\n{\n  \"_version\": 1,\n  \"hello\": \"Hello world\",\n  \"messages.hello\": \"Hello, %{name}\",\n  \"t_4Cct6Q289b12SkvF47dXIx\": \"Hello, %{name}\"\n}\n```\n\n```toml\nhello = \"Hello world\"\nt_4Cct6Q289b12SkvF47dXIx = \"Hello, %{name}\"\n\n[messages]\nhello = \"Hello, %{name}\"\n```\n\n### All Localized Texts in One File\n\n\u003e \\_version: 2\n\nMake sure all localized files (containing the localized mappings) are located in the `locales/` folder of the project root directory:\n\n```bash\n.\n├── Cargo.lock\n├── Cargo.toml\n├── locales\n│   ├── app.yml\n│   ├── some-module.yml\n└── src\n│   └── main.rs\n└── sub_app\n│   └── locales\n│   │   └── app.yml\n│   └── src\n│   │   └── main.rs\n│   └── Cargo.toml\n```\n\nIn the localized files, specify the localization keys and their corresponding values, for example, in `app.yml`:\n\n```yml\n_version: 2\nhello:\n  en: Hello world\n  zh-CN: 你好世界\nmessages.hello:\n  en: Hello, %{name}\n  zh-CN: 你好，%{name}\n# Generate short hashed keys using `minify_key=true, minify_key_thresh=10`\nt_4Cct6Q289b12SkvF47dXIx:\n  en: Hello, %{name}\n  zh-CN: 你好，%{name}\n```\n\nThis is useful when you use [GitHub Copilot](https://github.com/features/copilot), after you write a first translated text, then Copilot will auto generate other locale's translations for you.\n\n\u003cimg src=\"https://user-images.githubusercontent.com/5518/262332592-7b6cf058-7ef4-4ec7-8dea-0aa3619ce6eb.gif\" width=\"446\" /\u003e\n\n### Get Localized Strings in Rust\n\nImport the [`t!`] macro from this crate into your current scope:\n\n```rust,no_run\nuse rust_i18n::t;\n```\n\nThen, simply use it wherever a localized string is needed:\n\n```rust,no_run\n# macro_rules! t {\n#    ($($all_tokens:tt)*) =\u003e {}\n# }\n# fn main() {\n// use rust_i18n::t;\nt!(\"hello\");\n// =\u003e \"Hello world\"\n\nt!(\"hello\", locale = \"zh-CN\");\n// =\u003e \"你好世界\"\n\nt!(\"messages.hello\", name = \"world\");\n// =\u003e \"Hello, world\"\n\nt!(\"messages.hello\", \"name\" =\u003e \"world\");\n// =\u003e \"Hello, world\"\n\nt!(\"messages.hello\", locale = \"zh-CN\", name = \"Jason\", count = 2);\n// =\u003e \"你好，Jason (2)\"\n\nt!(\"messages.hello\", locale = \"zh-CN\", \"name\" =\u003e \"Jason\", \"count\" =\u003e 3 + 2);\n// =\u003e \"你好，Jason (5)\"\n\nt!(\"Hello, %{name}, you serial number is: %{sn}\", name = \"Jason\", sn = 123 : {:08});\n// =\u003e \"Hello, Jason, you serial number is: 000000123\"\n# }\n```\n\n### Current Locale\n\nYou can use [`rust_i18n::set_locale()`](\u003cset_locale()\u003e) to set the global locale at runtime, so that you don't have to specify the locale on each [`t!`] invocation.\n\n```rust\nrust_i18n::set_locale(\"zh-CN\");\n\nlet locale = rust_i18n::locale();\nassert_eq!(\u0026*locale, \"zh-CN\");\n```\n\n### Extend Backend\n\nSince v2.0.0 rust-i18n support extend backend for cusomize your translation implementation.\n\nFor example, you can use HTTP API for load translations from remote server:\n\n```rust,no_run\n# pub mod reqwest {\n#  pub mod blocking {\n#    pub struct Response;\n#    impl Response {\n#       pub fn text(\u0026self) -\u003e Result\u003cString, Box\u003cdyn std::error::Error\u003e\u003e { todo!() }\n#    }\n#    pub fn get(_url: \u0026str) -\u003e Result\u003cResponse, Box\u003cdyn std::error::Error\u003e\u003e { todo!() }\n#  }\n# }\n# use std::collections::HashMap;\n# use std::borrow::Cow;\nuse rust_i18n::Backend;\n\npub struct RemoteI18n {\n    trs: HashMap\u003cString, HashMap\u003cString, String\u003e\u003e,\n}\n\nimpl RemoteI18n {\n    fn new() -\u003e Self {\n        // fetch translations from remote URL\n        let response = reqwest::blocking::get(\"https://your-host.com/assets/locales.yml\").unwrap();\n        let trs = serde_yaml::from_str::\u003cHashMap\u003cString, HashMap\u003cString, String\u003e\u003e\u003e(\u0026response.text().unwrap()).unwrap();\n\n        return Self {\n            trs\n        };\n    }\n}\n\nimpl Backend for RemoteI18n {\n    fn available_locales(\u0026self) -\u003e Vec\u003cCow\u003c'_, str\u003e\u003e {\n        return self.trs.keys().map(|k| Cow::from(k.as_str())).collect();\n    }\n\n    fn translate(\u0026self, locale: \u0026str, key: \u0026str) -\u003e Option\u003cCow\u003c'_, str\u003e\u003e {\n        // Write your own lookup logic here.\n        // For example load from database\n        return self.trs.get(locale)?.get(key).map(|k| Cow::from(k.as_str()));\n    }\n\n    fn messages_for_locale(\u0026self, locale: \u0026str) -\u003e Option\u003cVec\u003c(Cow\u003c'_, str\u003e, Cow\u003c'_, str\u003e)\u003e\u003e {\n        None\n    }\n}\n```\n\nNow you can init rust_i18n by extend your own backend:\n\n```rust,no_run\n# use std::borrow::Cow;\n# struct RemoteI18n;\n# impl RemoteI18n {\n#   fn new() -\u003e Self { todo!() }\n# }\n# impl rust_i18n::Backend for RemoteI18n {\n#   fn available_locales(\u0026self) -\u003e Vec\u003cstd::borrow::Cow\u003c'_, str\u003e\u003e { todo!() }\n#   fn translate(\u0026self, locale: \u0026str, key: \u0026str) -\u003e Option\u003cstd::borrow::Cow\u003c'_, str\u003e\u003e { todo!() }\n    fn messages_for_locale(\u0026self, locale: \u0026str) -\u003e Option\u003cVec\u003c(Cow\u003c'_, str\u003e, Cow\u003c'_, str\u003e)\u003e\u003e { todo!() }\n# }\nrust_i18n::i18n!(\"locales\", backend = RemoteI18n::new());\n```\n\nThis also will load local translates from ./locales path, but your own `RemoteI18n` will priority than it.\n\nNow you call [`t!`] will lookup translates from your own backend first, if not found, will lookup from local files.\n\n## Example\n\nA minimal example of using rust-i18n can be found [here](https://github.com/longbridge/rust-i18n/tree/main/examples).\n\n## I18n Ally\n\nI18n Ally is a VS Code extension for helping you translate your Rust project.\n\nYou can add [i18n-ally-custom-framework.yml](https://github.com/longbridge/rust-i18n/blob/main/.vscode/i18n-ally-custom-framework.yml) to your project `.vscode` directory, and then use I18n Ally can parse `t!` marco to show translate text in VS Code editor.\n\n## Extractor\n\n\u003e **Experimental**\n\nWe provided a `cargo i18n` command line tool for help you extract the untranslated texts from the source code and then write into YAML file.\n\n\u003e In current only output YAML, and use `_version: 2` format.\n\nYou can install it via `cargo install rust-i18n-cli`, then you get `cargo i18n` command.\n\n```bash\n$ cargo install rust-i18n-cli\n```\n\n### Extractor Config\n\n💡 NOTE: `package.metadata.i18n` config section in Cargo.toml is just work for `cargo i18n` command, if you don't use that, you don't need this config.\n\n```toml\n[package.metadata.i18n]\n# The available locales for your application, default: [\"en\"].\n# available-locales = [\"en\", \"zh-CN\"]\n\n# The default locale, default: \"en\".\n# default-locale = \"en\"\n\n# Path for your translations YAML file, default: \"locales\".\n# This config for let `cargo i18n` command line tool know where to find your translations.\n# You must keep this path same as the one you pass to method `rust_i18n::i18n!`.\n# load-path = \"locales\"\n```\n\nRust I18n providered a `i18n` bin for help you extract the untranslated texts from the source code and then write into YAML file.\n\n```bash\n$ cargo install rust-i18n-cli\n# Now you have `cargo i18n` command\n```\n\nAfter that the untranslated texts will be extracted and saved into `locales/TODO.en.yml` file.\n\nYou also can special the locale by use `--locale` option:\n\n```bash\n$ cd your_project_root_directory\n$ cargo i18n\n\nChecking [en] and generating untranslated texts...\nFound 1 new texts need to translate.\n----------------------------------------\nWriting to TODO.en.yml\n\nChecking [fr] and generating untranslated texts...\nFound 11 new texts need to translate.\n----------------------------------------\nWriting to TODO.fr.yml\n\nChecking [zh-CN] and generating untranslated texts...\nAll thing done.\n\nChecking [zh-HK] and generating untranslated texts...\nFound 11 new texts need to translate.\n----------------------------------------\nWriting to TODO.zh-HK.yml\n```\n\nRun `cargo i18n -h` to see details.\n\n```bash\n$ cargo i18n -h\ncargo-i18n 3.1.0\n---------------------------------------\nRust I18n command to help you extract all untranslated texts from source code.\n\nIt will iterate all Rust files in the source directory and extract all untranslated texts that used `t!` macro. Then it will generate a YAML file and merge with the existing translations.\n\nhttps://github.com/longbridge/rust-i18n\n\nUsage: cargo i18n [OPTIONS] [-- \u003cSOURCE\u003e]\n\nArguments:\n  [SOURCE]\n          Extract all untranslated I18n texts from source code\n\n          [default: ./]\n\nOptions:\n  -t, --translate \u003cTEXT\u003e...\n          Manually add a translation to the localization file.\n\n          This is useful for non-literal values in the `t!` macro.\n\n          For example, if you have `t!(format!(\"Hello, {}!\", \"world\"))` in your code,\n          you can add a translation for it using `-t \"Hello, world!\"`,\n          or provide a translated message using `-t \"Hello, world! =\u003e Hola, world!\"`.\n\n          NOTE: The whitespace before and after the key and value will be trimmed.\n\n  -h, --help\n          Print help (see a summary with '-h')\n\n  -V, --version\n          Print version\n```\n\n## Debugging the Codegen Process\n\nThe `RUST_I18N_DEBUG` environment variable can be used to print out some debugging infos when code is being generated at compile time.\n\n```bash\n$ RUST_I18N_DEBUG=1 cargo build\n```\n\n## Benchmark\n\nBenchmark [`t!`] method, result on MacBook Pro (2023, Apple M3):\n\n```bash\nt                       time:   [32.637 ns 33.139 ns 33.613 ns]\nt_with_locale           time:   [24.616 ns 24.812 ns 25.071 ns]\nt_with_args             time:   [128.70 ns 128.97 ns 129.24 ns]\nt_with_args (str)       time:   [129.48 ns 130.08 ns 130.76 ns]\nt_with_args (many)      time:   [370.28 ns 374.46 ns 380.56 ns]\nt_with_threads          time:   [38.619 ns 39.506 ns 40.419 ns]\nt_lorem_ipsum           time:   [33.867 ns 34.286 ns 34.751 ns]\n```\n\nThe result `101 ns (0.0001 ms)` means if there have **10K** translate texts, it will cost `1ms`.\n\n## Use Cases\n\n- [longbridge-terminal](https://github.com/longbridge/longbridge-terminal)\n- [topgrade](https://github.com/topgrade-rs/topgrade)\n- [trippy](https://github.com/fujiapple852/trippy)\n- [hyperswitch](https://github.com/juspay/hyperswitch)\n- [MirrorX](https://github.com/MirrorX-Desktop/MirrorX)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flongbridge%2Frust-i18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flongbridge%2Frust-i18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flongbridge%2Frust-i18n/lists"}