13 releases
0.3.1 | Jul 1, 2024 |
---|---|
0.3.0 | Jun 12, 2024 |
0.2.8 | Mar 28, 2024 |
0.2.6 | Feb 22, 2024 |
0.1.1 | Jan 2, 2024 |
#51 in Internationalization (i18n)
170 downloads per month
36KB
716 lines
About this Crate
This crate offers some localization tools to be used with Axum.
Basic usage
You can use this crate to extract a language identifier (ex en-US
) from a request.
If the mode is set to RedirectMode::NoRedirect
, the Accept-Language header is used to find the users preferred language. If the mode is set to either RedirectMode::RedirectToFullLocaleSubPath
or RedirectMode::RedirectToLanguageSubPath
, the user will be redirected to a sub-path based on their Accept-Language headers, to to the default language if not supported.
use unic_langid::{langid, LanguageIdentifier};
use axum::Extension;
pub const ENGLISH: LanguageIdentifier = langid!("en");
pub const JAPANESE: LanguageIdentifier = langid!("ja");
let router = axum::Router::new()
.route("/lists", get(|Extension(lang): Extension<LanguageIdentifier>|
async move {
Html(format!("Your language is: {}", lang.to_string()))
}))
.layer(axum_l10n::LanguageIdentifierExtractorLayer::new(
ENGLISH,
vec![ENGLISH, JAPANESE],
axum_l10n::RedirectMode::NoRedirect,
));
For RedirectMode::RedirectToFullLocaleSubPath
or RedirectMode::RedirectToLanguageSubPath
, you must wrap this service/middleware around the entire
axum app, as explained here.
When using the subpath redirect modes, you may want to exclude some folders from the redirect as below:
let l10n_middleware = axum_l10n::LanguageIdentifierExtractorLayer::new(
JAPANESE,
vec![JAPANESE, ENGLISH],
axum_l10n::RedirectMode::RedirectToLanguageSubPath,
)
.excluded_paths(&["/api", "/assets", "/auth"]);
Features
fluent
Enabling fluent allows you to use the fluent Localizer to add bundles for translation.
See fluent-rs for details about fluent and rust.
Usage
use unic_langid::{langid, LanguageIdentifier};
use axum_l10n::Localizer;
pub const ENGLISH: LanguageIdentifier = langid!("en");
pub const JAPANESE: LanguageIdentifier = langid!("ja");
let mut localizer = Localizer::new();
localizer
.add_bundle(JAPANESE, &["locales/ja/main.ftl", "locales/ja/login.ftl"])
.unwrap();
localizer
.add_bundle(ENGLISH, &["locales/en/main.ftl", "locales/en/login.ftl"])
.unwrap();
let message = localizer.format_message(&ENGLISH, "test-key-a", None);
assert_eq!(Some(String::from("Hello World")), message);
tera
Enabling the tera feature allows you to use the fluent translations inside tera templates.
See tera for more information on tera.
Usage
Initialization:
use tera::Tera;
let mut tera = Tera::new("src/views/templates/**/*").expect("tera parsing error");
let mut localizer = Localizer::new();
localizer
.add_bundle(ENGLISH, &["locales/en/main.ftl", "locales/en/login.ftl"])
.unwrap();
tera.register_function("fluent", localizer);
Axum handler:
#[derive(Clone)]
struct ViewRouterState {
pool: mysql_async::Pool,
tera: Arc<tera::Tera>,
}
async fn lists_view(
State(state): State<ViewRouterState>,
Extension(lang): Extension<LanguageIdentifier>,
) -> axum::response::Response {
let lists: Vec<String> = List::paginate(&state.pool, claim.sub)
.await.unwrap();
let mut ctx = Context::new();
ctx.insert("lists", &lists);
ctx.insert("lang", &lang);
let html = state.tera.render("lists.html", &ctx).unwrap();
Html(html).into_response()
}
In tera template:
<label for="family-id">{{ fluent(key="list-family", lang=lang) }}</label>
<select name="family-id" id="family-id">
{% for family in families %}
<option value="{{ family.family_id }}">{{ family.family_name }}</option>
{% endfor %}
</select>
Dependencies
~3–12MB
~146K SLoC