🍻 Tercera revista a las traducciones por contexto

This commit is contained in:
Manuel Cillero 2023-05-27 22:44:12 +02:00
parent 88d6ce2a72
commit dd443ca375
21 changed files with 415 additions and 252 deletions

View file

@ -4,6 +4,9 @@ pub use context::{ContextOp, RenderContext};
mod definition;
pub use definition::{component_mut, component_ref, AnyComponent, BaseComponent, ComponentTrait};
mod default;
pub(crate) use default::DefaultComponent;
mod arc;
pub use arc::ComponentArc;
@ -19,3 +22,6 @@ pub use renderable::{IsRenderable, Renderable};
mod html_markup;
pub use html_markup::HtmlMarkup;
mod l10n;
pub use l10n::L10n;

View file

@ -1,32 +1,45 @@
use crate::core::component::{ComponentTrait, RenderContext};
use crate::core::component::{ComponentTrait, DefaultComponent, RenderContext};
use crate::html::{html, Markup};
use std::sync::{Arc, RwLock};
#[derive(Clone, Default)]
pub struct ComponentArc(Option<Arc<RwLock<dyn ComponentTrait>>>);
#[derive(Clone)]
pub struct ComponentArc(Arc<RwLock<dyn ComponentTrait>>);
impl Default for ComponentArc {
fn default() -> Self {
ComponentArc(Arc::new(RwLock::new(DefaultComponent)))
}
}
impl ComponentArc {
pub fn new(component: impl ComponentTrait) -> Self {
ComponentArc(Some(Arc::new(RwLock::new(component))))
pub fn new() -> Self {
ComponentArc::default()
}
pub fn replace(&mut self, component: impl ComponentTrait) {
self.0 = Some(Arc::new(RwLock::new(component)));
pub fn new_with(component: impl ComponentTrait) -> Self {
ComponentArc(Arc::new(RwLock::new(component)))
}
pub fn set(&mut self, component: impl ComponentTrait) {
self.0 = Arc::new(RwLock::new(component));
}
pub fn weight(&self) -> isize {
match &self.0 {
Some(component) => component.read().unwrap().weight(),
_ => 0,
}
self.0.read().unwrap().weight()
}
// ComponentArc RENDER.
pub fn render(&self, rcx: &mut RenderContext) -> Markup {
html! {
@if let Some(component) = &self.0 {
(component.write().unwrap().render(rcx))
}
self.0.write().unwrap().render(rcx)
}
pub fn optional_render(&self, rcx: &mut RenderContext) -> Option<Markup> {
let render = self.0.write().unwrap().render(rcx).into_string();
if !render.trim().is_empty() {
return Some(html! { (render) });
}
None
}
}

View file

@ -16,7 +16,7 @@ impl ComponentsBundle {
}
pub fn add(&mut self, component: impl ComponentTrait) {
self.0.push(ComponentArc::new(component));
self.0.push(ComponentArc::new_with(component));
}
pub fn clear(&mut self) {

View file

@ -0,0 +1,18 @@
use crate::core::component::{AnyComponent, ComponentTrait};
#[derive(Default)]
pub struct DefaultComponent;
impl ComponentTrait for DefaultComponent {
fn new() -> Self {
DefaultComponent::default()
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}

View file

@ -0,0 +1,138 @@
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, Handle};
use std::collections::HashMap;
define_handle!(COMPONENT_L10N);
#[rustfmt::skip]
#[derive(Default)]
pub struct L10n {
key : &'static str,
locales: Option<&'static Locales>,
args : HashMap<&'static str, String>,
escaped: bool,
}
impl ComponentTrait for L10n {
fn new() -> Self {
L10n::default()
}
fn handle(&self) -> Handle {
COMPONENT_L10N
}
fn default_render(&self, rcx: &mut RenderContext) -> Markup {
if let Some(locales) = self.locales() {
html! {
@if self.escaped() {
(PreEscaped(translate(
self.key(),
Locale::Using(
rcx.language(),
locales,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
)))
} @else {
(translate(
self.key(),
Locale::Using(
rcx.language(),
locales,
&self.args().iter().fold(HashMap::new(), |mut args, (key, value)| {
args.insert(key.to_string(), value.to_owned().into());
args
})
)
))
}
}
} else {
html! { (self.key()) }
}
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}
impl L10n {
pub fn n(text: &'static str) -> Self {
L10n {
key: text,
..Default::default()
}
}
pub fn t(key: &'static str, locales: &'static Locales) -> Self {
L10n {
key,
locales: Some(locales),
..Default::default()
}
}
pub fn e(key: &'static str, locales: &'static Locales) -> Self {
L10n {
key,
locales: Some(locales),
escaped: true,
..Default::default()
}
}
// HtmL10n BUILDER.
#[fn_builder]
pub fn alter_key(&mut self, key: &'static str) -> &mut Self {
self.key = key;
self
}
#[fn_builder]
pub fn alter_locales(&mut self, locales: &'static Locales) -> &mut Self {
self.locales = Some(locales);
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
}
// HtmL10n GETTERS.
pub fn key(&self) -> &str {
self.key
}
pub fn locales(&self) -> Option<&Locales> {
self.locales
}
pub fn args(&self) -> &HashMap<&str, String> {
&self.args
}
pub fn escaped(&self) -> bool {
self.escaped
}
}

View file

@ -1,9 +1,9 @@
use super::ModuleTrait;
use crate::config;
use crate::core::component::{ComponentTrait, RenderContext};
use crate::html::{html, Favicon, Markup};
use crate::response::page::Page;
use crate::{concat_string, config};
pub type ThemeStaticRef = &'static dyn ThemeTrait;
@ -17,20 +17,21 @@ pub trait ThemeTrait: ModuleTrait + Send + Sync {
}
fn render_page_head(&self, page: &mut Page) -> Markup {
let title = page.title();
let description = page.description();
let viewport = "width=device-width, initial-scale=1, shrink-to-fit=no";
html! {
head {
meta charset="utf-8";
@match page.title().get() {
Some(t) => title {
(concat_string!(config::SETTINGS.app.name, " | ", t))
},
None => title { (config::SETTINGS.app.name) }
@if !title.is_empty() {
title { (config::SETTINGS.app.name) (" | ") (title) }
} @else {
title { (config::SETTINGS.app.name) }
}
@if let Some(d) = page.description().get() {
meta name="description" content=(d);
@if !description.is_empty() {
meta name="description" content=(description);
}
meta name="viewport" content=(viewport);

View file

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

View file

@ -32,12 +32,15 @@ pub enum TextDirection {
RightToLeft,
}
pub type PageTitle = ComponentArc;
pub type PageDescription = ComponentArc;
#[rustfmt::skip]
pub struct Page {
language : AttributeValue,
direction : AttributeValue,
title : AttributeValue,
description : AttributeValue,
title : PageTitle,
description : PageDescription,
metadata : Vec<(&'static str, &'static str)>,
properties : Vec<(&'static str, &'static str)>,
favicon : Option<Favicon>,
@ -56,8 +59,8 @@ impl Default for Page {
Some(direction) => AttributeValue::new().with_value(direction),
_ => AttributeValue::new(),
},
title : AttributeValue::new(),
description : AttributeValue::new(),
title : PageTitle::new(),
description : PageDescription::new(),
metadata : Vec::new(),
properties : Vec::new(),
favicon : None,
@ -95,14 +98,14 @@ impl Page {
}
#[fn_builder]
pub fn alter_title(&mut self, title: &str) -> &mut Self {
self.title.alter_value(title);
pub fn alter_title(&mut self, title: L10n) -> &mut Self {
self.title.set(title);
self
}
#[fn_builder]
pub fn alter_description(&mut self, description: &str) -> &mut Self {
self.description.alter_value(description);
pub fn alter_description(&mut self, description: L10n) -> &mut Self {
self.description.set(description);
self
}
@ -167,12 +170,12 @@ impl Page {
&self.direction
}
pub fn title(&self) -> &AttributeValue {
&self.title
pub fn title(&mut self) -> String {
self.title.render(&mut self.context).into_string()
}
pub fn description(&self) -> &AttributeValue {
&self.description
pub fn description(&mut self) -> String {
self.description.render(&mut self.context).into_string()
}
pub fn metadata(&self) -> &Vec<(&str, &str)> {