🧑‍💻 Mejora y simplifica localización de módulos

This commit is contained in:
Manuel Cillero 2023-06-04 00:43:28 +02:00
parent d0add7c7ab
commit 520d3bb20b
21 changed files with 265 additions and 325 deletions

View file

@ -13,12 +13,12 @@ impl ModuleTrait for Admin {
MODULE_ADMIN MODULE_ADMIN
} }
fn name(&self) -> String { fn name(&self) -> L10n {
_t("module_name", Locale::From(&LOCALE_ADMIN)) L10n::t("module_name", &LOCALE_ADMIN)
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
Some(_t("module_description", Locale::From(&LOCALE_ADMIN))) L10n::t("module_description", &LOCALE_ADMIN)
} }
#[rustfmt::skip] #[rustfmt::skip]

View file

@ -5,64 +5,64 @@ use pagetop_minimal::component::*;
pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
let top_menu = MegaMenu::new() let top_menu = MegaMenu::new()
.with_item(MegaMenuItem::label(Text::t("module_name", &LOCALE_ADMIN))) .with_item(MegaMenuItem::label(L10n::t("module_name", &LOCALE_ADMIN)))
.with_item(MegaMenuItem::link( .with_item(MegaMenuItem::link(
Text::n("Opción 2"), L10n::text("Opción 2"),
"https://www.google.es", "https://www.google.es",
)) ))
.with_item(MegaMenuItem::link_blank( .with_item(MegaMenuItem::link_blank(
Text::n("Opción 3"), L10n::text("Opción 3"),
"https://www.google.es", "https://www.google.es",
)) ))
.with_item(MegaMenuItem::submenu( .with_item(MegaMenuItem::submenu(
Text::n("Submenú 1"), L10n::text("Submenú 1"),
MegaMenu::new() MegaMenu::new()
.with_item(MegaMenuItem::label(Text::n("Opción 1"))) .with_item(MegaMenuItem::label(L10n::text("Opción 1")))
.with_item(MegaMenuItem::label(Text::n("Opción 2"))), .with_item(MegaMenuItem::label(L10n::text("Opción 2"))),
)) ))
.with_item(MegaMenuItem::separator()) .with_item(MegaMenuItem::separator())
.with_item(MegaMenuItem::submenu( .with_item(MegaMenuItem::submenu(
Text::n("Submenú 2"), L10n::text("Submenú 2"),
MegaMenu::new() MegaMenu::new()
.with_item(MegaMenuItem::label(Text::n("Opción 1"))) .with_item(MegaMenuItem::label(L10n::text("Opción 1")))
.with_item(MegaMenuItem::label(Text::n("Opción 2"))), .with_item(MegaMenuItem::label(L10n::text("Opción 2"))),
)) ))
.with_item(MegaMenuItem::label(Text::n("Opción 4"))); .with_item(MegaMenuItem::label(L10n::text("Opción 4")));
let side_menu = MegaMenu::new() let side_menu = MegaMenu::new()
.with_item(MegaMenuItem::label(Text::n("Opción 1"))) .with_item(MegaMenuItem::label(L10n::text("Opción 1")))
.with_item(MegaMenuItem::link( .with_item(MegaMenuItem::link(
Text::n("Opción 2"), L10n::text("Opción 2"),
"https://www.google.es", "https://www.google.es",
)) ))
.with_item(MegaMenuItem::link_blank( .with_item(MegaMenuItem::link_blank(
Text::n("Opción 3"), L10n::text("Opción 3"),
"https://www.google.es", "https://www.google.es",
)) ))
.with_item(MegaMenuItem::submenu( .with_item(MegaMenuItem::submenu(
Text::n("Submenú 1"), L10n::text("Submenú 1"),
MegaMenu::new() MegaMenu::new()
.with_item(MegaMenuItem::label(Text::n("Opción 1"))) .with_item(MegaMenuItem::label(L10n::text("Opción 1")))
.with_item(MegaMenuItem::label(Text::n("Opción 2"))), .with_item(MegaMenuItem::label(L10n::text("Opción 2"))),
)) ))
.with_item(MegaMenuItem::separator()) .with_item(MegaMenuItem::separator())
.with_item(MegaMenuItem::submenu( .with_item(MegaMenuItem::submenu(
Text::n("Submenú 2"), L10n::text("Submenú 2"),
MegaMenu::new() MegaMenu::new()
.with_item(MegaMenuItem::label(Text::n("Opción 1"))) .with_item(MegaMenuItem::label(L10n::text("Opción 1")))
.with_item(MegaMenuItem::label(Text::n("Opción 2"))), .with_item(MegaMenuItem::label(L10n::text("Opción 2"))),
)) ))
.with_item(MegaMenuItem::label(Text::n("Opción 4"))); .with_item(MegaMenuItem::label(L10n::text("Opción 4")));
Page::new(request) Page::new(request)
.with_context(ContextOp::Theme("Bootsier")) .with_context(ContextOp::Theme("Bootsier"))
.with_title(Text::n("Admin")) .with_title(L10n::text("Admin"))
.with_this_in("top-menu", top_menu) .with_this_in("top-menu", top_menu)
.with_this_in( .with_this_in(
"region-content", "region-content",
grid::Row::new() grid::Row::new()
.with_column(grid::Column::new().with_component(side_menu)) .with_column(grid::Column::new().with_component(side_menu))
.with_column(grid::Column::new().with_component(Html::n(html! { .with_column(grid::Column::new().with_component(L10n::html(html! {
p { "Columna 2"} p { "Columna 2"}
}))), }))),
) )

View file

@ -45,7 +45,7 @@ impl ThemeTrait for Bootsier {
fn render_component( fn render_component(
&self, &self,
component: &dyn ComponentTrait, component: &dyn ComponentTrait,
_rcx: &mut RenderContext, rcx: &mut RenderContext,
) -> Option<Markup> { ) -> Option<Markup> {
match component.handle() { match component.handle() {
ERROR_404 => Some(html! { ERROR_404 => Some(html! {
@ -59,18 +59,18 @@ impl ThemeTrait for Bootsier {
div class="media-body" { div class="media-body" {
h1 class="display-4" { ("RESOURCE NOT FOUND") } h1 class="display-4" { ("RESOURCE NOT FOUND") }
p class="lead" { p class="lead" {
(_t("e404-description", Locale::From(&LOCALE_BOOTSIER))) (L10n::t("e404-description", &LOCALE_BOOTSIER).render(rcx))
} }
hr class="my-4"; hr class="my-4";
p { p {
(_t("e404-description", Locale::From(&LOCALE_BOOTSIER))) (L10n::t("e404-description", &LOCALE_BOOTSIER).render(rcx))
} }
a a
class="btn btn-primary btn-lg" class="btn btn-primary btn-lg"
href="/" href="/"
role="button" role="button"
{ {
(_t("back-homepage", Locale::From(&LOCALE_BOOTSIER))) (L10n::t("back-homepage", &LOCALE_BOOTSIER).render(rcx))
} }
} }
} }

View file

@ -14,12 +14,12 @@ impl ModuleTrait for HomeDemo {
MODULE_DEMOHOME MODULE_DEMOHOME
} }
fn name(&self) -> String { fn name(&self) -> L10n {
_t("module_name", Locale::From(&LOCALE_DEMOHOME)) L10n::t("module_name", &LOCALE_DEMOHOME)
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
Some(_t("module_description", Locale::From(&LOCALE_DEMOHOME))) L10n::t("module_description", &LOCALE_DEMOHOME)
} }
fn dependencies(&self) -> Vec<ModuleStaticRef> { fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -34,7 +34,7 @@ impl ModuleTrait for HomeDemo {
async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request) Page::new(request)
.with_title(Text::t("page_title", &LOCALE_DEMOHOME)) .with_title(L10n::t("page_title", &LOCALE_DEMOHOME))
.with_context(ContextOp::AddStyleSheet(StyleSheet::located( .with_context(ContextOp::AddStyleSheet(StyleSheet::located(
"/homedemo/css/styles.css", "/homedemo/css/styles.css",
))) )))
@ -55,11 +55,11 @@ fn hello_world() -> Container {
.with_classes(ClassesOp::Add, "hello-col-text") .with_classes(ClassesOp::Add, "hello-col-text")
.with_size(grid::ColumnSize::Is5of12) .with_size(grid::ColumnSize::Is5of12)
.with_component( .with_component(
Heading::h1(Text::t("page_title", &LOCALE_DEMOHOME)) Heading::h1(L10n::t("page_title", &LOCALE_DEMOHOME))
.with_display(HeadingDisplay::Medium), .with_display(HeadingDisplay::Medium),
) )
.with_component( .with_component(
Paragraph::with(Text::e("hello_intro", &LOCALE_DEMOHOME).with_arg( Paragraph::with(L10n::e("hello_intro", &LOCALE_DEMOHOME).with_arg(
"app", "app",
format!( format!(
"<span class=\"app-name\">{}</span>", "<span class=\"app-name\">{}</span>",
@ -69,7 +69,7 @@ fn hello_world() -> Container {
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with( .with_component(Paragraph::with(
Text::e("hello_powered", &LOCALE_DEMOHOME).with_arg( L10n::e("hello_powered", &LOCALE_DEMOHOME).with_arg(
"pagetop", "pagetop",
format!( format!(
"<a href=\"{}\" target=\"_blank\">{}</a>", "<a href=\"{}\" target=\"_blank\">{}</a>",
@ -80,14 +80,14 @@ fn hello_world() -> Container {
.with_component( .with_component(
Anchor::button( Anchor::button(
"https://github.com/manuelcillero/pagetop", "https://github.com/manuelcillero/pagetop",
Text::t("hello_code", &LOCALE_DEMOHOME), L10n::t("hello_code", &LOCALE_DEMOHOME),
) )
.with_target(AnchorTarget::Blank) .with_target(AnchorTarget::Blank)
.with_left_icon(Icon::with("git")) .with_left_icon(Icon::with("git"))
.with_classes(ClassesOp::Add, "code-link"), .with_classes(ClassesOp::Add, "code-link"),
) )
.with_component( .with_component(
Anchor::link("#welcome", Text::t("hello_welcome", &LOCALE_DEMOHOME)) Anchor::link("#welcome", L10n::t("hello_welcome", &LOCALE_DEMOHOME))
.with_left_icon(Icon::with("arrow-down-circle-fill")) .with_left_icon(Icon::with("arrow-down-circle-fill"))
.with_classes(ClassesOp::Add, "welcome-link"), .with_classes(ClassesOp::Add, "welcome-link"),
), ),
@ -104,9 +104,9 @@ fn welcome() -> Container {
Container::section() Container::section()
.with_id("welcome") .with_id("welcome")
.with_classes(ClassesOp::Add, "welcome-col-text") .with_classes(ClassesOp::Add, "welcome-col-text")
.with_component(Heading::h2(Text::t("welcome_page", &LOCALE_DEMOHOME))) .with_component(Heading::h2(L10n::t("welcome_page", &LOCALE_DEMOHOME)))
.with_component( .with_component(
Heading::h3(Text::e("welcome_subtitle", &LOCALE_DEMOHOME).with_arg( Heading::h3(L10n::e("welcome_subtitle", &LOCALE_DEMOHOME).with_arg(
"app", "app",
format!( format!(
"<span class=\"app-name\">{}</span>", "<span class=\"app-name\">{}</span>",
@ -116,10 +116,10 @@ fn welcome() -> Container {
.with_display(HeadingDisplay::Subtitle), .with_display(HeadingDisplay::Subtitle),
) )
.with_component( .with_component(
Paragraph::with(Text::t("welcome_text1", &LOCALE_DEMOHOME)) Paragraph::with(L10n::t("welcome_text1", &LOCALE_DEMOHOME))
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(Text::t("welcome_text2", &LOCALE_DEMOHOME))) .with_component(Paragraph::with(L10n::t("welcome_text2", &LOCALE_DEMOHOME)))
} }
fn about_pagetop() -> Container { fn about_pagetop() -> Container {
@ -134,21 +134,15 @@ fn about_pagetop() -> Container {
.with_column( .with_column(
grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "pagetop-col-text") .with_classes(ClassesOp::Add, "pagetop-col-text")
.with_component(Heading::h2(Text::t("pagetop_title", &LOCALE_DEMOHOME))) .with_component(Heading::h2(L10n::t("pagetop_title", &LOCALE_DEMOHOME)))
.with_component( .with_component(
Paragraph::with(Text::t("pagetop_text1", &LOCALE_DEMOHOME)) Paragraph::with(L10n::t("pagetop_text1", &LOCALE_DEMOHOME))
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(Text::t("pagetop_text2", &LOCALE_DEMOHOME))) .with_component(Paragraph::with(L10n::t("pagetop_text2", &LOCALE_DEMOHOME)))
.with_component(Paragraph::with( .with_component(Paragraph::with(
Text::e("pagetop_text3", &LOCALE_DEMOHOME).with_arg( L10n::e("pagetop_text3", &LOCALE_DEMOHOME)
"pagetop_website", .with_arg("href", "https://docs.rs/pagetop/latest/pagetop".to_string()),
format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://docs.rs/pagetop/latest/pagetop",
_t("pagetop_website", Locale::From(&LOCALE_DEMOHOME)),
),
),
)), )),
), ),
) )
@ -160,12 +154,12 @@ fn promo_pagetop() -> Container {
.with_column( .with_column(
grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "promo-col-text") .with_classes(ClassesOp::Add, "promo-col-text")
.with_component(Heading::h2(Text::t( .with_component(Heading::h2(L10n::t(
"pagetop_promo_title", "pagetop_promo_title",
&LOCALE_DEMOHOME, &LOCALE_DEMOHOME,
))) )))
.with_component( .with_component(
Paragraph::with(Text::e("pagetop_promo_text1", &LOCALE_DEMOHOME).with_arg( Paragraph::with(L10n::e("pagetop_promo_text1", &LOCALE_DEMOHOME).with_arg(
"pagetop", "pagetop",
format!( format!(
"<a href=\"{}\" target=\"_blank\">{}</a>", "<a href=\"{}\" target=\"_blank\">{}</a>",
@ -196,15 +190,15 @@ fn reporting_issues() -> Container {
grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "reporting-col-text") .with_classes(ClassesOp::Add, "reporting-col-text")
.with_size(grid::ColumnSize::Is6of12) .with_size(grid::ColumnSize::Is6of12)
.with_component(Heading::h2(Text::t( .with_component(Heading::h2(L10n::t(
"report_problems_title", "report_problems_title",
&LOCALE_DEMOHOME, &LOCALE_DEMOHOME,
))) )))
.with_component( .with_component(
Paragraph::with(Text::t("report_problems_text1", &LOCALE_DEMOHOME)) Paragraph::with(L10n::t("report_problems_text1", &LOCALE_DEMOHOME))
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(Text::t( .with_component(Paragraph::with(L10n::t(
"report_problems_text2", "report_problems_text2",
&LOCALE_DEMOHOME, &LOCALE_DEMOHOME,
))), ))),

View file

@ -16,8 +16,7 @@ welcome_text2 = If the problem persists, please contact your system administrato
pagetop_title = About PageTop pagetop_title = About PageTop
pagetop_text1 = If you can read this page, it means that the PageTop server is working properly, but has not yet been configured. pagetop_text1 = If you can read this page, it means that the PageTop server is working properly, but has not yet been configured.
pagetop_text2 = PageTop defines an interface for the most stable and popular Rust packages to build modular, extensible and configurable web solutions. pagetop_text2 = PageTop defines an interface for the most stable and popular Rust packages to build modular, extensible and configurable web solutions.
pagetop_text3 = For more information on PageTop please visit the { $pagetop_website }. pagetop_text3 = For more information on PageTop please visit the <a href="{ $href }" target="_blank">technical documentation</a>.
pagetop_website = technical documentation
pagetop_promo_title = Promoting PageTop pagetop_promo_title = Promoting PageTop
pagetop_promo_text1 = You are free to use the image below on applications powered by { $pagetop }. Thanks for using PageTop! pagetop_promo_text1 = You are free to use the image below on applications powered by { $pagetop }. Thanks for using PageTop!

View file

@ -16,8 +16,7 @@ welcome_text2 = Si el problema persiste, póngase en contacto con el administrad
pagetop_title = Sobre PageTop pagetop_title = Sobre PageTop
pagetop_text1 = Si puedes leer esta página, significa que el servidor PageTop funciona correctamente, pero aún no se ha configurado. pagetop_text1 = Si puedes leer esta página, significa que el servidor PageTop funciona correctamente, pero aún no se ha configurado.
pagetop_text2 = PageTop define una interfaz para los paquetes Rust más estables y populares para crear soluciones web modulares, extensibles y configurables. pagetop_text2 = PageTop define una interfaz para los paquetes Rust más estables y populares para crear soluciones web modulares, extensibles y configurables.
pagetop_text3 = Para más información sobre PageTop, por favor visita la { $pagetop_website }. pagetop_text3 = Para más información sobre PageTop, por favor visita la <a href="{ $href }" target="_blank">documentación técnica</a>.
pagetop_website = documentación técnica
pagetop_promo_title = Promociona PageTop pagetop_promo_title = Promociona PageTop
pagetop_promo_text1 = Eres libre de usar la siguiente imagen en aplicaciones desarrolladas con { $pagetop }. ¡Gracias por usar PageTop! pagetop_promo_text1 = Eres libre de usar la siguiente imagen en aplicaciones desarrolladas con { $pagetop }. ¡Gracias por usar PageTop!

View file

@ -2,8 +2,8 @@ use pagetop::prelude::*;
define_handle!(COMPONENT_MEGAMENUITEM); define_handle!(COMPONENT_MEGAMENUITEM);
type Label = OneComponent<Text>; type Label = OneComponent<L10n>;
type Content = OneComponent<Html>; type Content = OneComponent<L10n>;
#[derive(Default)] #[derive(Default)]
pub enum MegaMenuItemType { pub enum MegaMenuItemType {
@ -86,35 +86,35 @@ impl ComponentTrait for MegaMenuItem {
} }
impl MegaMenuItem { impl MegaMenuItem {
pub fn label(label: Text) -> Self { pub fn label(label: L10n) -> Self {
MegaMenuItem { MegaMenuItem {
item_type: MegaMenuItemType::Label(OneComponent::new_with(label)), item_type: MegaMenuItemType::Label(OneComponent::new_with(label)),
..Default::default() ..Default::default()
} }
} }
pub fn link(label: Text, path: &str) -> Self { pub fn link(label: L10n, path: &str) -> Self {
MegaMenuItem { MegaMenuItem {
item_type: MegaMenuItemType::Link(OneComponent::new_with(label), path.to_owned()), item_type: MegaMenuItemType::Link(OneComponent::new_with(label), path.to_owned()),
..Default::default() ..Default::default()
} }
} }
pub fn link_blank(label: Text, path: &str) -> Self { pub fn link_blank(label: L10n, path: &str) -> Self {
MegaMenuItem { MegaMenuItem {
item_type: MegaMenuItemType::LinkBlank(OneComponent::new_with(label), path.to_owned()), item_type: MegaMenuItemType::LinkBlank(OneComponent::new_with(label), path.to_owned()),
..Default::default() ..Default::default()
} }
} }
pub fn html(content: Html) -> Self { pub fn html(content: L10n) -> Self {
MegaMenuItem { MegaMenuItem {
item_type: MegaMenuItemType::Html(OneComponent::new_with(content)), item_type: MegaMenuItemType::Html(OneComponent::new_with(content)),
..Default::default() ..Default::default()
} }
} }
pub fn submenu(label: Text, menu: MegaMenu) -> Self { pub fn submenu(label: L10n, menu: MegaMenu) -> Self {
MegaMenuItem { MegaMenuItem {
item_type: MegaMenuItemType::Submenu(OneComponent::new_with(label), menu), item_type: MegaMenuItemType::Submenu(OneComponent::new_with(label), menu),
..Default::default() ..Default::default()

View file

@ -23,7 +23,7 @@ pub enum AnchorTarget {
} }
type AnchorIcon = OneComponent<Icon>; type AnchorIcon = OneComponent<Icon>;
type AnchorHtml = OneComponent<Text>; type AnchorHtml = OneComponent<L10n>;
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Default)] #[derive(Default)]
@ -91,11 +91,11 @@ impl ComponentTrait for Anchor {
} }
impl Anchor { impl Anchor {
pub fn link(href: &str, html: Text) -> Self { pub fn link(href: &str, html: L10n) -> Self {
Anchor::new().with_href(href).with_html(html) Anchor::new().with_href(href).with_html(html)
} }
pub fn button(href: &str, html: Text) -> Self { pub fn button(href: &str, html: L10n) -> Self {
Anchor::new() Anchor::new()
.with_type(AnchorType::Button) .with_type(AnchorType::Button)
.with_href(href) .with_href(href)
@ -152,7 +152,7 @@ impl Anchor {
} }
#[fn_builder] #[fn_builder]
pub fn alter_html(&mut self, html: Text) -> &mut Self { pub fn alter_html(&mut self, html: L10n) -> &mut Self {
self.html.set(html); self.html.set(html);
self self
} }

View file

@ -10,7 +10,7 @@ pub enum ButtonType {
Reset, Reset,
} }
type ButtonValue = OneComponent<Text>; type ButtonValue = OneComponent<L10n>;
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Default)] #[derive(Default)]
@ -77,11 +77,11 @@ impl ComponentTrait for Button {
} }
impl Button { impl Button {
pub fn with(value: Text) -> Self { pub fn with(value: L10n) -> Self {
Button::new().with_value(value) Button::new().with_value(value)
} }
pub fn submit(value: Text) -> Self { pub fn submit(value: L10n) -> Self {
let mut button = Button::new() let mut button = Button::new()
.with_classes(ClassesOp::Replace("form-button"), "form-submit") .with_classes(ClassesOp::Replace("form-button"), "form-submit")
.with_value(value); .with_value(value);
@ -89,7 +89,7 @@ impl Button {
button button
} }
pub fn reset(value: Text) -> Self { pub fn reset(value: L10n) -> Self {
let mut button = Button::new() let mut button = Button::new()
.with_classes(ClassesOp::Replace("form-button"), "form-reset") .with_classes(ClassesOp::Replace("form-button"), "form-reset")
.with_value(value); .with_value(value);
@ -124,7 +124,7 @@ impl Button {
} }
#[fn_builder] #[fn_builder]
pub fn alter_value(&mut self, value: Text) -> &mut Self { pub fn alter_value(&mut self, value: L10n) -> &mut Self {
self.value.set(value); self.value.set(value);
self self
} }

View file

@ -13,8 +13,8 @@ pub enum InputType {
Url, Url,
} }
type InputLabel = OneComponent<Text>; type InputLabel = OneComponent<L10n>;
type InputHelpText = OneComponent<Text>; type InputHelpText = OneComponent<L10n>;
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Default)] #[derive(Default)]
@ -206,7 +206,7 @@ impl Input {
} }
#[fn_builder] #[fn_builder]
pub fn alter_label(&mut self, label: Text) -> &mut Self { pub fn alter_label(&mut self, label: L10n) -> &mut Self {
self.label.set(label); self.label.set(label);
self self
} }
@ -281,7 +281,7 @@ impl Input {
} }
#[fn_builder] #[fn_builder]
pub fn alter_help_text(&mut self, help_text: Text) -> &mut Self { pub fn alter_help_text(&mut self, help_text: L10n) -> &mut Self {
self.help_text.set(help_text); self.help_text.set(help_text);
self self
} }

View file

@ -25,7 +25,7 @@ pub enum HeadingDisplay {
Subtitle, Subtitle,
} }
type HeadingText = OneComponent<Text>; type HeadingText = OneComponent<L10n>;
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Default)] #[derive(Default)]
@ -80,37 +80,37 @@ impl ComponentTrait for Heading {
} }
impl Heading { impl Heading {
pub fn h1(text: Text) -> Self { pub fn h1(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H1) .with_heading_type(HeadingType::H1)
.with_text(text) .with_text(text)
} }
pub fn h2(text: Text) -> Self { pub fn h2(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H2) .with_heading_type(HeadingType::H2)
.with_text(text) .with_text(text)
} }
pub fn h3(text: Text) -> Self { pub fn h3(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H3) .with_heading_type(HeadingType::H3)
.with_text(text) .with_text(text)
} }
pub fn h4(text: Text) -> Self { pub fn h4(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H4) .with_heading_type(HeadingType::H4)
.with_text(text) .with_text(text)
} }
pub fn h5(text: Text) -> Self { pub fn h5(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H5) .with_heading_type(HeadingType::H5)
.with_text(text) .with_text(text)
} }
pub fn h6(text: Text) -> Self { pub fn h6(text: L10n) -> Self {
Heading::new() Heading::new()
.with_heading_type(HeadingType::H6) .with_heading_type(HeadingType::H6)
.with_text(text) .with_text(text)
@ -149,7 +149,7 @@ impl Heading {
} }
#[fn_builder] #[fn_builder]
pub fn alter_text(&mut self, text: Text) -> &mut Self { pub fn alter_text(&mut self, text: L10n) -> &mut Self {
self.text.set(text); self.text.set(text);
self self
} }

View file

@ -11,11 +11,11 @@ impl ModuleTrait for Menu {
MODULE_MENU MODULE_MENU
} }
fn name(&self) -> String { fn name(&self) -> L10n {
_t("module_name", Locale::From(&LOCALE_MENU)) L10n::t("module_name", &LOCALE_MENU)
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
Some(_t("module_description", Locale::From(&LOCALE_MENU))) L10n::t("module_description", &LOCALE_MENU)
} }
} }

View file

@ -14,12 +14,12 @@ impl ModuleTrait for Node {
MODULE_NODE MODULE_NODE
} }
fn name(&self) -> String { fn name(&self) -> L10n {
_t("module_name", Locale::From(&LOCALE_NODE)) L10n::t("module_name", &LOCALE_NODE)
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
Some(_t("module_description", Locale::From(&LOCALE_NODE))) L10n::t("module_description", &LOCALE_NODE)
} }
fn configure_service(&self, cfg: &mut server::web::ServiceConfig) { fn configure_service(&self, cfg: &mut server::web::ServiceConfig) {
@ -41,7 +41,7 @@ impl ModuleTrait for Node {
} }
async fn node(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { async fn node(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request).with_title(Text::n("Nodo")).render() Page::new(request).with_title(L10n::text("Nodo")).render()
} }
fn before_render_page(page: &mut Page) { fn before_render_page(page: &mut Page) {

View file

@ -14,12 +14,12 @@ impl ModuleTrait for User {
MODULE_USER MODULE_USER
} }
fn name(&self) -> String { fn name(&self) -> L10n {
_t("module_name", Locale::From(&LOCALE_USER)) L10n::t("module_name", &LOCALE_USER)
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
Some(_t("module_description", Locale::From(&LOCALE_USER))) L10n::t("module_description", &LOCALE_USER)
} }
fn dependencies(&self) -> Vec<ModuleStaticRef> { fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -42,7 +42,7 @@ impl ModuleTrait for User {
async fn login(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { async fn login(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request) Page::new(request)
.with_title(Text::n("Identificación del usuario")) .with_title(L10n::text("Identificación del usuario"))
.with_this_in( .with_this_in(
"region-content", "region-content",
Container::new() Container::new()
@ -58,9 +58,9 @@ fn form_login() -> Form {
.with_element( .with_element(
form_element::Input::textfield() form_element::Input::textfield()
.with_name("name") .with_name("name")
.with_label(Text::t("username", &LOCALE_USER)) .with_label(L10n::t("username", &LOCALE_USER))
.with_help_text( .with_help_text(
Text::t("username_help", &LOCALE_USER) L10n::t("username_help", &LOCALE_USER)
.with_arg("app", config::SETTINGS.app.name.to_owned()), .with_arg("app", config::SETTINGS.app.name.to_owned()),
) )
.with_autofocus(true), .with_autofocus(true),
@ -68,8 +68,8 @@ fn form_login() -> Form {
.with_element( .with_element(
form_element::Input::password() form_element::Input::password()
.with_name("pass") .with_name("pass")
.with_label(Text::t("password", &LOCALE_USER)) .with_label(L10n::t("password", &LOCALE_USER))
.with_help_text(Text::t("password_help", &LOCALE_USER)), .with_help_text(L10n::t("password_help", &LOCALE_USER)),
) )
.with_element(form_element::Button::submit(Text::t("login", &LOCALE_USER))) .with_element(form_element::Button::submit(L10n::t("login", &LOCALE_USER)))
} }

View file

@ -17,6 +17,5 @@ pub(crate) use all::common_components;
mod renderable; mod renderable;
pub use renderable::{IsRenderable, Renderable}; pub use renderable::{IsRenderable, Renderable};
mod basic; mod l10n;
pub use basic::{Html, COMPONENT_HTML}; pub use l10n::{L10n, COMPONENT_L10N};
pub use basic::{Text, COMPONENT_TEXT};

View file

@ -1,143 +0,0 @@
use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup, PreEscaped};
use crate::locale::{translate, Locale, Locales};
use crate::{define_handle, fn_builder, paste, Handle};
use std::collections::HashMap;
macro_rules! basic_components {
( $($COMPONENT_HANDLE:ident: $Component:ty => $TypeValue:ty),* ) => { $( paste! {
define_handle!($COMPONENT_HANDLE);
pub enum [< $Component Op >] {
None,
Value($TypeValue),
Translated(&'static str, &'static Locales),
Escaped(&'static str, &'static Locales),
}
pub struct $Component {
op: [< $Component Op >],
args: HashMap<&'static str, String>,
}
impl Default for $Component {
fn default() -> Self {
$Component {
op: [< $Component Op >]::None,
args: HashMap::new(),
}
}
}
impl ComponentTrait for $Component {
fn new() -> Self {
$Component::default()
}
fn handle(&self) -> Handle {
$COMPONENT_HANDLE
}
fn default_render(&self, rcx: &mut RenderContext) -> Markup {
match self.op() {
[< $Component Op >]::None => html! {},
[< $Component Op >]::Value(value) => html! { (value) },
[< $Component Op >]::Translated(key, locales) => html! {
(translate(
key,
Locale::Using(
rcx.langid(),
locales,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
))
},
[< $Component Op >]::Escaped(key, locales) => html! {
(PreEscaped(translate(
key,
Locale::Using(
rcx.langid(),
locales,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
)))
},
}
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}
impl $Component {
pub fn n(value: $TypeValue) -> Self {
$Component {
op: [< $Component Op >]::Value(value),
..Default::default()
}
}
pub fn t(key: &'static str, locales: &'static Locales) -> Self {
$Component {
op: [< $Component Op >]::Translated(key, locales),
..Default::default()
}
}
pub fn e(key: &'static str, locales: &'static Locales) -> Self {
$Component {
op: [< $Component Op >]::Escaped(key, locales),
..Default::default()
}
}
// $Component BUILDER.
#[fn_builder]
pub fn alter_op(&mut self, op: [< $Component Op >]) -> &mut Self {
self.op = op;
self
}
#[fn_builder]
pub fn alter_arg(&mut self, arg: &'static str, value: String) -> &mut Self {
self.args.insert(arg, value);
self
}
pub fn clear_args(&mut self) -> &mut Self {
self.args.drain();
self
}
// $Component GETTERS.
pub fn op(&self) -> &[< $Component Op >] {
&self.op
}
pub fn args(&self) -> &HashMap<&str, String> {
&self.args
}
}
} )* };
}
basic_components!(
COMPONENT_HTML: Html => Markup,
COMPONENT_TEXT: Text => &'static str
);

View file

@ -0,0 +1,141 @@
use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup, PreEscaped};
use crate::locale::Locales;
use crate::{define_handle, fn_builder, Handle};
use fluent_templates::Loader;
use std::collections::HashMap;
define_handle!(COMPONENT_L10N);
pub enum L10nOp {
None,
Value(Markup),
Translated(&'static str, &'static Locales),
Escaped(&'static str, &'static Locales),
}
pub struct L10n {
op: L10nOp,
args: HashMap<&'static str, String>,
}
impl Default for L10n {
fn default() -> Self {
L10n {
op: L10nOp::None,
args: HashMap::new(),
}
}
}
impl ComponentTrait for L10n {
fn new() -> Self {
L10n::default()
}
fn handle(&self) -> Handle {
COMPONENT_L10N
}
fn default_render(&self, rcx: &mut RenderContext) -> Markup {
match self.op() {
L10nOp::None => html! {},
L10nOp::Value(value) => html! { (value) },
L10nOp::Translated(key, locales) => html! {
(locales
.lookup_with_args(
rcx.langid(),
key,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
.unwrap_or(key.to_string())
)
},
L10nOp::Escaped(key, locales) => html! {
(PreEscaped(locales
.lookup_with_args(
rcx.langid(),
key,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
.unwrap_or(key.to_string())
))
},
}
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}
impl L10n {
pub fn text(text: &'static str) -> Self {
L10n {
op: L10nOp::Value(html! { (text) }),
..Default::default()
}
}
pub fn html(html: Markup) -> Self {
L10n {
op: L10nOp::Value(html),
..Default::default()
}
}
pub fn t(key: &'static str, locales: &'static Locales) -> Self {
L10n {
op: L10nOp::Translated(key, locales),
..Default::default()
}
}
pub fn e(key: &'static str, locales: &'static Locales) -> Self {
L10n {
op: L10nOp::Escaped(key, locales),
..Default::default()
}
}
// L10n BUILDER.
#[fn_builder]
pub fn alter_op(&mut self, op: L10nOp) -> &mut Self {
self.op = op;
self
}
#[fn_builder]
pub fn alter_arg(&mut self, arg: &'static str, value: String) -> &mut Self {
self.args.insert(arg, value);
self
}
pub fn clear_args(&mut self) -> &mut Self {
self.args.drain();
self
}
// L10n GETTERS.
pub fn op(&self) -> &L10nOp {
&self.op
}
pub fn args(&self) -> &HashMap<&str, String> {
&self.args
}
}

View file

@ -1,5 +1,6 @@
use super::ThemeStaticRef; use super::ThemeStaticRef;
use crate::core::component::L10n;
use crate::core::hook::HookAction; use crate::core::hook::HookAction;
use crate::util::single_type_name; use crate::util::single_type_name;
use crate::{server, Handle}; use crate::{server, Handle};
@ -17,12 +18,12 @@ pub trait BaseModule {
pub trait ModuleTrait: BaseModule + Send + Sync { pub trait ModuleTrait: BaseModule + Send + Sync {
fn handle(&self) -> Handle; fn handle(&self) -> Handle;
fn name(&self) -> String { fn name(&self) -> L10n {
self.single_name().to_owned() L10n::text(self.single_name())
} }
fn description(&self) -> Option<String> { fn description(&self) -> L10n {
None L10n::default()
} }
fn theme(&self) -> Option<ThemeStaticRef> { fn theme(&self) -> Option<ThemeStaticRef> {

View file

@ -1,9 +1,7 @@
//! Localización (L10n). //! Localización (L10n).
//! //!
//! Proporciona soporte a [Fluent](https://www.projectfluent.org/), un conjunto de especificaciones //! PageTop usa el conjunto de especificaciones [Fluent](https://www.projectfluent.org/) para la
//! para la localización de aplicaciones, así como implementaciones y buenas prácticas originalmente //! localización de aplicaciones.
//! desarrolladas por Mozilla.
//!
//! //!
//! # Sintaxis Fluent (FTL) //! # Sintaxis Fluent (FTL)
//! //!
@ -72,43 +70,23 @@
//! //!
//! # Cómo aplicar la localización en tu código //! # Cómo aplicar la localización en tu código
//! //!
//! Una vez hayas creado tu directorio de recursos FTL, sólo tienes que usar la poderosa macro //! Una vez hayas creado tu directorio de recursos FTL usa la macro
//! [`define_locale!`](crate::define_locale) para integrarlos en tu módulo o aplicación. //! [`define_locale!`](crate::define_locale) para integrarlos en tu módulo o aplicación.
//! //!
//! Y podrás usar las funciones globales [`_t()`] o [`_e()`] para traducir tus textos:
//!
//! ``` //! ```
//! use pagetop::prelude::*; //! use pagetop::prelude::*;
//! //!
//! define_locale!(LOCALE_SAMPLE, "static/locales"); //! define_locale!(LOCALE_SAMPLE, "static/locales");
//!
//! fn demo() {
//! println!("* {}", _t("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", Locale::With(&LOCALE_SAMPLE, &args)));
//! }
//! ``` //! ```
//! Aunque preferirás usar normalmente los componentes básicos [Text](crate::core::component::Text) //! Y utiliza el componente [L10n](crate::core::component::L10n) para incluir, en respuestas a las
//! y [Html](crate::core::component::Html) para incluir, en respuestas a las peticiones web, textos //! peticiones web, textos y contenidos opcionalmente traducibles según el contexto de renderizado.
//! y contenidos opcionalmente traducibles según el contexto de renderizado.
use crate::html::{Markup, PreEscaped};
use crate::{args, config, trace, LazyStatic}; use crate::{args, config, trace, LazyStatic};
pub(crate) use unic_langid::{langid, LanguageIdentifier};
pub use fluent_templates; pub use fluent_templates;
pub(crate) use fluent_templates::StaticLoader as Locales; pub(crate) use fluent_templates::StaticLoader as Locales;
pub(crate) use unic_langid::{langid, LanguageIdentifier};
use fluent_templates::fluent_bundle::FluentValue;
use fluent_templates::Loader;
use std::collections::HashMap; use std::collections::HashMap;
@ -127,7 +105,7 @@ static FALLBACK_LANGID: LazyStatic<LanguageIdentifier> = LazyStatic::new(|| lang
/// Almacena el Identificador de Idioma Unicode /// Almacena el Identificador de Idioma Unicode
/// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) /// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier))
/// global para la aplicación a partir de `SETTINGS.app.language`. /// global para la aplicación a partir de `SETTINGS.app.language`.
pub static DEFAULT_LANGID: LazyStatic<&LanguageIdentifier> = pub(crate) static DEFAULT_LANGID: LazyStatic<&LanguageIdentifier> =
LazyStatic::new(|| langid_for(config::SETTINGS.app.language.as_str())); LazyStatic::new(|| langid_for(config::SETTINGS.app.language.as_str()));
pub fn langid_for(language: &str) -> &LanguageIdentifier { pub fn langid_for(language: &str) -> &LanguageIdentifier {
@ -144,31 +122,3 @@ pub fn langid_for(language: &str) -> &LanguageIdentifier {
} }
} }
} }
pub enum Locale<'a> {
From(&'a Locales),
With(&'a Locales, &'a HashMap<String, FluentValue<'a>>),
Using(
&'a LanguageIdentifier,
&'a Locales,
&'a HashMap<String, FluentValue<'a>>,
),
}
pub fn _t(key: &str, locale: Locale) -> String {
translate(key, locale)
}
pub fn _e(key: &str, locale: Locale) -> Markup {
PreEscaped(translate(key, locale))
}
#[inline]
pub(crate) fn translate(key: &str, locale: Locale) -> String {
match locale {
Locale::From(locales) => locales.lookup(&DEFAULT_LANGID, key),
Locale::With(locales, args) => locales.lookup_with_args(&DEFAULT_LANGID, key, args),
Locale::Using(langid, locales, args) => locales.lookup_with_args(langid, key, args),
}
.unwrap_or(key.to_string())
}

View file

@ -3,7 +3,7 @@ pub use error403::ERROR_403;
mod error404; mod error404;
pub use error404::ERROR_404; pub use error404::ERROR_404;
use crate::core::component::Text; use crate::core::component::L10n;
use crate::response::{page::Page, ResponseError}; use crate::response::{page::Page, ResponseError};
use crate::server::http::{header::ContentType, StatusCode}; use crate::server::http::{header::ContentType, StatusCode};
use crate::server::{HttpRequest, HttpResponse}; use crate::server::{HttpRequest, HttpResponse};
@ -32,7 +32,7 @@ impl fmt::Display for FatalError {
FatalError::AccessDenied(request) => { FatalError::AccessDenied(request) => {
let error_page = Page::new(request.clone()); let error_page = Page::new(request.clone());
if let Ok(page) = error_page if let Ok(page) = error_page
.with_title(Text::n("Error FORBIDDEN")) .with_title(L10n::text("Error FORBIDDEN"))
.with_this_in("region-content", error403::Error403) .with_this_in("region-content", error403::Error403)
.with_template("error") .with_template("error")
.render() .render()
@ -46,7 +46,7 @@ impl fmt::Display for FatalError {
FatalError::NotFound(request) => { FatalError::NotFound(request) => {
let error_page = Page::new(request.clone()); let error_page = Page::new(request.clone());
if let Ok(page) = error_page if let Ok(page) = error_page
.with_title(Text::n("Error RESOURCE NOT FOUND")) .with_title(L10n::text("Error RESOURCE NOT FOUND"))
.with_this_in("region-content", error404::Error404) .with_this_in("region-content", error404::Error404)
.with_template("error") .with_template("error")
.render() .render()

View file

@ -11,8 +11,8 @@ use unic_langid::CharacterDirection;
use std::collections::HashMap; use std::collections::HashMap;
type PageTitle = OneComponent<Text>; type PageTitle = OneComponent<L10n>;
type PageDescription = OneComponent<Text>; type PageDescription = OneComponent<L10n>;
#[rustfmt::skip] #[rustfmt::skip]
pub struct Page { pub struct Page {
@ -60,13 +60,13 @@ impl Page {
} }
#[fn_builder] #[fn_builder]
pub fn alter_title(&mut self, title: Text) -> &mut Self { pub fn alter_title(&mut self, title: L10n) -> &mut Self {
self.title.set(title); self.title.set(title);
self self
} }
#[fn_builder] #[fn_builder]
pub fn alter_description(&mut self, description: Text) -> &mut Self { pub fn alter_description(&mut self, description: L10n) -> &mut Self {
self.description.set(description); self.description.set(description);
self self
} }