Crate bevy_intl

Crate bevy_intl 

Source
Expand description

Β§bevy-intl Plugin

A simple internationalization (i18n) plugin for Bevy to manage translations from JSON files. Supports fallback languages, placeholders, plurals, gendered translations, and full WASM compatibility with bundled translations.


Β§Features

  • 🌐 WASM Compatible: Automatically bundles translations for web deployment
  • πŸ“ Flexible Loading: Load from filesystem (desktop) or bundled files (WASM)
  • πŸ”§ Feature Flag: bundle-only feature to force bundled translations on any platform
  • πŸ—‚οΈ JSON Organization: Load translations from JSON files organized per language
  • πŸ”„ Translation Support:
    • Basic translation
    • Placeholders/arguments
    • Plural forms
    • Gendered text
  • πŸ›‘οΈ Fallback Language: Automatic fallback when translations are missing
  • ⚑ Bevy Integration: Native Bevy plugin with resource system integration

Β§πŸš€ Quick Setup

Add to your Cargo.toml:

[dependencies]
bevy = "0.16"
bevy-intl = "0.2.1"

# Optional: Force bundled translations on all platforms
# bevy-intl = { version = "0.2.0", features = ["bundle-only"] }

Initialize the plugin in your Bevy app:

use bevy::prelude::*;
use bevy_intl::{I18nPlugin, I18nConfig};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Default setup - auto-detects WASM vs desktop
        .add_plugins(I18nPlugin::default())
        .add_systems(Startup, setup_ui)
        .run();
}

// Or with custom configuration
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(I18nPlugin::with_config(I18nConfig {
            use_bundled_translations: false, // Force filesystem loading  This gets ignored when bundle-only feature is enabled
            messages_folder: "locales".to_string(), // Custom folder
            default_lang: "fr".to_string(),
            fallback_lang: "en".to_string(),
        }))
        .add_systems(Startup, setup_ui)
        .run();
}

Β§πŸ“ Folder Structure

messages/
β”œβ”€β”€ en/
β”‚   β”œβ”€β”€ test.json
β”‚   └── another_file.json
β”œβ”€β”€ fr/
β”‚   β”œβ”€β”€ test.json
β”‚   └── another_file.json
└── es/
    β”œβ”€β”€ test.json
    └── another_file.json
assets/
src/

§🌐 WASM & Platform Behavior

Desktop/Native:

  • Loads translations from messages/ folder at runtime
  • Hot-reloadable during development
  • File system access required

WASM/Web:

  • Automatically uses bundled translations (compiled at build time)
  • No file system access needed

Force Bundled Mode:

bevy-intl = { version = "0.2.1", features = ["bundle-only"] }

This forces bundled translations on all platforms


Β§πŸ“„ JSON Format

Each JSON file can contain either simple strings or nested maps for plurals/genders:

{
    "greeting": "Hello",
    "farewell": {
        "male": "Goodbye, sir",
        "female": "Goodbye, ma'am"
    },
    "apples": {
        "zero": "No apples",
        "one": "One apple",
        "two": "A couple of apples",
        "few": "A few apples",
        "many": "{{count}} apples",
        "other": "{{count}} apples"
    },
    "items": {
        "0": "No items",
        "1": "One item",
        "2": "Two items",
        "5": "Exactly five items",
        "other": "{{count}} items"
    }
}

Β§Plural Key Priority (most specific to least):

  1. Exact count: "0", "1", "2", "5", etc.
  2. ICU Categories: "zero", "one", "two", "few", "many"
  3. Basic fallback: "one" vs "other"
  4. Legacy: "many" as last resort

This supports complex plural rules for languages like:

  • English: one, other
  • French: one, many
  • Polish: one, few, many
  • Russian: one, few, many
  • Arabic: zero, one, two, few, many

Β§πŸ”§ API Usage

Β§Accessing translations in systems
use bevy::prelude::*;
use bevy_intl::{I18n, LanguageAppExt};

fn translation_system(i18n: Res<I18n>) {
    // Load a translation file
    let text = i18n.translation("test");

    // Basic translation
    let greeting = text.t("greeting");

    // Translation with arguments
    let apple_count = text.t_with_arg("apples", &[&5]);

    // Plural translation
    let plural_text = text.t_with_plural("apples", 5);

    // Gendered translation
    let farewell = text.t_with_gender("farewell", "female");

    // Gendered translation with arguments
    let farewell_with_name = text.t_with_gender_and_arg("farewell", "male", &[&"John"]);
}
Β§Changing language
// Method 1: Using App extension trait
fn setup_language(mut app: ResMut<App>) {
    app.set_lang_i18n("fr");         // Set current language
    app.set_fallback_lang("en");     // Set fallback language
}

// Method 2: Direct resource access
fn change_language_system(mut i18n: ResMut<I18n>) {
    i18n.set_lang("en");     // Set current language
    let current = i18n.get_lang(); // Get current language
    let available = i18n.available_languages(); // Get all available languages
}

Β§πŸ’‘ Complete Example

use bevy::prelude::*;
use bevy_intl::{I18nPlugin, I18n, LanguageAppExt};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(I18nPlugin::default())
        .add_systems(Startup, (setup_ui, setup_language))
        .add_systems(Update, language_switcher)
        .run();
}

fn setup_language(mut app: ResMut<App>) {
    app.set_lang_i18n("en");
    app.set_fallback_lang("en");
}

fn setup_ui(mut commands: Commands, i18n: Res<I18n>) {
    let text = i18n.translation("ui");

    commands.spawn((
        Text::new(text.t("welcome_message")),
        Node {
            position_type: PositionType::Absolute,
            bottom: Val::Px(5.0),
            right: Val::Px(5.0),
            ..default()
        },
    ));
}

fn language_switcher(
    input: Res<ButtonInput<KeyCode>>,
    mut i18n: ResMut<I18n>
) {
    if input.just_pressed(KeyCode::F1) {
        i18n.set_lang("en");
    }
    if input.just_pressed(KeyCode::F2) {
        i18n.set_lang("fr");
    }
}

Β§Debugging

  • Missing translation files or invalid locales are warned in the console.

  • If a translation is missing, the fallback language will be used, or an β€œError missing text” placeholder is returned.


Β§License

This crate is licensed under either of the following, at your option:

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, shall be dual licensed as above, without any additional terms or conditions.

Β§bevy-intl

A comprehensive internationalization (i18n) plugin for Bevy that provides:

  • WASM Compatible: Automatic translation bundling for web deployment
  • Flexible Loading: Filesystem (desktop) or bundled files (WASM)
  • Feature Flag: bundle-only to force bundled translations on any platform
  • Advanced Plurals: Support for complex plural rules (ICU-compliant)
  • Gender Support: Gendered translations
  • Placeholders: Dynamic text replacement
  • Fallback System: Automatic fallback to default language

Β§Quick Start

use bevy::prelude::*;
use bevy_intl::I18nPlugin;
 
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(I18nPlugin::default())
        .add_systems(Startup, setup_ui)
        .run();
}
 
fn setup_ui(mut commands: Commands, i18n: Res<bevy_intl::I18n>) {
    let text = i18n.translation("ui");
     
    commands.spawn((
        Text::new(text.t("welcome")),
        Node::default(),
    ));
}

Β§Features

Β§Translation Loading

  • Desktop: Loads from messages/ folder at runtime
  • WASM: Uses bundled translations (compiled at build time)
  • Bundle-only: Force bundled mode with features = ["bundle-only"]

Β§Advanced Plural Support

Supports multiple plural forms with fallback priority:

  1. Exact counts: "0", "1", "2", etc.
  2. ICU categories: "zero", "one", "two", "few", "many"
  3. Basic fallback: "one" vs "other"

Perfect for complex languages like Polish, Russian, and Arabic.

StructsΒ§

I18n
Main resource for accessing translations in Bevy systems.
I18nConfig
Configuration for the I18n plugin.
I18nPartial
Represents translations for a single file.
I18nPlugin
Main plugin for Bevy internationalization.
Translations
Contains all translations loaded from filesystem or bundled data.

EnumsΒ§

SectionValue
Represents a value in a translation file.

TraitsΒ§

LanguageAppExt
Extension trait for App to easily manage languages.