Install
$ agentstack add skill-chrisgliddon-bevy-skills-bevy-fluent ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Bevy 0.18 — Localization (es-fluent)
When to use this skill
- Adding Fluent-based i18n to a Bevy app (
es-fluent-manager-bevy = "0.18"). - Defining typed UI messages with
#[derive(EsFluent)]and#[derive(BevyFluentText)]. - Wrapping a UI text entity with
FluentTextfor 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.18"
es-fluent = { version = "0.15", features = ["derive"] }
es-fluent-manager-bevy = { version = "0.18", 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, Component)]
#[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 writes translations into a sibling Text component.
commands.spawn((FluentText::new(UiMessage::StartGame), Text::new("")));
}
pub fn switch_locale_on_keypress(
keys: Res>,
requested: Res,
mut locale_events: MessageWriter,
) {
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](references/lib-target-layout.md).
Topics
| Topic | Reference | |-------|-----------| | Why types must live in the lib target; cargo es-fluent generate invisibility footgun; full layout | [references/lib-target-layout.md](references/lib-target-layout.md) | | BevyFluentText derive vs FluentText component; Component bound; Text::new("") | [references/components.md](references/components.md) | | LocaleChangeEvent (request) vs LocaleChangedEvent (confirmation); RequestedLanguageId; Messages vs Events | [references/locale-events.md](references/locale-events.md) | | i18n.toml full schema, assets_dir, I18nPluginConfig runtime override | [references/i18n-toml.md](references/i18n-toml.md) | | generate, watch, check, clean, sync, tree, format — dev and CI workflows | [references/cli.md](references/cli.md) | | rustc 1.95+ requirement; rust-toolchain.toml pin; failure modes on older toolchains | [references/toolchain.md](references/toolchain.md) |
Gotchas
cargo es-fluent generateonly 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](references/lib-target-layout.md).
BevyFluentTextis a derive macro;FluentTextis the component. The
derive registers refresh systems via inventory. The component is what you spawn on UI entities. Your message enum T must also derive Component because FluentTextRegistration::register_fluent_text requires T: ToFluentString + Clone + Component + Send + Sync + 'static. Missing Component gives a confusing trait-bound error. See [references/components.md](references/components.md).
- Minimum rustc 1.95. Older toolchains fail with cryptic trait-resolution
errors that do not mention the version requirement. Pin with rust-toolchain.toml ([toolchain] / channel = "1.95"). See [references/toolchain.md](references/toolchain.md).
- Events are Messages. Use
MessageWriterand
MessageReader — not EventWriter/EventReader (renamed in Bevy 0.17). See [references/locale-events.md](references/locale-events.md).
See also
bevy-ui—FluentTextis used alongsideText,Node, andButton.bevy-ecs-components—#[derive(Component)]patterns required byFluentText.bevy-assets— how Bevy'sAssetServerloads.ftlfiles; relevant for hot-reload.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: chrisgliddon
- Source: chrisgliddon/bevy-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.