♻️ (locale): Renombra L10n a Lc

Simplifica el nombre del tipo de localización. Actualiza todas las
referencias en el crate principal, extensiones, ejemplos y tests.
This commit is contained in:
Manuel Cillero 2026-08-12 22:56:09 +02:00
parent 3198b74399
commit be10dd28a1
65 changed files with 612 additions and 621 deletions

View file

@ -17,7 +17,7 @@
//! [`Context`]: crate::core::component::Context
use crate::core::action::{ActionDispatcher, ActionKey, try_dispatch_actions};
use crate::locale::L10n;
use crate::locale::Lc;
use crate::response::ErrorPage;
use crate::web::HttpRequest;
use crate::{CowStr, UniqueId, Weight};
@ -116,8 +116,8 @@ pub trait Permission: Send + Sync {
///
/// Por defecto devuelve la propia clave; una extensión que registre sus permisos en un catálogo
/// visible debería sobrescribirlo con un texto traducible.
fn label(&self) -> L10n {
L10n::n(self.key())
fn label(&self) -> Lc {
Lc::n(self.key())
}
/// Identificador estable de la categoría del permiso, usado para agrupar en un catálogo (p.
@ -129,8 +129,8 @@ pub trait Permission: Send + Sync {
/// Título traducible de [`group()`](Self::group), mostrado en la UI de administración.
///
/// Por defecto reutiliza el propio identificador del grupo como texto fijo.
fn group_label(&self) -> L10n {
L10n::n(self.group())
fn group_label(&self) -> Lc {
Lc::n(self.group())
}
}

View file

@ -6,14 +6,14 @@ use crate::prelude::*;
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let badge = Badge::labeled(L10n::n("Admin"));
/// let badge = Badge::labeled(Lc::n("Admin"));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Badge {
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
props: Props,
/// Devuelve la etiqueta del badge.
label: L10n,
label: Lc,
}
#[async_trait]
@ -41,7 +41,7 @@ impl Component for Badge {
impl Badge {
/// Crea un badge a partir de la etiqueta indicada.
pub fn labeled(label: L10n) -> Self {
pub fn labeled(label: Lc) -> Self {
Self {
label,
..Default::default()
@ -66,7 +66,7 @@ impl Badge {
/// Establece la etiqueta del badge.
#[builder_fn]
pub fn with_label(mut self, label: L10n) -> Self {
pub fn with_label(mut self, label: Lc) -> Self {
self.label = label;
self
}

View file

@ -9,7 +9,7 @@ pub struct Block {
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
props: Props,
/// Devuelve el título del bloque.
title: L10n,
title: Lc,
/// Devuelve la lista de componentes hijo del bloque.
children: Children,
}
@ -69,7 +69,7 @@ impl Block {
/// Establece el título del bloque.
#[builder_fn]
pub fn with_title(mut self, title: L10n) -> Self {
pub fn with_title(mut self, title: Lc) -> Self {
self.title = title;
self
}

View file

@ -22,9 +22,9 @@ use crate::prelude::*;
/// use pagetop::prelude::*;
///
/// let bc = Breadcrumb::new()
/// .with_crumb(breadcrumb::Crumb::new(L10n::n("Home"), "/"))
/// .with_crumb(breadcrumb::Crumb::new(L10n::n("Users"), "/admin/users"))
/// .with_crumb(breadcrumb::Crumb::current(L10n::n("Julia")));
/// .with_crumb(breadcrumb::Crumb::new(Lc::n("Home"), "/"))
/// .with_crumb(breadcrumb::Crumb::new(Lc::n("Users"), "/admin/users"))
/// .with_crumb(breadcrumb::Crumb::current(Lc::n("Julia")));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Breadcrumb {
@ -56,7 +56,7 @@ impl Component for Breadcrumb {
}
Ok(html! {
nav (self.props()) aria-label=[L10n::l("breadcrumb_label").lookup(cx)] {
nav (self.props()) aria-label=[Lc::l("breadcrumb_label").lookup(cx)] {
ol.breadcrumb {
@for crumb in self.crumbs() {
(crumb.render_crumb(cx))

View file

@ -15,16 +15,16 @@ use crate::prelude::*;
/// use pagetop::prelude::*;
///
/// let bc = Breadcrumb::new()
/// .with_crumb(breadcrumb::Crumb::new(L10n::n("Home"), "/"))
/// .with_crumb(breadcrumb::Crumb::text(L10n::n("Users")))
/// .with_crumb(breadcrumb::Crumb::current(L10n::n("Julia")));
/// .with_crumb(breadcrumb::Crumb::new(Lc::n("Home"), "/"))
/// .with_crumb(breadcrumb::Crumb::text(Lc::n("Users")))
/// .with_crumb(breadcrumb::Crumb::current(Lc::n("Julia")));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Crumb {
/// Devuelve identificador, clases CSS y atributos HTML del elemento.
props: Props,
/// Devuelve la etiqueta del elemento.
label: L10n,
label: Lc,
/// Devuelve la ruta de destino del elemento, si es un enlace.
route: Option<Route>,
/// Devuelve si el elemento representa la página actual.
@ -33,7 +33,7 @@ pub struct Crumb {
impl Crumb {
/// Crea un elemento enlazado a la ruta indicada.
pub fn new(label: L10n, route: impl Into<Route>) -> Self {
pub fn new(label: Lc, route: impl Into<Route>) -> Self {
Self {
label,
route: Some(route.into()),
@ -46,7 +46,7 @@ impl Crumb {
///
/// Al renderizarse dentro de un `Breadcrumb`, el elemento lleva `aria-current="page"` y la
/// clase `.active`.
pub fn current(label: L10n) -> Self {
pub fn current(label: Lc) -> Self {
Self {
label,
is_current: true,
@ -56,7 +56,7 @@ impl Crumb {
/// Crea un elemento de sólo texto, sin enlace ni marca de página actual (por ejemplo, un nivel
/// intermedio sin URL propia).
pub fn text(label: L10n) -> Self {
pub fn text(label: Lc) -> Self {
Self {
label,
..Default::default()

View file

@ -47,9 +47,9 @@ impl fmt::Display for ButtonAction {
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let save = Button::submit(L10n::n("Save"));
/// let cancel = Button::plain(L10n::n("Cancel"));
/// let clear = Button::reset(L10n::n("Clear"));
/// let save = Button::submit(Lc::n("Save"));
/// let cancel = Button::plain(Lc::n("Cancel"));
/// let clear = Button::reset(Lc::n("Clear"));
/// ```
///
/// Cuando el botón activa el envío, el navegador incluye el par `name=value` en los datos del
@ -74,9 +74,9 @@ pub struct Button {
/// Devuelve el valor del botón.
value: AttrValue,
/// Devuelve la etiqueta del botón.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto emergente del botón (atributo `title`).
title: Attr<L10n>,
title: Attr<Lc>,
/// Devuelve si el botón recibe el foco automáticamente al cargar la página.
autofocus: bool,
/// Devuelve si el botón está deshabilitado.
@ -121,7 +121,7 @@ impl Button {
///
/// Es la acción predeterminada al pulsar un botón en la mayoría de los formularios: envía los
/// datos al servidor.
pub fn submit(label: L10n) -> Self {
pub fn submit(label: Lc) -> Self {
Self {
kind: ButtonAction::Submit,
label: Attr::some(label),
@ -132,7 +132,7 @@ impl Button {
/// Crea un botón de **restablecimiento** (`type="reset"`).
///
/// Al pulsarlo, devuelve todos los campos del formulario a sus valores iniciales.
pub fn reset(label: L10n) -> Self {
pub fn reset(label: Lc) -> Self {
Self {
kind: ButtonAction::Reset,
label: Attr::some(label),
@ -144,7 +144,7 @@ impl Button {
///
/// No tiene un comportamiento predeterminado sobre el formulario. Su comportamiento puede
/// definirse mediante JavaScript.
pub fn plain(label: L10n) -> Self {
pub fn plain(label: Lc) -> Self {
Self {
kind: ButtonAction::Plain,
label: Attr::some(label),
@ -190,14 +190,14 @@ impl Button {
/// Establece o elimina la etiqueta visible del botón (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto emergente del botón (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_title(mut self, title: impl Into<Option<L10n>>) -> Self {
pub fn with_title(mut self, title: impl Into<Option<Lc>>) -> Self {
self.title.alter_opt(title.into());
self
}

View file

@ -14,14 +14,14 @@ use crate::prelude::*;
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let item = form::check::Item::new("apple", L10n::n("Apple")).with_checked(true);
/// let item = form::check::Item::new("apple", Lc::n("Apple")).with_checked(true);
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Item {
/// Devuelve el valor enviado al servidor cuando la casilla está marcada.
value: AttrValue,
/// Devuelve la etiqueta de la casilla.
label: L10n,
label: Lc,
/// Devuelve si la casilla debe aparecer marcada por defecto.
checked: bool,
/// Devuelve si la casilla está deshabilitada.
@ -30,7 +30,7 @@ pub struct Item {
impl Item {
/// Crea una nueva casilla con el valor y la etiqueta indicados.
pub fn new(value: impl AsRef<str>, label: L10n) -> Self {
pub fn new(value: impl AsRef<str>, label: Lc) -> Self {
Self {
value: AttrValue::new(value),
label,
@ -70,10 +70,10 @@ impl Item {
///
/// let interests = form::check::Field::new()
/// .with_name("interests")
/// .with_label(L10n::n("Areas of interest"))
/// .with_item(form::check::Item::new("art", L10n::n("Art")))
/// .with_item(form::check::Item::new("tech", L10n::n("Technology")))
/// .with_item(form::check::Item::new("science", L10n::n("Science")).with_checked(true));
/// .with_label(Lc::n("Areas of interest"))
/// .with_item(form::check::Item::new("art", Lc::n("Art")))
/// .with_item(form::check::Item::new("tech", Lc::n("Technology")))
/// .with_item(form::check::Item::new("science", Lc::n("Science")).with_checked(true));
/// ```
///
/// El navegador envía una entrada por cada casilla marcada, todas bajo la misma clave (por ejemplo,
@ -101,9 +101,9 @@ pub struct Field {
/// Devuelve el nombre compartido por todas las casillas del grupo.
name: AttrName,
/// Devuelve la etiqueta del grupo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del grupo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve las casillas del grupo.
items: Vec<Item>,
/// Devuelve si todo el grupo está deshabilitado.
@ -207,14 +207,14 @@ impl Field {
/// Establece o elimina la etiqueta visible del grupo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del grupo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}

View file

@ -16,12 +16,12 @@ use crate::prelude::*;
///
/// let accept_terms = form::Checkbox::new()
/// .with_name("terms_accepted")
/// .with_label(L10n::n("I accept the terms and conditions"))
/// .with_label(Lc::n("I accept the terms and conditions"))
/// .with_required(true);
///
/// let notifications = form::Checkbox::switch()
/// .with_name("notifications_enabled")
/// .with_label(L10n::n("Receive email notifications"))
/// .with_label(Lc::n("Receive email notifications"))
/// .with_checked(true);
/// ```
///
@ -46,7 +46,7 @@ pub struct Checkbox {
/// Devuelve el nombre del campo.
name: AttrName,
/// Devuelve la etiqueta del control.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve si el control debe estar marcado/activo por defecto.
checked: bool,
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
@ -123,7 +123,7 @@ impl Component for Checkbox {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -184,7 +184,7 @@ impl Checkbox {
/// Establece o elimina la etiqueta visible del control (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}

View file

@ -24,22 +24,22 @@ use crate::base::component::form;
/// .with_child(
/// form::input::Field::email()
/// .with_name("email")
/// .with_label(L10n::n("Email"))
/// .with_label(Lc::n("Email"))
/// .with_required(true),
/// )
/// .with_child(
/// form::input::Field::password()
/// .with_name("password")
/// .with_label(L10n::n("Password"))
/// .with_label(Lc::n("Password"))
/// .with_required(true),
/// )
/// .with_child(
/// form::Checkbox::check()
/// .with_name("remember")
/// .with_label(L10n::n("Remember me")),
/// .with_label(Lc::n("Remember me")),
/// )
/// .with_child(
/// Button::submit(L10n::n("Sign in"))
/// Button::submit(Lc::n("Sign in"))
/// );
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]

View file

@ -17,19 +17,19 @@ use crate::prelude::*;
/// use pagetop::prelude::*;
///
/// let personal_data = form::Fieldset::new()
/// .with_legend(L10n::n("Personal data"))
/// .with_description(L10n::n("Enter your full name and contact email."))
/// .with_child(form::input::Field::text().with_name("name").with_label(L10n::n("Full name")))
/// .with_child(form::input::Field::email().with_name("email").with_label(L10n::n("Email")));
/// .with_legend(Lc::n("Personal data"))
/// .with_description(Lc::n("Enter your full name and contact email."))
/// .with_child(form::input::Field::text().with_name("name").with_label(Lc::n("Full name")))
/// .with_child(form::input::Field::email().with_name("email").with_label(Lc::n("Email")));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Fieldset {
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
props: Props,
/// Devuelve la leyenda del `fieldset`.
legend: Attr<L10n>,
legend: Attr<Lc>,
/// Devuelve la descripción del `fieldset`.
description: Attr<L10n>,
description: Attr<Lc>,
/// Devuelve si el `fieldset` está deshabilitado.
disabled: bool,
/// Devuelve la lista de componentes del `fieldset`.
@ -86,14 +86,14 @@ impl Fieldset {
/// Establece o elimina la leyenda del `fieldset` (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_legend(mut self, legend: impl Into<Option<L10n>>) -> Self {
pub fn with_legend(mut self, legend: impl Into<Option<Lc>>) -> Self {
self.legend.alter_opt(legend.into());
self
}
/// Establece o elimina la descripción del `fieldset` (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_description(mut self, description: impl Into<Option<L10n>>) -> Self {
pub fn with_description(mut self, description: impl Into<Option<Lc>>) -> Self {
self.description.alter_opt(description.into());
self
}

View file

@ -135,8 +135,8 @@ impl fmt::Display for Mode {
///
/// let email = form::input::Field::email()
/// .with_name("email")
/// .with_label(L10n::n("Email address"))
/// .with_placeholder(L10n::n("user@example.com"))
/// .with_label(Lc::n("Email address"))
/// .with_placeholder(Lc::n("user@example.com"))
/// .with_autocomplete(Some(form::Autocomplete::email()))
/// .with_required(true);
/// ```
@ -161,15 +161,15 @@ pub struct Field {
/// Devuelve el valor inicial del campo.
value: AttrValue,
/// Devuelve la etiqueta del campo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del campo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve la longitud mínima permitida en caracteres.
minlength: Attr<u16>,
/// Devuelve la longitud máxima permitida en caracteres.
maxlength: Attr<u16>,
/// Devuelve el texto indicativo del campo.
placeholder: Attr<L10n>,
placeholder: Attr<Lc>,
/// Devuelve la configuración de autocompletado del campo.
autocomplete: Attr<form::Autocomplete>,
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
@ -238,7 +238,7 @@ impl Component for Field {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -420,14 +420,14 @@ impl Field {
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}
@ -449,9 +449,9 @@ impl Field {
/// Establece o elimina el texto indicativo del campo (`None` para quitarlo).
///
/// Este texto aparece en el mismo campo y desaparece en cuanto el usuario empieza a escribir.
/// Al ser texto visible para el usuario se acepta [`L10n`] para poder localizarlo.
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
#[builder_fn]
pub fn with_placeholder(mut self, placeholder: impl Into<Option<L10n>>) -> Self {
pub fn with_placeholder(mut self, placeholder: impl Into<Option<Lc>>) -> Self {
self.placeholder.alter_opt(placeholder.into());
self
}

View file

@ -13,7 +13,7 @@ use crate::prelude::*;
///
/// let quantity = form::Number::new()
/// .with_name("quantity")
/// .with_label(L10n::n("Quantity"))
/// .with_label(Lc::n("Quantity"))
/// .with_min(Some(1))
/// .with_max(Some(100))
/// .with_value(Some(1));
@ -37,9 +37,9 @@ pub struct Number {
/// Devuelve el valor inicial del campo.
value: Attr<u64>,
/// Devuelve la etiqueta del campo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del campo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve el valor mínimo permitido.
min: Attr<u64>,
/// Devuelve el valor máximo permitido.
@ -89,7 +89,7 @@ impl Component for Number {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -153,14 +153,14 @@ impl Number {
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}

View file

@ -15,14 +15,14 @@ use crate::prelude::*;
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let item = form::radio::Item::new("monthly", L10n::n("Monthly")).with_checked(true);
/// let item = form::radio::Item::new("monthly", Lc::n("Monthly")).with_checked(true);
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Item {
/// Devuelve el valor enviado al servidor cuando la opción está seleccionada.
value: AttrValue,
/// Devuelve la etiqueta de la opción.
label: L10n,
label: Lc,
/// Devuelve si la opción debe aparecer seleccionada por defecto.
checked: bool,
/// Devuelve si la opción está deshabilitada.
@ -31,7 +31,7 @@ pub struct Item {
impl Item {
/// Crea una nueva opción con el valor y la etiqueta indicados.
pub fn new(value: impl AsRef<str>, label: L10n) -> Self {
pub fn new(value: impl AsRef<str>, label: Lc) -> Self {
Self {
value: AttrValue::new(value),
label,
@ -77,9 +77,9 @@ impl Item {
///
/// let plan = form::radio::Field::new()
/// .with_name("plan")
/// .with_label(L10n::n("Subscription plan"))
/// .with_item(form::radio::Item::new("monthly", L10n::n("Monthly")))
/// .with_item(form::radio::Item::new("annual", L10n::n("Annual")).with_checked(true))
/// .with_label(Lc::n("Subscription plan"))
/// .with_item(form::radio::Item::new("monthly", Lc::n("Monthly")))
/// .with_item(form::radio::Item::new("annual", Lc::n("Annual")).with_checked(true))
/// .with_required(true);
/// ```
///
@ -99,9 +99,9 @@ pub struct Field {
/// Devuelve el nombre compartido por todos los botones de opción del grupo.
name: AttrName,
/// Devuelve la etiqueta del grupo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del grupo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve las opciones del grupo.
items: Vec<Item>,
/// Devuelve si la selección de alguna opción del grupo es obligatoria.
@ -150,7 +150,7 @@ impl Component for Field {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -227,14 +227,14 @@ impl Field {
/// Establece o elimina la etiqueta visible del grupo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del grupo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}

View file

@ -13,7 +13,7 @@ use crate::prelude::*;
///
/// let volume = form::Range::new()
/// .with_name("volume")
/// .with_label(L10n::n("Volume"))
/// .with_label(Lc::n("Volume"))
/// .with_min(Some(0.0))
/// .with_max(Some(100.0))
/// .with_step(Some(5.0))
@ -38,9 +38,9 @@ pub struct Range {
/// Devuelve el valor inicial del campo.
value: Attr<f64>,
/// Devuelve la etiqueta del campo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del campo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve el valor mínimo permitido.
min: Attr<f64>,
/// Devuelve el valor máximo permitido.
@ -141,14 +141,14 @@ impl Range {
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}

View file

@ -18,14 +18,14 @@ use crate::prelude::*;
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let item = form::select::Item::new("es", L10n::n("Spanish")).with_selected(true);
/// let item = form::select::Item::new("es", Lc::n("Spanish")).with_selected(true);
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Item {
/// Devuelve el valor enviado al servidor cuando se selecciona el elemento.
value: AttrValue,
/// Devuelve la etiqueta visible del elemento.
label: L10n,
label: Lc,
/// Devuelve si el elemento debe aparecer seleccionado por defecto.
selected: bool,
/// Devuelve si el elemento está deshabilitado.
@ -34,7 +34,7 @@ pub struct Item {
impl Item {
/// Crea un nuevo elemento con el valor y la etiqueta indicados.
pub fn new(value: impl AsRef<str>, label: L10n) -> Self {
pub fn new(value: impl AsRef<str>, label: Lc) -> Self {
Self {
value: AttrValue::new(value),
label,
@ -74,14 +74,14 @@ impl Item {
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let group = form::select::Group::new(L10n::n("Europe"))
/// .with_item(form::select::Item::new("es", L10n::n("Spanish")))
/// .with_item(form::select::Item::new("fr", L10n::n("French")));
/// let group = form::select::Group::new(Lc::n("Europe"))
/// .with_item(form::select::Item::new("es", Lc::n("Spanish")))
/// .with_item(form::select::Item::new("fr", Lc::n("French")));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Group {
/// Devuelve la etiqueta visible del grupo de elementos.
label: L10n,
label: Lc,
/// Devuelve los elementos del grupo.
items: Vec<Item>,
/// Devuelve si el grupo de elementos está deshabilitado.
@ -90,7 +90,7 @@ pub struct Group {
impl Group {
/// Crea un nuevo grupo con la etiqueta indicada.
pub fn new(label: L10n) -> Self {
pub fn new(label: Lc) -> Self {
Self {
label,
..Self::default()
@ -149,17 +149,17 @@ pub enum Entry {
///
/// let idioma = form::select::Field::new()
/// .with_name("language")
/// .with_label(L10n::n("Language"))
/// .with_item(form::select::Item::new("", L10n::n("— Choose —")).with_selected(true))
/// .with_label(Lc::n("Language"))
/// .with_item(form::select::Item::new("", Lc::n("— Choose —")).with_selected(true))
/// .with_group(
/// form::select::Group::new(L10n::n("Europe"))
/// .with_item(form::select::Item::new("es", L10n::n("Spanish")))
/// .with_item(form::select::Item::new("fr", L10n::n("French"))),
/// form::select::Group::new(Lc::n("Europe"))
/// .with_item(form::select::Item::new("es", Lc::n("Spanish")))
/// .with_item(form::select::Item::new("fr", Lc::n("French"))),
/// )
/// .with_group(
/// form::select::Group::new(L10n::n("Americas"))
/// .with_item(form::select::Item::new("en", L10n::n("English")))
/// .with_item(form::select::Item::new("pt", L10n::n("Portuguese"))),
/// form::select::Group::new(Lc::n("Americas"))
/// .with_item(form::select::Item::new("en", Lc::n("English")))
/// .with_item(form::select::Item::new("pt", Lc::n("Portuguese"))),
/// )
/// .with_required(true);
/// ```
@ -193,9 +193,9 @@ pub struct Field {
/// Devuelve el nombre del campo.
name: AttrName,
/// Devuelve la etiqueta del campo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del campo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve las entradas de la lista (elementos individuales y grupos de elementos).
entries: Vec<Entry>,
/// Devuelve si la lista permite selección múltiple.
@ -246,7 +246,7 @@ impl Component for Field {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -331,14 +331,14 @@ impl Field {
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}

View file

@ -15,10 +15,10 @@ use crate::prelude::*;
///
/// let descripcion = form::Textarea::new()
/// .with_name("description")
/// .with_label(L10n::n("Description"))
/// .with_label(Lc::n("Description"))
/// .with_rows(Some(8))
/// .with_maxlength(Some(500))
/// .with_placeholder(L10n::n("Write here..."))
/// .with_placeholder(Lc::n("Write here..."))
/// .with_required(true);
/// ```
///
@ -40,9 +40,9 @@ pub struct Textarea {
/// Devuelve el valor inicial del área de texto.
value: AttrValue,
/// Devuelve la etiqueta del campo.
label: Attr<L10n>,
label: Attr<Lc>,
/// Devuelve el texto de ayuda del campo.
help_text: Attr<L10n>,
help_text: Attr<Lc>,
/// Devuelve el número de filas visibles del área de texto.
rows: Attr<u16>,
/// Devuelve la longitud mínima permitida en caracteres.
@ -50,7 +50,7 @@ pub struct Textarea {
/// Devuelve la longitud máxima permitida en caracteres.
maxlength: Attr<u16>,
/// Devuelve el texto indicativo del área de texto.
placeholder: Attr<L10n>,
placeholder: Attr<Lc>,
/// Devuelve la configuración de autocompletado del campo.
autocomplete: Attr<form::Autocomplete>,
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
@ -99,7 +99,7 @@ impl Component for Textarea {
@if *self.required() {
span
class="form-required"
title=[L10n::l("field_required").lookup(cx)]
title=[Lc::l("field_required").lookup(cx)]
{
"*"
}
@ -168,14 +168,14 @@ impl Textarea {
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
#[builder_fn]
pub fn with_label(mut self, label: impl Into<Option<L10n>>) -> Self {
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
self.label.alter_opt(label.into());
self
}
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
#[builder_fn]
pub fn with_help_text(mut self, help_text: impl Into<Option<L10n>>) -> Self {
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
self.help_text.alter_opt(help_text.into());
self
}
@ -207,9 +207,9 @@ impl Textarea {
/// Establece o elimina el texto indicativo del área de texto (`None` para quitarlo).
///
/// Este texto aparece en el área de texto y desaparece en cuanto el usuario empieza a escribir.
/// Al ser texto visible para el usuario se acepta [`L10n`] para poder localizarlo.
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
#[builder_fn]
pub fn with_placeholder(mut self, placeholder: impl Into<Option<L10n>>) -> Self {
pub fn with_placeholder(mut self, placeholder: impl Into<Option<Lc>>) -> Self {
self.placeholder.alter_opt(placeholder.into());
self
}

View file

@ -44,10 +44,10 @@ pub enum IntroOpening {
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let intro = Intro::default()
/// .with_title(L10n::l("intro_custom_title"))
/// .with_slogan(L10n::l("intro_custom_slogan"))
/// .with_title(Lc::l("intro_custom_title"))
/// .with_slogan(Lc::l("intro_custom_slogan"))
/// .with_button(Some((
/// L10n::l("intro_learn_more"),
/// Lc::l("intro_learn_more"),
/// "/learn-more".into()
/// )));
/// ```
@ -57,7 +57,7 @@ pub enum IntroOpening {
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let intro = Intro::default()
/// .with_button(None::<(L10n, Route)>)
/// .with_button(None::<(Lc, Route)>)
/// .with_opening(IntroOpening::Custom);
/// ```
///
@ -68,13 +68,13 @@ pub enum IntroOpening {
/// let intro = Intro::default()
/// .with_child(
/// Block::new()
/// .with_title(L10n::l("intro_custom_block_title"))
/// .with_title(Lc::l("intro_custom_block_title"))
/// .with_child(Html::with(move |cx| {
/// html! {
/// p class="intro-text-lead" {
/// (L10n::l("intro_custom_paragraph_1").using(cx))
/// (Lc::l("intro_custom_paragraph_1").using(cx))
/// }
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
/// p { (Lc::l("intro_custom_paragraph_2").using(cx)) }
/// }
/// })),
/// );
@ -82,11 +82,11 @@ pub enum IntroOpening {
#[derive(Clone, Debug, Getters)]
pub struct Intro {
/// Devuelve el título de entrada.
title: L10n,
title: Lc,
/// Devuelve el eslogan de la entrada.
slogan: L10n,
slogan: Lc,
/// Devuelve el botón de llamada a la acción, si existe.
button: Option<(L10n, Route)>,
button: Option<(Lc, Route)>,
/// Devuelve el modo de apertura configurado.
opening: IntroOpening,
/// Devuelve la lista de componentes hijo de la intro.
@ -98,9 +98,9 @@ impl Default for Intro {
const BUTTON_LINK: &str = "https://pagetop.cillero.es";
Intro {
title: L10n::l("intro_default_title"),
slogan: L10n::l("intro_default_slogan").with_arg("app", &global::SETTINGS.app.name),
button: Some((L10n::l("intro_default_button"), BUTTON_LINK.into())),
title: Lc::l("intro_default_title"),
slogan: Lc::l("intro_default_slogan").with_arg("app", &global::SETTINGS.app.name),
button: Some((Lc::l("intro_default_button"), BUTTON_LINK.into())),
opening: IntroOpening::default(),
children: Children::default(),
}
@ -133,7 +133,7 @@ impl Component for Intro {
}
"#)
.replace("LANGID", cx.langid().to_string().as_str())
.replace("LABEL", L10n::l("intro_release_label").using(cx).as_str())
.replace("LABEL", Lc::l("intro_release_label").using(cx).as_str())
)));
}
@ -173,24 +173,24 @@ impl Component for Intro {
div class="intro-text-body" {
@if *self.opening() == IntroOpening::PageTop {
p class="intro-text-lead" {
(L10n::l("intro_text1").using(cx))
(Lc::l("intro_text1").using(cx))
}
div id="intro-badges" {
img
src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge"
alt=[L10n::l("intro_pagetop_label").lookup(cx)] {} (" ")
alt=[Lc::l("intro_pagetop_label").lookup(cx)] {} (" ")
img
id="intro-release"
alt=[L10n::l("intro_release_label").lookup(cx)] {} (" ")
alt=[Lc::l("intro_release_label").lookup(cx)] {} (" ")
img
src=(format!(
"https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label={}&style=for-the-badge",
L10n::l("intro_license_label").lookup(cx).unwrap_or_default()
Lc::l("intro_license_label").lookup(cx).unwrap_or_default()
))
alt=[L10n::l("intro_license_label").lookup(cx)] {}
alt=[Lc::l("intro_license_label").lookup(cx)] {}
}
p class="intro-text-lead" {
(L10n::l("intro_text2").using(cx))
(Lc::l("intro_text2").using(cx))
}
}
(self.children().render(cx).await)
@ -206,8 +206,8 @@ impl Component for Intro {
div class="intro-footer-links" {
a href="https://crates.io/crates/pagetop" target="_blank" rel="noopener noreferrer" { ("Crates.io") }
a href="https://docs.rs/pagetop" target="_blank" rel="noopener noreferrer" { ("Docs.rs") }
a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noopener noreferrer" { (L10n::l("intro_code").using(cx)) }
em { (L10n::l("intro_have_fun").using(cx)) }
a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noopener noreferrer" { (Lc::l("intro_code").using(cx)) }
em { (Lc::l("intro_have_fun").using(cx)) }
}
}
}
@ -225,10 +225,10 @@ impl Intro {
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let intro = Intro::default().with_title(L10n::n("Intro title"));
/// let intro = Intro::default().with_title(Lc::n("Intro title"));
/// ```
#[builder_fn]
pub fn with_title(mut self, title: L10n) -> Self {
pub fn with_title(mut self, title: Lc) -> Self {
self.title = title;
self
}
@ -239,10 +239,10 @@ impl Intro {
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let intro = Intro::default().with_slogan(L10n::n("A short slogan"));
/// let intro = Intro::default().with_slogan(Lc::n("A short slogan"));
/// ```
#[builder_fn]
pub fn with_slogan(mut self, slogan: L10n) -> Self {
pub fn with_slogan(mut self, slogan: Lc) -> Self {
self.slogan = slogan;
self
}
@ -258,12 +258,12 @@ impl Intro {
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// // Define un botón con texto y una ruta interna (preserva `lang` si corresponde).
/// let intro = Intro::default().with_button(Some((L10n::n("Start"), "/start".into())));
/// let intro = Intro::default().with_button(Some((Lc::n("Start"), "/start".into())));
/// // Descarta el botón de la intro.
/// let intro_no_button = Intro::default().with_button(None);
/// ```
#[builder_fn]
pub fn with_button(mut self, button: Option<(L10n, Route)>) -> Self {
pub fn with_button(mut self, button: Option<(Lc, Route)>) -> Self {
self.button = button;
self
}

View file

@ -29,8 +29,8 @@ use std::fmt;
/// "sidebar"
/// }
///
/// fn label(&self) -> L10n {
/// L10n::n("Sidebar")
/// fn label(&self) -> Lc {
/// Lc::n("Sidebar")
/// }
/// }
///

View file

@ -163,8 +163,8 @@ pub struct Pager {
/// Devuelve la visibilidad del formulario para saltar directamente a una página.
jump: PagerVisibility,
/// Devuelve la etiqueta de accesibilidad (`aria-label`) del elemento `<nav>`.
#[default(L10n::l("pager_aria_label"))]
aria_label: L10n,
#[default(Lc::l("pager_aria_label"))]
aria_label: Lc,
}
// Elemento visible del listado de páginas: un número de página o una elipsis que resume un tramo
@ -241,7 +241,7 @@ impl Component for Pager {
@let first = (page - 1) * items_per_page + 1;
@let last = (page * items_per_page).min(self.total_items());
span.pager-summary {
(L10n::l("pager_summary")
(Lc::l("pager_summary")
.with_arg("first", first.to_string())
.with_arg("last", last.to_string())
.with_arg("total", self.total_items().to_string())
@ -254,8 +254,8 @@ impl Component for Pager {
a.page-link
href=[(!first_disabled).then(|| Self::page_route(&route, page - 1))]
aria-disabled=[first_disabled.then_some("true")]
aria-label=[L10n::l("pager_previous_aria_label").lookup(cx)] {
span.page-link-icon { (L10n::l("pager_previous_label").using(cx)) }
aria-label=[Lc::l("pager_previous_aria_label").lookup(cx)] {
span.page-link-icon { (Lc::l("pager_previous_label").using(cx)) }
}
}
}
@ -283,8 +283,8 @@ impl Component for Pager {
a.page-link
href=[(!last_disabled).then(|| Self::page_route(&route, page + 1))]
aria-disabled=[last_disabled.then_some("true")]
aria-label=[L10n::l("pager_next_aria_label").lookup(cx)] {
span.page-link-icon { (L10n::l("pager_next_label").using(cx)) }
aria-label=[Lc::l("pager_next_aria_label").lookup(cx)] {
span.page-link-icon { (Lc::l("pager_next_label").using(cx)) }
}
}
}
@ -324,9 +324,9 @@ impl Component for Pager {
.with_value(Some(page)),
)
.with_child(
Button::submit(L10n::l("pager_goto_button"))
Button::submit(Lc::l("pager_goto_button"))
.with_prop(PropsOp::add_classes("pager-jump-button"))
.with_title(L10n::l("pager_goto_label")),
.with_title(Lc::l("pager_goto_label")),
)
.render(cx).await
}) }
@ -443,7 +443,7 @@ impl Pager {
/// Establece la etiqueta de accesibilidad (`aria-label`) del elemento `<nav>`. Por defecto es
/// "Page navigation" (clave `pager_aria_label`), igual que hace el paginador de Bootstrap.
#[builder_fn]
pub fn with_aria_label(mut self, aria_label: L10n) -> Self {
pub fn with_aria_label(mut self, aria_label: Lc) -> Self {
self.aria_label = aria_label;
self
}

View file

@ -1,7 +1,14 @@
use crate::prelude::*;
// Enlace a la página oficial de PageTop.
const LINK: &str = "<a href=\"https://pagetop.cillero.es\" rel=\"noopener noreferrer\">PageTop</a>";
const PAGETOP_LINK: &str = concat!(
"<a ",
"href=\"https://pagetop.cillero.es\" ",
"target=\"_blank\" ",
"rel=\"noopener noreferrer\">",
"PageTop",
"</a>",
);
/// Componente que muestra el típico mensaje *Powered by* (*Funciona con*) en el pie de página.
///
@ -33,7 +40,7 @@ impl Component for PoweredBy {
span class="poweredby__copyright" { (c) "." } " "
}
span class="poweredby__pagetop" {
(L10n::l("poweredby_pagetop").with_arg("pagetop_link", LINK).using(cx))
(Lc::l("poweredby_pagetop").with_arg("pagetop_link", PAGETOP_LINK).using(cx))
}
}
})

View file

@ -13,12 +13,12 @@ pub struct Welcome;
#[async_trait]
impl Extension for Welcome {
fn name(&self) -> L10n {
L10n::l("welcome_extension_name")
fn name(&self) -> Lc {
Lc::l("welcome_extension_name")
}
fn description(&self) -> L10n {
L10n::l("welcome_extension_description")
fn description(&self) -> Lc {
Lc::l("welcome_extension_description")
}
fn configure_router(&self, router: Router) -> Router {
@ -30,33 +30,33 @@ async fn home(request: HttpRequest) -> Result<Markup, ErrorPage> {
let app = &global::SETTINGS.app.name;
Page::new(request)
.with_title(L10n::l("welcome_title"))
.with_title(Lc::l("welcome_title"))
.with_child(
Intro::new()
.with_child(
Block::new()
.with_title(L10n::l("welcome_status_title"))
.with_title(Lc::l("welcome_status_title"))
.with_child(Html::with(move |cx| {
html! {
p class="intro-text-lead" {
(L10n::l("welcome_status_1").using(cx))
(Lc::l("welcome_status_1").using(cx))
}
p class="intro-text-lead" {
(L10n::l("welcome_status_2").using(cx))
(Lc::l("welcome_status_2").using(cx))
}
}
})),
)
.with_child(
Block::new()
.with_title(L10n::l("welcome_support_title"))
.with_title(Lc::l("welcome_support_title"))
.with_child(Html::with(move |cx| {
html! {
p class="intro-text-lead" {
(L10n::l("welcome_support_1").using(cx))
(Lc::l("welcome_support_1").using(cx))
}
p class="intro-text-lead" {
(L10n::l("welcome_support_2").with_arg("app", app).using(cx))
(Lc::l("welcome_support_2").with_arg("app", app).using(cx))
}
}
})),

View file

@ -206,9 +206,9 @@ impl<C: Component> Embed<C> {
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let mut embed = Embed::with(Block::new().with_title(L10n::n("Title")));
/// let mut embed = Embed::with(Block::new().with_title(Lc::n("Title")));
/// if let Some(component) = embed.get_mut() {
/// component.alter_title(L10n::n("New Title"));
/// component.alter_title(Lc::n("New Title"));
/// }
/// ```
pub fn get_mut(&mut self) -> Option<&mut C>

View file

@ -6,7 +6,7 @@ use crate::core::theme::{ChildrenInRegions, CoreRegions, CoreTemplates};
use crate::core::theme::{RegionRef, TemplateRef, ThemeRef};
use crate::html::{Assets, Favicon, JavaScript, Preload, StyleSheet};
use crate::html::{Markup, Props, PropsOp, RoutePath, html};
use crate::locale::L10n;
use crate::locale::Lc;
use crate::locale::{LangId, LanguageIdentifier, RequestLocale};
use crate::web::HttpRequest;
use crate::{builder_fn, util};
@ -509,9 +509,9 @@ impl Context {
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// # let mut cx = Context::default();
/// cx.push_message(MessageLevel::Warning, L10n::n("Session is not valid"));
/// cx.push_message(MessageLevel::Warning, Lc::n("Session is not valid"));
/// ```
pub fn push_message(&mut self, level: MessageLevel, text: L10n) {
pub fn push_message(&mut self, level: MessageLevel, text: Lc) {
self.messages.push(StatusMessage::new(level, text));
}
@ -532,13 +532,13 @@ impl Context {
/// global de idioma de la aplicación, la cabecera `Accept-Language` y/o el idioma de respaldo.
///
/// Todo ello según la negociación indicada en [`global::SETTINGS.app.lang_negotiation`]. Esto
/// permite que el [`Context`] se use como fuente de idioma coherente en [`L10n::lookup()`] o
/// [`L10n::using()`].
/// permite que el [`Context`] se use como fuente de idioma coherente en [`Lc::lookup()`] o
/// [`Lc::using()`].
///
/// [`Context`]: crate::core::component::Context
/// [`global::SETTINGS.app.lang_negotiation`]: crate::global::App::lang_negotiation
/// [`L10n::lookup()`]: crate::locale::L10n::lookup
/// [`L10n::using()`]: crate::locale::L10n::using
/// [`Lc::lookup()`]: crate::locale::Lc::lookup
/// [`Lc::using()`]: crate::locale::Lc::using
impl LangId for Context {
#[inline]
fn langid(&self) -> &'static LanguageIdentifier {

View file

@ -111,28 +111,33 @@ pub trait Component: AnyInfo + ComponentClone + ComponentRender + Send + Sync {
/// Genera el marcado HTML del componente cuando ningún tema lo sobrescribe.
///
/// Este es el cuarto paso del [ciclo de renderizado](ComponentRender) tras llamar al
/// [`setup()`](Self::setup) del componente y despachar la acción
/// [`BeforeRender`](crate::base::action::component::BeforeRender) que atiende los cambios de
/// [`setup()`] del componente y despachar la acción [`BeforeRender`] que atiende los cambios de
/// otras extensiones antes de renderizar. Se invoca sólo si ningún tema en la cadena devuelve
/// `Some` en [`Theme::handle_component()`](crate::core::theme::Theme::handle_component) para
/// este componente.
/// `Some` en [`Theme::handle_component()`] para este componente.
///
/// Es `async`, a diferencia de [`setup()`](Self::setup), para permitir a los componentes
/// realizar aquí sus llamadas asíncronas, como consultas a base de datos, peticiones a
/// servicios externos, o cualquier operación de E/S, que necesiten para preparar su contenido.
/// Es `async`, a diferencia de [`setup()`], para permitir a los componentes realizar aquí sus
/// llamadas asíncronas, como consultas a base de datos, peticiones a servicios externos, o
/// cualquier operación de E/S, que necesiten para preparar su contenido.
///
/// Se recomienda obtener los datos del componente a través de sus propios métodos para que los
/// temas puedan implementar `handle_component()` sin depender de los detalles internos.
///
/// Los campos que representen contenido no deben almacenar [`Markup`] ya generado, sino que
/// guardarán el dato en bruto, por ejemplo [`L10n`](crate::locale::L10n) para textos
/// traducibles, o un componente anidado como [`Html`](crate::base::component::Html), o
/// cualquier otro `Component`, normalmente a través de [`Embed`](crate::core::component::Embed)
/// o [`Child`](crate::core::component::Child); y será este método quien lo convierta en HTML en
/// el momento del renderizado, no quien construye la instancia.
/// guardarán el dato en bruto, por ejemplo [`Lc`] para textos traducibles, o un componente
/// anidado como [`Html`], o cualquier otro `Component`, normalmente a través de [`Embed`] o
/// [`Child`]; y será este método quien lo convierta en HTML en el momento del renderizado, no
/// quien construye la instancia.
///
/// Por defecto, devuelve un [`Markup`] vacío (`Ok(html! {})`). En caso de error, devuelve un
/// [`ComponentError`] que puede incluir un marcado alternativo (*fallback*).
///
/// [`setup()`]: Self::setup
/// [`BeforeRender`]: crate::base::action::component::BeforeRender
/// [`Theme::handle_component()`]: crate::core::theme::Theme::handle_component
/// [`Lc`]: crate::locale::Lc
/// [`Html`]: crate::base::component::Html
/// [`Embed`]: crate::core::component::Embed
/// [`Child`]: crate::core::component::Child
#[allow(unused_variables)]
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
Ok(html! {})

View file

@ -1,4 +1,4 @@
use crate::locale::L10n;
use crate::locale::Lc;
use crate::{AutoDefault, Getters};
/// Nivel de severidad de un [`StatusMessage`].
@ -20,30 +20,30 @@ pub enum MessageLevel {
/// puede generarse en cualquier punto del procesamiento de una petición web (handlers, renderizado,
/// lógica de negocio, etc.).
///
/// El texto se almacena como [`L10n`] para resolverse con el idioma del contexto en el momento de
/// la visualización.
/// El texto se almacena como [`Lc`] para resolverse con el idioma del contexto en el momento de la
/// visualización.
///
/// # Ejemplo
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// // Mensaje informativo con clave traducible.
/// let info = StatusMessage::new(MessageLevel::Info, L10n::l("saved-successfully"));
/// let info = StatusMessage::new(MessageLevel::Info, Lc::l("saved-successfully"));
///
/// // Aviso con texto literal sin traducción.
/// let warn = StatusMessage::new(MessageLevel::Warning, L10n::n("Incomplete form."));
/// let warn = StatusMessage::new(MessageLevel::Warning, Lc::n("Incomplete form."));
/// ```
#[derive(Debug, Getters)]
pub struct StatusMessage {
/// Nivel de severidad del mensaje.
level: MessageLevel,
/// Texto del mensaje.
text: L10n,
text: Lc,
}
impl StatusMessage {
/// Crea un nuevo mensaje de usuario con el nivel y texto indicados.
pub fn new(level: MessageLevel, text: L10n) -> Self {
pub fn new(level: MessageLevel, text: Lc) -> Self {
StatusMessage { level, text }
}
}

View file

@ -3,7 +3,7 @@ use crate::async_trait;
use crate::core::AnyInfo;
use crate::core::action::ActionBox;
use crate::core::theme::ThemeRef;
use crate::locale::L10n;
use crate::locale::Lc;
use crate::web::Router;
/// Interfaz común que debe implementar cualquier extensión de PageTop.
@ -17,12 +17,12 @@ use crate::web::Router;
///
/// #[async_trait]
/// impl Extension for MyExtension {
/// fn name(&self) -> L10n {
/// L10n::n("My Extension")
/// fn name(&self) -> Lc {
/// Lc::n("My Extension")
/// }
///
/// fn description(&self) -> L10n {
/// L10n::n("Does something useful")
/// fn description(&self) -> Lc {
/// Lc::n("Does something useful")
/// }
/// }
/// ```
@ -32,15 +32,15 @@ pub trait Extension: AnyInfo + Send + Sync {
///
/// Predeterminado por el [`short_name()`](AnyInfo::short_name) del tipo asociado a la
/// extensión.
fn name(&self) -> L10n {
L10n::n(self.short_name())
fn name(&self) -> Lc {
Lc::n(self.short_name())
}
/// Descripción corta de la extensión como *texto localizado* para paneles, listados, etc.
///
/// Por defecto devuelve un valor vacío (`L10n::default()`).
fn description(&self) -> L10n {
L10n::default()
/// Por defecto devuelve un valor vacío (`Lc::default()`).
fn description(&self) -> Lc {
Lc::default()
}
/// Devuelve una referencia a esta misma extensión cuando actúa como un tema.

View file

@ -119,7 +119,7 @@
use crate::AutoDefault;
use crate::core::AnyInfo;
use crate::locale::L10n;
use crate::locale::Lc;
// **< RegionName >*********************************************************************************
@ -162,7 +162,7 @@ pub trait RegionName: Send + Sync + AnyInfo {
///
/// En la implementación predeterminada de [`Region`](crate::base::component::layout::Region),
/// este valor se usa como `aria-label` del contenedor de la región.
fn label(&self) -> L10n;
fn label(&self) -> Lc;
}
/// Referencia estática a una región.
@ -221,12 +221,12 @@ impl RegionName for CoreRegions {
}
#[inline]
fn label(&self) -> L10n {
fn label(&self) -> Lc {
match self {
Self::Header => L10n::l("region_header"),
Self::Aside => L10n::l("region_aside"),
Self::Content => L10n::l("region_content"),
Self::Footer => L10n::l("region_footer"),
Self::Header => Lc::l("region_header"),
Self::Aside => Lc::l("region_aside"),
Self::Content => Lc::l("region_content"),
Self::Footer => Lc::l("region_footer"),
}
}
}
@ -249,7 +249,7 @@ pub trait TemplateName: Send + Sync + AnyInfo {
fn name(&self) -> &'static str;
/// Devuelve un *texto localizado* como etiqueta descriptiva de la plantilla.
fn label(&self) -> L10n;
fn label(&self) -> Lc;
}
/// Referencia estática a una plantilla.
@ -282,10 +282,10 @@ impl TemplateName for CoreTemplates {
}
#[inline]
fn label(&self) -> L10n {
fn label(&self) -> Lc {
match self {
Self::Standard => L10n::l("template-standard"),
Self::Admin => L10n::l("template-admin"),
Self::Standard => Lc::l("template-standard"),
Self::Admin => Lc::l("template-admin"),
}
}
}

View file

@ -6,7 +6,7 @@ use crate::core::extension::Extension;
use crate::core::theme::CoreRegions;
use crate::global;
use crate::html::{Markup, html};
use crate::locale::L10n;
use crate::locale::Lc;
use crate::response::Page;
use crate::web::http::StatusCode;
@ -32,12 +32,12 @@ use crate::web::http::StatusCode;
///
/// #[async_trait]
/// impl Extension for MyTheme {
/// fn name(&self) -> L10n {
/// L10n::n("My theme")
/// fn name(&self) -> Lc {
/// Lc::n("My theme")
/// }
///
/// fn description(&self) -> L10n {
/// L10n::n("A personal theme")
/// fn description(&self) -> Lc {
/// Lc::n("A personal theme")
/// }
///
/// fn theme(&self) -> Option<ThemeRef> {
@ -234,14 +234,14 @@ pub trait Theme: Extension + Send + Sync {
if let Some(parent) = self.parent() {
return parent.error_403(page);
}
page.alter_title(L10n::l("error403_title")).alter_child_in(
page.alter_title(Lc::l("error403_title")).alter_child_in(
&CoreRegions::Content,
ChildOp::Prepend(
Html::with(move |cx| {
html! {
div {
h1 { (L10n::l("error403_alert").using(cx)) }
p { (L10n::l("error403_help").using(cx)) }
h1 { (Lc::l("error403_alert").using(cx)) }
p { (Lc::l("error403_help").using(cx)) }
}
}
})
@ -260,14 +260,14 @@ pub trait Theme: Extension + Send + Sync {
if let Some(parent) = self.parent() {
return parent.error_404(page);
}
page.alter_title(L10n::l("error404_title")).alter_child_in(
page.alter_title(Lc::l("error404_title")).alter_child_in(
&CoreRegions::Content,
ChildOp::Prepend(
Html::with(move |cx| {
html! {
div {
h1 { (L10n::l("error404_alert").using(cx)) }
p { (L10n::l("error404_help").using(cx)) }
h1 { (Lc::l("error404_alert").using(cx)) }
p { (Lc::l("error404_help").using(cx)) }
}
}
})
@ -304,7 +304,7 @@ pub trait Theme: Extension + Send + Sync {
/// [`ErrorPage::ServiceUnavailable`]: crate::response::ErrorPage::ServiceUnavailable
/// [`ErrorPage::GatewayTimeout`]: crate::response::ErrorPage::GatewayTimeout
/// [`CoreTemplates::Standard`]: crate::core::theme::CoreTemplates::Standard
fn error_fatal(&self, page: &mut Page, code: StatusCode, title: L10n, alert: L10n, help: L10n) {
fn error_fatal(&self, page: &mut Page, code: StatusCode, title: Lc, alert: Lc, help: Lc) {
if let Some(parent) = self.parent() {
return parent.error_fatal(page, code, title, alert, help);
}
@ -312,8 +312,8 @@ pub trait Theme: Extension + Send + Sync {
&CoreRegions::Content,
ChildOp::Prepend(
Intro::new()
.with_title(L10n::l("error_code").with_arg("code", code.to_string()))
.with_slogan(L10n::n(code.to_string()))
.with_title(Lc::l("error_code").with_arg("code", code.to_string()))
.with_slogan(Lc::n(code.to_string()))
.with_button(None)
.with_opening(IntroOpening::Custom)
.with_child(Html::with(move |cx| {

View file

@ -1,4 +1,4 @@
use crate::locale::{L10n, LangId};
use crate::locale::{LangId, Lc};
use crate::{AutoDefault, builder_fn, util};
/// Valor opcional para atributos HTML.
@ -7,7 +7,7 @@ use crate::{AutoDefault, builder_fn, util};
/// opcionales, uniformes y tipados.
///
/// Este tipo **no impone ninguna normalización ni semántica concreta**; dichas reglas se definen en
/// implementaciones concretas como `Attr<L10n>` y `Attr<String>`, o en tipos específicos como
/// implementaciones concretas como `Attr<Lc>` y `Attr<String>`, o en tipos específicos como
/// [`AttrName`].
#[derive(AutoDefault, Clone, Debug)]
pub struct Attr<T>(Option<T>);
@ -72,18 +72,18 @@ impl<T> Attr<T> {
}
}
// **< Attr<L10n> >*********************************************************************************
// **< Attr<Lc> >***********************************************************************************
/// Extiende [`Attr`] para [texto localizado](crate::locale) en atributos HTML.
///
/// Encapsula un [`L10n`] para manejar traducciones de forma segura en atributos.
/// Encapsula un [`Lc`] para manejar traducciones de forma segura en atributos.
///
/// # Ejemplo
///
/// ```rust
/// # use pagetop::prelude::*;
/// // Traducción por clave en las locales por defecto de PageTop.
/// let hello = Attr::<L10n>::new(L10n::l("test_hello_world"));
/// let hello = Attr::<Lc>::new(Lc::l("test_hello_world"));
///
/// // Español disponible.
/// assert_eq!(
@ -101,9 +101,9 @@ impl<T> Attr<T> {
/// let title = hello.value(&Locale::resolve("es-ES"));
/// // Ejemplo: html! { a title=(title) { "Link" } }
/// ```
impl Attr<L10n> {
/// Crea una nueva instancia `Attr<L10n>`.
pub fn new(value: L10n) -> Self {
impl Attr<Lc> {
/// Crea una nueva instancia `Attr<Lc>`.
pub fn new(value: Lc) -> Self {
Self::some(value)
}

View file

@ -1,7 +1,7 @@
use crate::AutoDefault;
use crate::core::component::Context;
use crate::html::{Markup, html};
use crate::locale::L10n;
use crate::locale::Lc;
/// Representación SVG del **logotipo de PageTop** para incrustar en HTML.
///
@ -54,7 +54,7 @@ impl PageTopSvg {
viewBox="0 0 1614 1614"
xmlns="http://www.w3.org/2000/svg"
role="img"
aria-label=[L10n::l("pagetop_logo").lookup(cx)]
aria-label=[Lc::l("pagetop_logo").lookup(cx)]
preserveAspectRatio="xMidYMid slice"
focusable="false"
{

View file

@ -354,7 +354,7 @@ impl PropsOp {
/// # use pagetop::prelude::*;
/// #[derive(AutoDefault, Clone, Getters)]
/// pub struct MyButton {
/// label: L10n,
/// label: Lc,
/// props: Props,
/// }
///

View file

@ -87,7 +87,7 @@
//! ```
//!
//! Y *voilà*, sólo queda operar con los idiomas soportados por PageTop usando [`Locale`] y traducir
//! textos con [`L10n`].
//! textos con [`Lc`].
pub use fluent_templates;
pub use unic_langid::{CharacterDirection, LanguageIdentifier};
@ -102,8 +102,8 @@ pub use definition::{LangId, Locale};
mod request;
pub use request::RequestLocale;
mod l10n;
pub use l10n::L10n;
mod lc;
pub use lc::Lc;
// **< include_locales! >***************************************************************************

View file

@ -199,8 +199,11 @@ impl Locale {
/// devuelve el idioma por defecto de la aplicación y, si tampoco está disponible, el idioma de
/// respaldo (`"en-US"`).
///
/// Resulta útil para usar un valor de [`Locale`] como fuente de traducción en
/// [`L10n::lookup()`](crate::locale::L10n::lookup) o [`L10n::using()`](crate::locale::L10n::using).
/// Resulta útil para usar un valor de [`Locale`] como fuente de traducción en [`Lc::lookup()`] o
/// [`Lc::using()`].
///
/// [`Lc::lookup()`]: crate::locale::Lc::lookup
/// [`Lc::using()`]: crate::locale::Lc::using
impl LangId for Locale {
#[inline]
fn langid(&self) -> &'static LanguageIdentifier {

View file

@ -12,13 +12,13 @@ use std::collections::HashMap;
use std::fmt;
/// Operación de localización a realizar.
///
/// * `None` - No se aplica ninguna localización.
/// * `Text` - Con una cadena literal que se devolverá tal cual.
/// * `Translate` - Con la clave a resolver en el `Locales` indicado.
// Tipo de localización a aplicar.
//
// * `None` - No se aplica ninguna localización.
// * `Text` - Con una cadena literal que se devolverá tal cual.
// * `Translate` - Con la clave a resolver en el `Locales` indicado.
#[derive(AutoDefault, Clone, Debug)]
enum L10nOp {
enum LcKind {
#[default]
None,
Text(CowStr),
@ -61,10 +61,10 @@ enum L10nOp {
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// // Texto literal sin traducción.
/// let raw = L10n::n("© 2025 PageTop").get();
/// let raw = Lc::n("© 2025 PageTop").get();
///
/// // Traducción simple con clave y argumentos.
/// let hello = L10n::l("greeting")
/// let hello = Lc::l("greeting")
/// .with_arg("name", "Manuel")
/// .get();
/// ```
@ -73,19 +73,19 @@ enum L10nOp {
///
/// ```rust,ignore
/// // Traducción con clave, conjunto de traducciones y fuente de idioma.
/// let bye = L10n::t("goodbye", &LOCALES_CUSTOM).lookup(&Locale::resolve("it"));
/// let bye = Lc::t("goodbye", &LOCALES_CUSTOM).lookup(&Locale::resolve("it"));
/// ```
#[derive(AutoDefault, Clone)]
pub struct L10n {
op: L10nOp,
pub struct Lc {
op: LcKind,
#[default(&LOCALES_PAGETOP)]
locales: &'static Locales,
args: Vec<(CowStr, CowStr)>,
}
impl fmt::Debug for L10n {
impl fmt::Debug for Lc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("L10n")
f.debug_struct("Lc")
.field("op", &self.op)
.field("args", &self.args)
// No se puede mostrar `locales`; se representa con un texto fijo.
@ -94,11 +94,11 @@ impl fmt::Debug for L10n {
}
}
impl L10n {
impl Lc {
/// **n** = *“native”*. Crea una instancia con una cadena literal sin traducción.
pub fn n(text: impl Into<CowStr>) -> Self {
Self {
op: L10nOp::Text(text.into()),
op: LcKind::Text(text.into()),
..Default::default()
}
}
@ -107,7 +107,7 @@ impl L10n {
/// predefinidas propias de PageTop.
pub fn l(key: impl Into<CowStr>) -> Self {
Self {
op: L10nOp::Translate(key.into()),
op: LcKind::Translate(key.into()),
..Default::default()
}
}
@ -116,7 +116,7 @@ impl L10n {
/// específico.
pub fn t(key: impl Into<CowStr>, locales: &'static Locales) -> Self {
Self {
op: L10nOp::Translate(key.into()),
op: LcKind::Translate(key.into()),
locales,
..Default::default()
}
@ -151,7 +151,7 @@ impl L10n {
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let text = L10n::l("greeting").with_arg("name", "Manuel").get();
/// let text = Lc::l("greeting").with_arg("name", "Manuel").get();
/// ```
pub fn get(&self) -> Option<String> {
self.lookup(&Locale::default())
@ -179,13 +179,13 @@ impl L10n {
/// }
///
/// let r = ResourceLang;
/// let text = L10n::l("greeting").with_arg("name", "Usuario").lookup(&r);
/// let text = Lc::l("greeting").with_arg("name", "Usuario").lookup(&r);
/// ```
pub fn lookup(&self, language: &impl LangId) -> Option<String> {
match &self.op {
L10nOp::None => None,
L10nOp::Text(text) => Some(text.clone().into_owned()),
L10nOp::Translate(key) => {
LcKind::None => None,
LcKind::Text(text) => Some(text.clone().into_owned()),
LcKind::Translate(key) => {
if self.args.is_empty() {
self.locales.try_lookup(language.langid(), key.as_ref())
} else {
@ -219,11 +219,11 @@ impl L10n {
///
/// ```rust,no_run
/// # use pagetop::prelude::*;
/// let html = L10n::l("welcome.message").using(&Locale::resolve("es"));
/// let html = Lc::l("welcome.message").using(&Locale::resolve("es"));
/// ```
pub fn using(&self, language: &impl LangId) -> Markup {
match &self.op {
L10nOp::Text(text) => text.render(),
LcKind::Text(text) => text.render(),
_ => PreEscaped(self.lookup(language).unwrap_or_default()),
}
}

View file

@ -27,7 +27,7 @@ use crate::core::theme::{CoreRegions, RegionName, RegionRef, TemplateRef, ThemeR
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
use crate::html::{Attr, Props, PropsOp};
use crate::html::{DOCTYPE, Markup, html};
use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier};
use crate::locale::{CharacterDirection, LangId, LanguageIdentifier, Lc};
use crate::web::HttpRequest;
use crate::{AutoDefault, builder_fn};
@ -73,8 +73,8 @@ impl RegionName for ReservedRegions {
}
#[inline]
fn label(&self) -> L10n {
L10n::default()
fn label(&self) -> Lc {
Lc::default()
}
}
@ -88,8 +88,8 @@ impl RegionName for ReservedRegions {
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Page {
title : Attr<L10n>,
description : Attr<L10n>,
title : Attr<Lc>,
description : Attr<Lc>,
metadata : Vec<(&'static str, &'static str)>,
properties : Vec<(&'static str, &'static str)>,
context : Context,
@ -127,14 +127,14 @@ impl Page {
/// Establece el título de la página como un valor traducible.
#[builder_fn]
pub fn with_title(mut self, title: L10n) -> Self {
pub fn with_title(mut self, title: Lc) -> Self {
self.title.alter_value(title);
self
}
/// Establece la descripción de la página como un valor traducible.
#[builder_fn]
pub fn with_description(mut self, description: L10n) -> Self {
pub fn with_description(mut self, description: Lc) -> Self {
self.description.alter_value(description);
self
}
@ -258,8 +258,8 @@ impl Page {
/// Permite a [`Page`] actuar como proveedor de idioma usando el [`Context`] de la página.
///
/// Resulta útil para usar [`Page`] directamente como fuente de traducción en [`L10n::lookup()`] o
/// [`L10n::using()`].
/// Resulta útil para usar [`Page`] directamente como fuente de traducción en [`Lc::lookup()`] o
/// [`Lc::using()`].
impl LangId for Page {
#[inline]
fn langid(&self) -> &'static LanguageIdentifier {

View file

@ -3,7 +3,7 @@ use axum::middleware::Next;
use crate::core::component::Contextual;
use crate::html::Markup;
use crate::locale::L10n;
use crate::locale::Lc;
use crate::web::{HttpRequest, IntoResponse, Response, http};
use crate::{trace, util};
@ -89,9 +89,9 @@ impl ErrorPage {
page.theme().error_fatal(
&mut page,
status,
L10n::l(util::join!("error", status.as_str(), "_title")),
L10n::l(util::join!("error", status.as_str(), "_alert")),
L10n::l(util::join!("error", status.as_str(), "_help")),
Lc::l(util::join!("error", status.as_str(), "_title")),
Lc::l(util::join!("error", status.as_str(), "_alert")),
Lc::l(util::join!("error", status.as_str(), "_help")),
);
page
}