cargo-i18n

repository·master·Indexed 40 days ago

https://github.com/kellpossible/cargo-i18n

A Cargo sub-command and ecosystem of tools for extracting and building localization resources to embed in Rust applications and libraries. It includes i18n-build for build script integration, i18n-config for shared configuration schemas, and i18n-embed for runtime localization. The ecosystem supports the Fluent and gettext localization systems, providing features like compile-time checked localization via the fl! macro in i18n-embed-fl and platform-specific language detection for desktop and WASM contexts.

Tokens
12.9K
Snippets
41
Records
58
Agent score
100%

What's inside cargo-i18n

  1. Use i18n-build in project build scripts

    master
    The i18n-build crate is a library designed for use within the cargo-i18n tool to facilitate crate localization. While primarily used by the cargo-i18n CLI, it is published independently so that developers can call its functionality directly within their own project build scripts (build.rs) if they need custom localization build logic.
  2. Implement Fluent localization with i18n-embed

    master

    To embed Fluent localization into your application, follow these steps:

    1. Add i18n-embed with fluent-system and a requester feature (e.g., desktop-requester) to your Cargo.toml.
    2. Create an i18n.toml file in your crate root.
    3. Define a struct using #[derive(RustEmbed)] pointing to your assets directory.
    4. Use the fluent_language_loader!() macro to instantiate the loader.
    5. Use i18n_embed::select to resolve the requested languages against your embedded localizations.
    use i18n_embed::{DesktopLanguageRequester, fluent::{
        FluentLanguageLoader, fluent_language_loader
    }};
    use rust_embed::RustEmbed;
    
    #[derive(RustEmbed)]
    #[folder = "i18n"] // path to the compiled localization resources
    struct Localizations;
    
    fn main() {
        // Create the loader using the macro which pulls config from i18n.toml
        let language_loader: FluentLanguageLoader = fluent_language_loader!();
    
        // Resolve the system's requested languages
        let requested_languages = DesktopLanguageRequester::requested_languages();
        
        // Select the appropriate localizations
        let _result = i18n_embed::select(
            &language_loader, &Localizations, &requested_languages);
    
        // continue on with your application
    }
  3. Set up i18n-embed-fl with Fluent

    master

    To use i18n-embed-fl for compile-time checked Fluent localization, you must configure an i18n.toml file in your crate root and organize your Fluent files in a specific directory structure.

    1. Configure i18n.toml

    Create an i18n.toml file in your crate root with the following required keys:

    • fallback_language: The language identifier used as the primary fallback (e.g., "en-GB").
    • [fluent] assets_dir: The path to your localization assets.

    2. Organize Assets

    Your assets directory must follow this structure: assets_dir/{language}/{domain}.ftl

    Note: The {domain} should be the Rust path of your crate, using underscores (_) instead of hyphens (-).

    3. Implementation Example

    Use fluent_language_loader! to initialize the loader and the fl! macro from i18n-embed-fl to retrieve messages with compile-time validation of both the message ID and the arguments.

    # i18n.toml
    fallback_language = "en-GB"
    
    [fluent]
    assets_dir = "i18n"
    use i18n_embed::{
        fluent::{fluent_language_loader, FluentLanguageLoader},
        LanguageLoader,
    };
    use i18n_embed_fl::fl;
    use rust_embed::RustEmbed;
    
    #[derive(RustEmbed)]
    #[folder = "i18n/"]
    struct Localizations;
    
    let loader: FluentLanguageLoader = fluent_language_loader!();
    loader
        .load_languages(&Localizations, &[loader.fallback_language().clone()])
        .unwrap();
    
    // The fl! macro performs compile-time checks for the ID and arguments
    assert_eq!(
        "Hello \u{2068}Bob 23\u{2069}!",
        fl!(loader, "hello-arg", name = format!("Bob {}", 23))
    )
  4. How asset watching and hot-reloading works

    master

    Hot-reloading is achieved through the subscribe_changed method on the I18nAssets trait.

    1. Subscription: You pass a closure (wrapped in std::sync::Arc) to subscribe_changed. This closure is executed whenever a change is detected.
    2. The Watcher: The method returns a Box<dyn Watcher>. You must keep this object in scope. When the Watcher is dropped, the file system subscription is automatically cancelled.
    3. Implementation Support:
      • FileSystemAssets supports this if the autoreload feature is enabled and notify_changes_enabled(true) was called.
      • RustEmbedNotifyAssets supports this if the autoreload feature is enabled.
      • AssetsMultiplexor propagates the subscription to all its children.
  5. Configure i18n-embed features

    master

    The i18n-embed crate provides optional features to support different localization systems and platform-specific language detection:

    • fluent-system: Enables support for the Fluent localization system via the FluentLanguageLoader.
    • gettext-system: Enables support for the gettext localization system using the tr macro and the gettext crate via the GettextLanguageLoader.
    • desktop-requester: Provides DesktopLanguageRequester for desktop platforms (Windows, macOS, Linux) using the sys-locale crate to resolve the system locale.
    • web-sys-requester: Provides WebLanguageRequester for WASM contexts using the web-sys crate to resolve the browser's language.
  6. Understand the LanguageRequester trait

    master

    The LanguageRequester trait is used by I18nAssets to determine which languages are currently being requested by the system or environment. It allows you to register Localizer instances as listeners that will automatically update via their select() method whenever the requested languages change.

    Key capabilities include:

    • Adding Listeners: You can add listeners using add_listener (for Weak<dyn Localizer>, which are automatically removed when the underlying Arc is dropped) or add_listener_ref (for &'a dyn Localizer, which are permanent).
    • Polling: The poll() method checks the current system/environment language and notifies all registered listeners.
    • Overrides: You can use set_language_override to force a specific unic_langid::LanguageIdentifier regardless of the system settings.
    • Introspection: You can query requested_languages(), available_languages(), and current_languages() to see what the system wants, what is available, and what is currently loaded across different domains.
  7. Enable runtime localization with the `localize` feature

    master

    By default, i18n-build is focused on the build process. If you need to enable runtime localization using the localize() function via the i18n-embed crate, you must enable the localize Cargo feature in your Cargo.toml.

    [dependencies]
    i18n-build = { version = "0.2.13", features = ["localize"] }
  8. Configure i18n.toml for Fluent localization

    master

    To use the Fluent localization system, create an i18n.toml file in your crate root. This file is used by cargo-i18n and the fluent_language_loader!() macro to configure the localization environment at compile time.

    Required keys:

    • fallback_language: The Unicode language identifier used as the primary fallback (all strings must be present in this language).
    • [fluent].assets_dir: The path to your localization assets. The directory structure must follow the pattern: assets_dir/{language}/{domain}.ftl.
    # (Required) The primary fallback language
    fallback_language = "en-GB"
    
    [fluent]
    # (Required) The path to the assets directory
    assets_dir = "i18n"
  9. Gettext directory paths

    master

    When configuring Gettext in i18n.toml, you can optionally specify custom directories for .pot, .po, and .mo files. If these are not explicitly provided, the following defaults are used relative to your output_dir:

    • POT directory: [output_dir]/pot
    • PO directory: [output_dir]/po
    • MO directory: [output_dir]/mo
  10. Use WebLanguageRequester for web applications

    master

    For web-based applications, use WebLanguageRequester. It retrieves the requested languages from the browser's navigator.languages API.

    Note: This requires the web-sys-requester crate feature to be enabled.

    Similar to the desktop version, you register listeners and call poll() to update your localizers based on the browser's language settings.

    use i18n_embed::LanguageRequester;
    use i18n_embed::WebLanguageRequester;
    use std::sync::Arc;
    
    let mut requester = WebLanguageRequester::new();
    requester.add_listener(Arc::downgrade(&localizer));
    
    // Sync with browser locale
    requester.poll().unwrap();