🎨 Nueov enfoque para las funciones de traducción

This commit is contained in:
Manuel Cillero 2023-05-21 21:59:47 +02:00
parent 7691bf4b2f
commit 71b0b0889d
11 changed files with 110 additions and 93 deletions

View file

@ -2,7 +2,7 @@ use pagetop::prelude::*;
define_handle!(MODULE_ADMIN);
define_locale!("src/locales");
define_locale!(LOCALE_ADMIN, "src/locales");
mod summary;
@ -14,11 +14,11 @@ impl ModuleTrait for Admin {
}
fn name(&self) -> String {
l("module_name")
t("module_name", Locale::From(&LOCALE_ADMIN))
}
fn description(&self) -> Option<String> {
Some(l("module_description"))
Some(t("module_description", Locale::From(&LOCALE_ADMIN)))
}
#[rustfmt::skip]

View file

@ -1,11 +1,13 @@
use super::l;
use super::LOCALE_ADMIN;
use pagetop::prelude::*;
use pagetop_megamenu::component::{MegaMenu, MegaMenuItem};
use pagetop_minimal::component::*;
pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
let top_menu = MegaMenu::new()
.with_item(MegaMenuItem::label(l("module_name").as_str()))
.with_item(MegaMenuItem::label(
t("module_name", Locale::From(&LOCALE_ADMIN)).as_str(),
))
.with_item(MegaMenuItem::link("Opción 2", "https://www.google.es"))
.with_item(MegaMenuItem::link_blank(
"Opción 3",

View file

@ -2,7 +2,7 @@ use pagetop::prelude::*;
define_handle!(THEME_BOOTSIER);
define_locale!("src/locales");
define_locale!(LOCALE_BOOTSIER, "src/locales");
include!(concat!(env!("OUT_DIR"), "/bootsier.rs"));
@ -53,15 +53,15 @@ impl ThemeTrait for Bootsier {
alt="Caution!";
div class="media-body" {
h1 class="display-4" { ("RESOURCE NOT FOUND") }
p class="lead" { (l("e404-description")) }
p class="lead" { (t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
hr class="my-4";
p { (l("e404-description")) }
p { (t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
a
class="btn btn-primary btn-lg"
href="/"
role="button"
{
(l("back-homepage"))
(t("back-homepage", Locale::From(&LOCALE_BOOTSIER)))
}
}
}

View file

@ -3,7 +3,7 @@ use pagetop_minimal::component::*;
define_handle!(MODULE_DEMOHOME);
define_locale!("src/locales");
define_locale!(LOCALE_DEMOHOME, "src/locales");
include!(concat!(env!("OUT_DIR"), "/homedemo.rs"));
@ -15,11 +15,11 @@ impl ModuleTrait for HomeDemo {
}
fn name(&self) -> String {
l("module_name")
t("module_name", Locale::From(&LOCALE_DEMOHOME))
}
fn description(&self) -> Option<String> {
Some(l("module_description"))
Some(t("module_description", Locale::From(&LOCALE_DEMOHOME)))
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -34,7 +34,7 @@ impl ModuleTrait for HomeDemo {
async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request)
.with_title(l("page_title").as_str())
.with_title(t("page_title", Locale::From(&LOCALE_DEMOHOME)).as_str())
.with_context(ContextOp::AddStyleSheet(StyleSheet::located(
"/homedemo/css/styles.css",
)))
@ -56,43 +56,46 @@ fn hello_world() -> Container {
.with_size(grid::ColumnSize::Is5of12)
.with_component(
Heading::h1(html! {
(l("page_title"))
(t("page_title", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(HeadingDisplay::Medium),
)
.with_component(
Paragraph::with(html! {
(e("hello_intro", &args![
(e("hello_intro", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!(
"<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name,
)
]))
])))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(e("hello_powered", &args![
(e("hello_powered", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://pagetop.cillero.es",
"PageTop",
)
]))
])))
}))
.with_component(
Anchor::button(
"https://github.com/manuelcillero/pagetop",
html! { (l("hello_code")) },
html! { (t("hello_code", Locale::From(&LOCALE_DEMOHOME))) },
)
.with_target(AnchorTarget::Blank)
.with_left_icon(Icon::with("git"))
.with_classes(ClassesOp::Add, "code-link"),
)
.with_component(
Anchor::link("#welcome", html! { (l("hello_welcome")) })
.with_left_icon(Icon::with("arrow-down-circle-fill"))
.with_classes(ClassesOp::Add, "welcome-link"),
Anchor::link(
"#welcome",
html! { (t("hello_welcome", Locale::From(&LOCALE_DEMOHOME))) },
)
.with_left_icon(Icon::with("arrow-down-circle-fill"))
.with_classes(ClassesOp::Add, "welcome-link"),
),
)
.with_column(
@ -108,26 +111,28 @@ fn welcome() -> Container {
.with_id("welcome")
.with_classes(ClassesOp::Add, "welcome-col-text")
.with_component(Heading::h2(html! {
(l("welcome_page"))
(t("welcome_page", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Heading::h3(html! {
(e("welcome_subtitle", &args![
(e("welcome_subtitle", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!(
"<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name
)
]))
])))
})
.with_display(HeadingDisplay::Subtitle),
)
.with_component(
Paragraph::with(html! {
(l("welcome_text1"))
(t("welcome_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! { (l("welcome_text2")) }))
.with_component(Paragraph::with(
html! { (t("welcome_text2", Locale::From(&LOCALE_DEMOHOME))) },
))
}
fn about_pagetop() -> Container {
@ -143,25 +148,25 @@ fn about_pagetop() -> Container {
grid::Column::new()
.with_classes(ClassesOp::Add, "pagetop-col-text")
.with_component(Heading::h2(html! {
(l("pagetop_title"))
(t("pagetop_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(l("pagetop_text1"))
(t("pagetop_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(l("pagetop_text2"))
(t("pagetop_text2", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(Paragraph::with(html! {
(e("pagetop_text3", &args![
(e("pagetop_text3", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop_website" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://docs.rs/pagetop/latest/pagetop",
l("pagetop_website"),
t("pagetop_website", Locale::From(&LOCALE_DEMOHOME)),
)
]))
])))
})),
),
)
@ -174,17 +179,17 @@ fn promo_pagetop() -> Container {
grid::Column::new()
.with_classes(ClassesOp::Add, "promo-col-text")
.with_component(Heading::h2(html! {
(l("pagetop_promo_title"))
(t("pagetop_promo_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(e("pagetop_promo_text1", &args![
(e("pagetop_promo_text1", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://crates.io/crates/pagetop",
"PageTop",
)
]))
])))
})
.with_display(ParagraphDisplay::Small),
),
@ -211,16 +216,16 @@ fn reporting_issues() -> Container {
.with_classes(ClassesOp::Add, "reporting-col-text")
.with_size(grid::ColumnSize::Is6of12)
.with_component(Heading::h2(html! {
(l("report_problems_title"))
(t("report_problems_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(l("report_problems_text1"))
(t("report_problems_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(l("report_problems_text2"))
(t("report_problems_text2", Locale::From(&LOCALE_DEMOHOME)))
})),
),
)

View file

@ -8,8 +8,8 @@ mod escape;
mod generate;
mod parse;
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
pub fn expand(input: TokenStream) -> TokenStream {
@ -27,7 +27,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
),
_ => quote!(
pagetop::html::PreEscaped(#output_ident)
)
),
};
quote!({

View file

@ -113,7 +113,7 @@ impl Generator {
),
_ => quote!(
pagetop::html::html_private::render_to!(&#expr, &mut #output_ident);
)
),
});
}

View file

@ -2,7 +2,7 @@ use pagetop::prelude::*;
define_handle!(MODULE_MENU);
define_locale!("src/module/menu/locales");
define_locale!(LOCALE_MENU, "src/module/menu/locales");
pub struct Menu;
@ -12,10 +12,10 @@ impl ModuleTrait for Menu {
}
fn name(&self) -> String {
l("module_name")
t("module_name", Locale::From(&LOCALE_MENU))
}
fn description(&self) -> Option<String> {
Some(l("module_description"))
Some(t("module_description", Locale::From(&LOCALE_MENU)))
}
}

View file

@ -2,7 +2,7 @@ use pagetop::prelude::*;
define_handle!(MODULE_NODE);
define_locale!("src/locales");
define_locale!(LOCALE_NODE, "src/locales");
//mod entity;
mod migration;
@ -15,11 +15,11 @@ impl ModuleTrait for Node {
}
fn name(&self) -> String {
l("module_name")
t("module_name", Locale::From(&LOCALE_NODE))
}
fn description(&self) -> Option<String> {
Some(l("module_description"))
Some(t("module_description", Locale::From(&LOCALE_NODE)))
}
fn configure_service(&self, cfg: &mut server::web::ServiceConfig) {

View file

@ -3,7 +3,7 @@ use pagetop_minimal::component::*;
define_handle!(MODULE_USER);
define_locale!("src/locales");
define_locale!(LOCALE_USER, "src/locales");
mod migration;
@ -15,11 +15,11 @@ impl ModuleTrait for User {
}
fn name(&self) -> String {
l("module_name")
t("module_name", Locale::From(&LOCALE_USER))
}
fn description(&self) -> Option<String> {
Some(l("module_description"))
Some(t("module_description", Locale::From(&LOCALE_USER)))
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -58,13 +58,14 @@ fn form_login() -> Form {
.with_element(
form_element::Input::textfield()
.with_name("name")
.with_label(l("username").as_str())
.with_label(t("username", Locale::From(&LOCALE_USER)).as_str())
.with_help_text(
t(
"username_help",
&args![
"app" => config::SETTINGS.app.name.to_owned()
],
Locale::With(
&LOCALE_USER,
&args!["app" => config::SETTINGS.app.name.to_owned()],
),
)
.as_str(),
)
@ -73,8 +74,10 @@ fn form_login() -> Form {
.with_element(
form_element::Input::password()
.with_name("pass")
.with_label(l("password").as_str())
.with_help_text(l("password_help").as_str()),
.with_label(t("password", Locale::From(&LOCALE_USER)).as_str())
.with_help_text(t("password_help", Locale::From(&LOCALE_USER)).as_str()),
)
.with_element(form_element::Button::submit(l("login").as_str()))
.with_element(form_element::Button::submit(
t("login", Locale::From(&LOCALE_USER)).as_str(),
))
}

View file

@ -71,36 +71,36 @@
//! Una vez hayas creado tu directorio de recursos FTL, sólo tienes que usar la poderosa macro
//! [`define_locale!`](crate::define_locale) para integrar fácilmente tus recursos de localización.
//!
//! Esta macro crea dos funciones para el ámbito donde se ejecuta. Por un lado la función `l()` para
//! traducciones directas de etiquetas. Y por otro la función `t()` para traducciones que requieren
//! argumentos:
//! Luego sólo tendrás que usar la función `t()` para realizar tus traducciones:
//!
//! ```
//! use pagetop::{args, define_locale};
//! use pagetop::{args, define_locale, t};
//!
//! define_locale!("en-US");
//! define_locale!(LOCALE_SAMPLE, "src/locales");
//!
//! fn demo() {
//! println!("* {}", l("hello-world"));
//! println!("* {}", t("hello-world", &args![]));
//! println!("* {}", t("hello-user", &args!["userName" => "Julia"]));
//! println!("* {}", l("hello-world", Locale::From(&LOCALE_SAMPLE)));
//! println!("* {}", t("hello-user", Locale::With(&LOCALE_SAMPLE, &args!["userName" => "Julia"])));
//!
//! let args = args![
//! "userName" => "Roberto",
//! "photoCount" => 3,
//! "userGender" => "male"
//! ];
//! println!("* {}\n", t("shared-photos", &args));
//! println!("* {}\n", t("shared-photos", Locale::With(&LOCALE_SAMPLE, &args)));
//! }
//! ```
use crate::html::{Markup, PreEscaped};
use crate::{config, trace, LazyStatic};
use unic_langid::LanguageIdentifier;
pub use fluent_templates;
pub use fluent_templates::fluent_bundle::FluentValue;
pub use fluent_templates::{static_loader as static_locale, Loader as Locale};
pub use fluent_templates::{static_loader as static_locale, Loader, StaticLoader as Locales};
use std::collections::HashMap;
/// Almacena el Identificador de Idioma Unicode
/// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier))
@ -124,11 +124,11 @@ pub static LANGID: LazyStatic<LanguageIdentifier> =
#[macro_export]
/// Define un conjunto de elementos de localización y funciones locales de traducción.
macro_rules! define_locale {
( $dir_locales:literal $(, $core_locales:literal)? ) => {
( $LOCALES:ident, $dir_locales:literal $(, $core_locales:literal)? ) => {
use $crate::locale::*;
static_locale! {
static LOCALES = {
static $LOCALES = {
locales: $dir_locales,
$( core_locales: $core_locales, )?
fallback_language: "en-US",
@ -137,28 +137,33 @@ macro_rules! define_locale {
customise: |bundle| bundle.set_use_isolating(false),
};
}
#[allow(dead_code)]
fn l(key: &str) -> String {
LOCALES.lookup(&LANGID, key).unwrap_or(key.to_string())
}
#[allow(dead_code)]
fn t(
key: &str,
args: &std::collections::HashMap<String, FluentValue>
) -> String {
LOCALES.lookup_with_args(&LANGID, key, args).unwrap_or(key.to_string())
}
#[allow(dead_code)]
fn e(
key: &str,
args: &std::collections::HashMap<String, FluentValue>
) -> $crate::html::PreEscaped<String> {
$crate::html::PreEscaped(
LOCALES.lookup_with_args(&LANGID, key, args).unwrap_or(key.to_string())
)
}
};
}
pub enum Locale<'a> {
From(&'a Locales),
With(&'a Locales, &'a HashMap<String, FluentValue<'a>>),
Lang(&'a Locales, &'a LanguageIdentifier),
Using(
&'a Locales,
&'a LanguageIdentifier,
&'a HashMap<String, FluentValue<'a>>,
),
}
pub fn t(key: &str, locale: Locale) -> String {
match locale {
Locale::From(locales) => locales.lookup(&LANGID, key).unwrap_or(key.to_string()),
Locale::With(locales, args) => locales
.lookup_with_args(&LANGID, key, args)
.unwrap_or(key.to_string()),
Locale::Lang(locales, langid) => locales.lookup(langid, key).unwrap_or(key.to_string()),
Locale::Using(locales, langid, args) => locales
.lookup_with_args(langid, key, args)
.unwrap_or(key.to_string()),
}
}
pub fn e(key: &str, locale: Locale) -> Markup {
PreEscaped(t(key, locale))
}

View file

@ -16,6 +16,8 @@ pub use crate::config;
pub use crate::trace;
pub use crate::locale::*;
pub use crate::html::*;
#[cfg(feature = "database")]