mcpField
B
bevy-fluent

bevy-fluent

github.com/chrisgliddonDevelopment2678 installsUpdated 9w ago
Repository

About bevy-fluent

bevy-fluent is a skill for adding Fluent-based localization to Bevy 0.19 applications. It guides developers through typed UI messages, locale-aware text refresh, runtime language switching, and the required project layout.

Key features

  1. 01Fluent-based Bevy localization setup
  2. 02Typed UI message definitions
  3. 03Locale-driven UI text refresh
  4. 04Runtime locale switching
  5. 05CLI workflows for translation files
  6. 06Toolchain and project layout guidance

Use cases

  1. 01Add localization to a Bevy application
  2. 02Refresh UI text when the locale changes
  3. 03Generate and validate translation files

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/chrisgliddon/bevy-skills --skill bevy-fluent

After installing, type/bevy-fluentin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
cli.md
components.md
i18n-toml.md
lib-target-layout.md
locale-events.md
toolchain.md

Details

From the project README

Bevy 0.19 — Localization (es-fluent)

When to use this skill

  • Adding Fluent-based i18n to a Bevy app (es-fluent-manager-bevy = "0.19.2").
  • Defining typed UI messages with #[derive(EsFluent)] and #[derive(BevyFluentText)].
  • Wrapping a UI text entity with FluentText<T> for automatic locale-driven refresh.
  • Switching locales at runtime via LocaleChangeEvent.
  • Reading the current locale from RequestedLanguageId.

Canonical pattern — 5-file minimum shape

Cargo.toml:

[dependencies]
bevy                   = "0.19"
es-fluent              = { version = "0.18.1", features = ["derive"] }
es-fluent-manager-bevy = { version = "0.19.2", features = ["macros"] }
unic-langid            = "0.9"

i18n.toml (crate root, read at compile time):

fallback_language = "en"
assets_dir = "assets/locales"

src/i18n.rs:

es_fluent_manager_bevy::define_i18n_module!();

src/lib.rs — message types must live here (see Gotchas):

use bevy::prelude::*;
use es_fluent::EsFluent;
use es_fluent_manager_bevy::{
    BevyFluentText, FluentText, I18nPlugin, LocaleChangeEvent, RequestedLanguageId,
};
use unic_langid::langid;

pub mod i18n;

#[derive(BevyFluentText, Clone, EsFluent)]
#[fluent(namespace = "ui")]
pub enum UiMessage { StartGame, Settings, QuitGame }

pub fn build_i18n_plugin() -> I18nPlugin {
    I18nPlugin::with_language(langid!("en"))
}

pub fn setup_ui(mut commands: Commands) {
    commands.spawn(Camera2d);
    // FluentText<T> writes translations into a sibling Text component.
    commands.spawn((FluentText::new(UiMessage::StartGame), Text::new("")));
}

pub fn switch_locale_on_keypress(
    keys: Res<ButtonInput<KeyCode>>,
    requested: Res<RequestedLanguageId>,
    mut locale_events: MessageWriter<LocaleChangeEvent>,
) {
    if keys.just_pressed(KeyCode::KeyL) {
        let next = if requested.0.to_string() == "en" { langid!("fr") } else { langid!("en") };
        locale_events.write(LocaleChangeEvent(next));
    }
}

src/main.rs — thin binary entry point:

use bevy::prelude::*;
use my_game::{build_i18n_plugin, setup_ui, switch_locale_on_keypress};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(build_i18n_plugin())
        .add_systems(Startup, setup_ui)
        .add_systems(Update, switch_locale_on_keypress)
        .run();
}

Replace my_game with your package name (hyphens become underscores). The full walkthrough — including the lib-vs-binary rationale, package-name placeholder, and pure-binary-crate workarounds — is in references/lib-target-layout.md.

Topics

TopicReference
Why types must live in the lib target; cargo es-fluent generate invisibility footgun; full layoutreferences/lib-target-layout.md
BevyFluentText derive vs FluentText<T> component; Text::new("")references/components.md
LocaleChangeEvent (request) vs LocaleChangedEvent (confirmation); RequestedLanguageId; Messages vs Eventsreferences/locale-events.md
i18n.toml full schema, assets_dir, I18nPluginConfig runtime overridereferences/i18n-toml.md
generate, watch, check, clean, sync, tree, format — dev and CI workflowsreferences/cli.md
rustc 1.96+ requirement; rust-toolchain.toml pin; failure modes on older toolchainsreferences/toolchain.md

Gotchas

  1. cargo es-fluent generate only sees the lib target. Message enums declared only in src/main.rs are invisible to the CLI and produce empty .ftl output with no error. Move every BevyFluentText / EsFluent-derived type to src/lib.rs (or a module reachable from it). See references/lib-target-layout.md.

  2. BevyFluentText is a derive macro; FluentText<T> is the component. The derive registers refresh systems via inventory. The component is what you spawn on UI entities. In 0.19.2 the message type needs FluentMessage + Clone + Send + Sync + 'static; it does not derive Component itself. See references/components.md.

  3. Minimum rustc 1.96. Older toolchains fail with cryptic trait-resolution errors that do not mention the version requirement. Pin with rust-toolchain.toml ([toolchain] / channel = "1.96"). See references/toolchain.md.

  4. Events are Messages. Use MessageWriter<LocaleChangeEvent> and MessageReader<LocaleChangedEvent> — not EventWriter/EventReader (renamed in Bevy 0.17). See references/locale-events.md.

See also

  • bevy-ui — FluentText<T> is used alongside Text, Node, and Button.
  • bevy-ecs-components — #[derive(Component)] patterns required by FluentText<T>.
  • bevy-assets — how Bevy's AssetServer loads .ftl files; relevant for hot-reload.

Comments

Markdown supported