-
-
Notifications
You must be signed in to change notification settings - Fork 32
Localization base #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Localization base #272
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,7 @@ ctrlc = "3" | |
| bevy_simple_subsecond_system = { version = "0.2", optional = true } | ||
| winit.workspace = true | ||
| image.workspace = true | ||
| jackdaw_localization = { version = "0.4.1", path = "crates/jackdaw_localization" } | ||
|
|
||
| [workspace.dependencies] | ||
| bevy = { version = "0.18", features = [ | ||
|
|
@@ -156,6 +157,7 @@ jackdaw_remote = { version = "0.4.1", path = "crates/jackdaw_remote" } | |
| jackdaw_node_graph = { version = "0.4.1", path = "crates/jackdaw_node_graph" } | ||
| jackdaw_animation = { version = "0.4.1", path = "crates/jackdaw_animation" } | ||
| jackdaw_fuzzy = { version = "0.4.1", path = "crates/jackdaw_fuzzy" } | ||
| jackdaw_localization = { version = "0.4.1", path = "crates/jackdaw_localization" } | ||
| noise = "0.9" | ||
| rand = "0.9" | ||
| serde = "1" | ||
|
|
@@ -195,6 +197,10 @@ tracing = "0.1" | |
| tracing-subscriber = "0.3" | ||
| winit = "0.30.13" | ||
| image = { version = "0.25.10", default-features = false, features = ["png"] } | ||
| fluent_content = "0.0.5" | ||
| bevy_fluent = { git = "https://github.com/ThierryBerger/bevy_fluent.git", branch = "asset_path_resolve" } | ||
| unic-langid = { version = "0.9", features = ["macros"] } | ||
| sys-locale = "0.2" | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sys-locale is not too maintained, but useful and not much "easy" alternatives to my knowledge |
||
|
|
||
| # Idiomatic Bevy code often triggers these lints, and the CI workflow treats them as errors. | ||
| [workspace.lints.clippy] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| [package] | ||
| name = "jackdaw_localization" | ||
| version = "0.4.1" | ||
| edition = "2024" | ||
| description = "Internal crate for Jackdaw localization" | ||
| license = "MIT OR Apache-2.0" | ||
| repository = "https://github.com/jbuehler23/jackdaw" | ||
|
|
||
| [features] | ||
|
|
||
| [dependencies] | ||
| bevy.workspace = true | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll want to split this in multiple crates, but that's what @janhohenheim is exploring on another PR, last to be merged gets to update :') |
||
| fluent_content.workspace = true | ||
| bevy_fluent.workspace = true | ||
| unic-langid.workspace = true | ||
| sys-locale.workspace = true | ||
|
|
||
| [lints] | ||
| workspace = true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| //! Localization for jackdaw Editor. | ||
|
|
||
| use bevy::asset::{LoadedFolder, embedded_asset}; | ||
| use bevy::prelude::*; | ||
| use bevy_fluent::prelude::*; | ||
| use fluent_content::Content; | ||
| use unic_langid::langid; | ||
|
|
||
| macro_rules! supported_languages { | ||
| ($($lang:literal),+ $(,)?) => { | ||
| pub const SUPPORTED_LANGUAGES: &[&str] = &[$($lang),+]; | ||
|
|
||
| /// Macro-generated boilerplate to register our locale assets. | ||
| fn embed_locale_assets(app: &mut App) { | ||
| $( | ||
| embedded_asset!(app, concat!("locales/", $lang, "/main.ftl")); | ||
| embedded_asset!(app, concat!("locales/", $lang, "/main.ftl.yml")); | ||
| )+ | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| supported_languages!("en-US"); | ||
|
|
||
| pub struct LocalizationPlugin; | ||
|
|
||
| impl Plugin for LocalizationPlugin { | ||
| fn build(&self, app: &mut App) { | ||
| embed_locale_assets(app); | ||
|
|
||
| // TODO: Offer a way for user to customize language + persist to disk. | ||
| let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US")); | ||
| let parsed_locale = locale.parse().unwrap_or(langid!("en-US")); | ||
| app.insert_resource(SelectedLocale { locale }); | ||
| app.insert_resource(Locale::new(parsed_locale).with_default(langid!("en-US"))); | ||
|
|
||
| app.add_plugins(FluentPlugin); | ||
| app.init_resource::<LocaleFolder>(); | ||
| app.init_resource::<Localization>(); | ||
|
|
||
| app.add_systems(PreStartup, load_editor_locales); | ||
| app.add_systems( | ||
| Update, | ||
| ( | ||
| update_used_locale.run_if(resource_changed::<SelectedLocale>), | ||
| rebuild_localization.run_if(localization_needs_rebuild), | ||
| update_all_text.run_if(resource_changed::<Localization>), | ||
| update_changed_text, | ||
| ) | ||
| .chain(), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| #[derive(Resource)] | ||
| pub struct SelectedLocale { | ||
| pub locale: String, | ||
| } | ||
|
|
||
| #[derive(Resource, Default)] | ||
| pub struct LocaleFolder(pub Option<Handle<LoadedFolder>>); | ||
|
|
||
| #[derive(Component, Default, Reflect)] | ||
| #[require(Text)] | ||
| pub struct LocalizedText(pub String); | ||
|
|
||
| impl LocalizedText { | ||
| pub fn new(request: impl Into<String>) -> Self { | ||
| Self(request.into()) | ||
| } | ||
| } | ||
|
|
||
| fn load_editor_locales(asset_server: Res<AssetServer>, mut folder: ResMut<LocaleFolder>) { | ||
| folder.0 = Some(asset_server.load_folder("embedded://jackdaw_localization/locales")); | ||
| } | ||
|
|
||
| /// Parse [`SelectedLocale`] and store it into [`Locale`]. | ||
| pub fn update_used_locale(selected: Res<SelectedLocale>, mut locale: ResMut<Locale>) { | ||
| locale.requested = selected.locale.parse().unwrap_or(langid!("en-US")); | ||
| } | ||
|
|
||
| fn localization_needs_rebuild( | ||
| locale: Res<Locale>, | ||
| mut folder_events: MessageReader<AssetEvent<LoadedFolder>>, | ||
| mut bundle_events: MessageReader<AssetEvent<BundleAsset>>, | ||
| ) -> bool { | ||
| let folder_ready = folder_events | ||
| .read() | ||
| .any(|e| matches!(e, AssetEvent::LoadedWithDependencies { .. })); | ||
| let bundle_ready = bundle_events.read().any(|e| { | ||
| matches!( | ||
| e, | ||
| AssetEvent::LoadedWithDependencies { .. } | AssetEvent::Modified { .. } | ||
| ) | ||
| }); | ||
| locale.is_changed() || folder_ready || bundle_ready | ||
| } | ||
|
|
||
| fn rebuild_localization( | ||
| builder: LocalizationBuilder, | ||
| folder: Res<LocaleFolder>, | ||
| mut localization: ResMut<Localization>, | ||
| ) { | ||
| let Some(handle) = folder.0.as_ref() else { | ||
| return; | ||
| }; | ||
| *localization = builder.build(handle); | ||
| } | ||
|
|
||
| /// Re-resolve every [`LocalizedText`] after the active locale changes. | ||
| fn update_all_text(localization: Res<Localization>, mut q: Query<(&LocalizedText, &mut Text)>) { | ||
| for (loc, mut text) in q.iter_mut() { | ||
| text.0 = localization | ||
| .content(&loc.0) | ||
| .unwrap_or_else(|| loc.0.clone()); | ||
| } | ||
| } | ||
|
|
||
| /// Resolve newly inserted or mutated [`LocalizedText`] entries. | ||
| fn update_changed_text( | ||
| localization: Res<Localization>, | ||
| mut q: Query<(&LocalizedText, &mut Text), Changed<LocalizedText>>, | ||
| ) { | ||
| for (loc, mut text) in q.iter_mut() { | ||
| text.0 = localization | ||
| .content(&loc.0) | ||
| .unwrap_or_else(|| loc.0.clone()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| cancel = Cancel | ||
| submit = Submit | ||
| add-node = Add Node | ||
| add-entity = Add Entity | ||
| add-component = Add Component | ||
| ready = Ready | ||
| no-animation-clip-on-selection = No animation clip on selection. Pick a named entity and create one. | ||
| new-workspace = New Workspace | ||
| preparing-build = Preparing build... | ||
| source-checkout = Source checkout | ||
| read-only = (read-only) | ||
| material-not-loaded = (material not loaded) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Locale files may be in YAML as in this example, or in RON | ||
| locale: en-US | ||
| resources: | ||
| - embedded://jackdaw_localization/locales/en-US/main.ftl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
targeting my git branch, PR is kgv/bevy_fluent#58