-
Notifications
You must be signed in to change notification settings - Fork 36
Description
I have a multi crate project with the following structure:
|- nih
|- data
| |- i18n
| |- en-GB
| |- nih_i18n.ftl <-- Why do I need this file?
| |- nih.ftl
|- nih
| |- i18n.toml <-- Why do I need this file?
| |- ...
|- nih_i18n
| |- i18n.toml <-- Why do I need this file?
| |- ...
|- i18n.toml
|- ...
The contents of my main i18n.toml is:
subcrates = ["nih"]
fallback_language = "en-GB"
[fluent]
assets_dir = "data/i18n"
The docs say that all subcrates will be treated as part of the parent crate, unless they have their own i18n.toml file. If I remove either of these files from the subcrates, I get the following errors depending on which file I remove:
error: proc macro panicked
--> nih_i18n\src\lib.rs:30:44
|
30 | let loader: FluentLanguageLoader = fluent_language_loader!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: fluent_language_loader!() had a problem reading i18n config file "D:\\Dev\\nih\\nih_i18n\\i18n.toml": Cannot read file "D:\\Dev\\nih\\nih_i18n\\i18n.toml" in the current working directory Ok("D:\\Dev\\nih") because The system cannot find the file specified. (os error 2).
or
error: fl!() had a problem reading i18n config file "D:\\Dev\\nih\\nih\\i18n.toml": Cannot read file "D:\\Dev\\nih\\nih\\i18n.toml" in the current working directory Ok("D:\\Dev\\nih") because The system cannot find the file specified. (os error 2).
= help: Try creating the `i18n.toml` configuration file.
--> nih\src\main.rs:28:27
|
28 | window.set_title(&fl!("hello-world"));
| ^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `$crate::i18n_embed_fl::fl` which comes from the expansion of the macro `fl` (in Nightly builds, run with -Z macro-backtrace for more info)
Why do the subcrates need the individual i18n.toml files? I can perhaps understand why it is needed in the nih crate where I have translations, although it is surprising that the global file is not used. I don't understand why it is needed in the nih_i18n crate, where there are not translations and I'm just setting up the language loader, localiser and re-exporting the fl macro as per the example:
use std::sync::OnceLock;
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DefaultLocalizer, DesktopLanguageRequester, I18nEmbedError, LanguageLoader, Localizer,
RustEmbedNotifyAssets,
};
pub use i18n_embed_fl;
use nih_error::Error;
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "../data/i18n/"]
pub struct LocalizationsEmbed;
pub fn get_localisations() -> &'static RustEmbedNotifyAssets<LocalizationsEmbed> {
pub static LOCALIZATIONS: OnceLock<RustEmbedNotifyAssets<LocalizationsEmbed>> = OnceLock::new();
LOCALIZATIONS.get_or_init(|| {
RustEmbedNotifyAssets::new(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("data/i18n/"),
)
})
}
pub fn get_language_loader() -> &'static FluentLanguageLoader {
static LANGUAGE_LOADER: OnceLock<FluentLanguageLoader> = OnceLock::new();
LANGUAGE_LOADER.get_or_init(|| {
let loader: FluentLanguageLoader = fluent_language_loader!();
// Load the fallback langauge by default so that users of the
// library don't need to if they don't care about localization.
loader
.load_fallback_language(get_localisations())
.expect("Error while loading fallback language");
loader
})
}
#[macro_export]
macro_rules! fl {
($message_id:literal) => {{
$crate::i18n_embed_fl::fl!($crate::get_language_loader(), $message_id)
}};
($message_id:literal, $($args:expr),*) => {{
$crate::i18n_embed_fl::fl!($crate::get_language_loader(), $message_id, $($args), *)
}};
}
fn localizer() -> DefaultLocalizer<'static> {
DefaultLocalizer::new(get_language_loader(), get_localisations())
}
pub fn init_i18n() -> Result<(), Error> {
let localiser = localizer()
.with_autoreload()
.expect("failed to enable localisation autoreloader");
let requested_languages = DesktopLanguageRequester::requested_languages();
nih_logging::debug!("{:?}", &requested_languages);
localiser
.select(&requested_languages)
.map_err(failed_to_load_i18n_languages)?;
Ok(())
}
fn failed_to_load_i18n_languages(error: I18nEmbedError) -> Error {
Error::new("failed to load languages for i18n").with_source(error)
}
Then, when this is working with all these extra config files, it selects the wrong translation. Below are my translation files:
nih_i18n.ftl:
hello-world = Hello World Wrong!
nih.ftl:
hello-world = Hello World Right!
And the usage code in nih/src/main.rs:
fn create() -> Result<Self, Error> {
let window = Context::get_window();
window.set_title(&fl!("hello-world"));
Ok(Nih {})
}
But as you can see, it does not use the correct translation:
What I was expecting:
- a single
i18n.tomlfile at the project root. - not having to provide
nih_i18n.ftlfor a crate where I do no localisation. - for the
fl!invocation in thenihcrate to use the translation specified innih.ftl. - project structure like this:
|- nih
|- data
| |- i18n
| |- en-GB
| |- nih.ftl
|- nih
| |- ...
|- nih_i18n
| |- ...
|- i18n.toml
|- ...
