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-onlyfeature 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):
- Exact count:
"0","1","2","5", etc. - ICU Categories:
"zero","one","two","few","many" - Basic fallback:
"one"vs"other" - 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:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
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-onlyto 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:
- Exact counts:
"0","1","2", etc. - ICU categories:
"zero","one","two","few","many" - Basic fallback:
"one"vs"other"
Perfect for complex languages like Polish, Russian, and Arabic.
StructsΒ§
- I18n
- Main resource for accessing translations in Bevy systems.
- I18n
Config - Configuration for the I18n plugin.
- I18n
Partial - Represents translations for a single file.
- I18n
Plugin - Main plugin for Bevy internationalization.
- Translations
- Contains all translations loaded from filesystem or bundled data.
EnumsΒ§
- Section
Value - Represents a value in a translation file.
TraitsΒ§
- Language
AppExt - Extension trait for
Appto easily manage languages.