Añade traducción de textos con plantillas Fluent
This commit is contained in:
parent
96884cbbc0
commit
0e3300dc90
14 changed files with 149 additions and 53 deletions
|
|
@ -28,6 +28,10 @@ doc-comment = "0.3.3"
|
|||
once_cell = "1.9.0"
|
||||
|
||||
config_rs = { package = "config", version = "0.11.0", features = ["toml"] }
|
||||
|
||||
fluent-templates = "0.6.1"
|
||||
unic-langid = "0.9.0"
|
||||
|
||||
actix-web = "4.0.0-rc.3"
|
||||
sycamore = { version = "0.8.0-beta.2", features = ["ssr"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
[app]
|
||||
name = "PageTop Application"
|
||||
description = "Developed with the amazing PageTop framework."
|
||||
# Idioma (localización) predeterminado.
|
||||
language = "en-US"
|
||||
|
||||
[webserver]
|
||||
# Configuración opcional del servidor web.
|
||||
|
|
|
|||
3
src/base/mod.rs
Normal file
3
src/base/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
//! Temas, Módulos y Componentes base.
|
||||
|
||||
pub mod module;
|
||||
4
src/base/module/homepage/locales/en-US/homepage.ftl
Normal file
4
src/base/module/homepage/locales/en-US/homepage.ftl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module_name = Default homepage
|
||||
module_desc = Displays a default homepage when none is configured.
|
||||
|
||||
greeting = Hello { $name }!
|
||||
4
src/base/module/homepage/locales/es-ES/homepage.ftl
Normal file
4
src/base/module/homepage/locales/es-ES/homepage.ftl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module_name = Página de inicio predeterminada
|
||||
module_desc = Muestra una página de inicio predeterminada cuando no hay ninguna configurada.
|
||||
|
||||
greeting = Hola { $name }!
|
||||
39
src/base/module/homepage/mod.rs
Normal file
39
src/base/module/homepage/mod.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
localize!("en-US", "src/base/module/homepage/locales");
|
||||
|
||||
pub struct HomepageModule;
|
||||
|
||||
impl Module for HomepageModule {
|
||||
fn name(&self) -> String {
|
||||
l("module_name")
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
l("module_desc")
|
||||
}
|
||||
|
||||
fn configure_module(&self, cfg: &mut server::web::ServiceConfig) {
|
||||
cfg.service(
|
||||
server::web::resource("/")
|
||||
.route(server::web::get().to(greet))
|
||||
);
|
||||
cfg.service(
|
||||
server::web::resource("/{name}")
|
||||
.route(server::web::get().to(greet_with_param))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn greet() -> impl server::Responder {
|
||||
t("greeting", &args!["name" => config_get!("app.name")])
|
||||
}
|
||||
|
||||
async fn greet_with_param(req: server::HttpRequest) -> server::HttpResponse {
|
||||
let name: String = req.match_info().get("name").unwrap_or("World").into();
|
||||
let args = args!["name" => name];
|
||||
server::HttpResponse::Ok()
|
||||
.body(sycamore::render_to_string(|ctx| sycamore::view! { ctx,
|
||||
p { (t("greeting", &args)) }
|
||||
}))
|
||||
}
|
||||
1
src/base/module/mod.rs
Normal file
1
src/base/module/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod homepage;
|
||||
|
|
@ -72,6 +72,7 @@ macro_rules! config_map {
|
|||
pub struct App {
|
||||
pub name : String,
|
||||
pub description : String,
|
||||
pub language : String,
|
||||
pub run_mode : String,
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ pub struct Settings {
|
|||
}
|
||||
|
||||
config_map!(r#"
|
||||
Ajustes globales y valores por defecto para las secciones *\[app\]* y
|
||||
Ajustes globales y valores predeterminados para las secciones *\[app\]* y
|
||||
*\[webserver\]* específicas de PageTop.
|
||||
"#,
|
||||
SETTINGS, Settings,
|
||||
|
|
@ -96,6 +97,7 @@ Ajustes globales y valores por defecto para las secciones *\[app\]* y
|
|||
// [app]
|
||||
"app.name" => "PageTop Application",
|
||||
"app.description" => "Developed with the amazing PageTop framework.",
|
||||
"app.language" => "en-US",
|
||||
|
||||
// [webserver]
|
||||
"webserver.bind_address" => "localhost",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
use crate::base;
|
||||
use crate::config::SETTINGS;
|
||||
use crate::core::{Server, all, server};
|
||||
use crate::core::{Server, all, register_module, server};
|
||||
|
||||
pub fn run(bootstrap: Option<fn()>) -> Result<Server, std::io::Error> {
|
||||
// Ejecuta la función de inicio específica para la aplicación.
|
||||
// Ejecuta la función de arranque de la aplicación.
|
||||
if bootstrap != None {
|
||||
let _ = &(bootstrap.unwrap())();
|
||||
}
|
||||
|
||||
// Registra la página de inicio de PageTop como último módulo.
|
||||
// Así, la función de arranque de la aplicación podría sobrecargarlo.
|
||||
register_module(&base::module::homepage::HomepageModule);
|
||||
|
||||
// Inicializa el servidor web.
|
||||
let server = server::HttpServer::new(|| {
|
||||
server::App::new()
|
||||
|
|
|
|||
|
|
@ -7,5 +7,10 @@ pub use once_cell::sync::Lazy;
|
|||
// APIs públicas.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub mod macros; // Macros útiles.
|
||||
pub mod config; // Gestión de la configuración.
|
||||
pub mod locale; // Localización.
|
||||
pub mod core; // Servidor web y sistemas para Temas, Módulos y Respuestas.
|
||||
pub mod base; // Temas, Módulos y Componentes base.
|
||||
|
||||
pub mod prelude; // Re-exporta recursos comunes.
|
||||
|
|
|
|||
47
src/locale/mod.rs
Normal file
47
src/locale/mod.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use crate::Lazy;
|
||||
use crate::config::SETTINGS;
|
||||
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
pub use fluent_templates::{static_loader as static_locale, Loader as Locale};
|
||||
pub use fluent_templates;
|
||||
pub use fluent_templates::fluent_bundle::FluentValue;
|
||||
|
||||
/// Almacena el Identificador de Idioma Unicode ([Unicode Language Identifier]
|
||||
/// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de
|
||||
/// la aplicación, obtenido de `SETTINGS.app.language`.
|
||||
pub static LANGID: Lazy<LanguageIdentifier> = Lazy::new(|| {
|
||||
SETTINGS.app.language.parse().expect("Failed to parse.")
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
/// Permite integrar fácilmente localización en tus temas y módulos.
|
||||
macro_rules! localize {
|
||||
( $DEF_LANGID:literal, $locales:literal $(, $core_locales:literal)? ) => {
|
||||
use $crate::locale::*;
|
||||
|
||||
static_locale! {
|
||||
static LOCALES = {
|
||||
locales: $locales,
|
||||
$( core_locales: $core_locales, )?
|
||||
fallback_language: $DEF_LANGID,
|
||||
|
||||
// Elimina las marcas Unicode que delimitan los argumentos.
|
||||
customise: |bundle| bundle.set_use_isolating(false),
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn l(key: &str) -> String {
|
||||
LOCALES.lookup(&LANGID, key)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn t(
|
||||
key: &str,
|
||||
args: &std::collections::HashMap<String, FluentValue>
|
||||
) -> String {
|
||||
LOCALES.lookup_with_args(&LANGID, key, args)
|
||||
}
|
||||
};
|
||||
}
|
||||
19
src/macros/mod.rs
Normal file
19
src/macros/mod.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#[macro_export]
|
||||
/// Macro para construir grupos de pares clave-valor.
|
||||
///
|
||||
/// ```
|
||||
/// let args = args![
|
||||
/// "userName" => "Roberto",
|
||||
/// "photoCount" => 3,
|
||||
/// "userGender" => "male"
|
||||
/// ];
|
||||
/// ```
|
||||
macro_rules! args {
|
||||
( $($key:expr => $value:expr),* ) => {{
|
||||
let mut a = std::collections::HashMap::new();
|
||||
$(
|
||||
a.insert(String::from($key), $value.into());
|
||||
)*
|
||||
a
|
||||
}};
|
||||
}
|
||||
51
src/main.rs
51
src/main.rs
|
|
@ -1,53 +1,4 @@
|
|||
use pagetop::config_get;
|
||||
use pagetop::core::module::Module;
|
||||
use pagetop::core::{register_module, server};
|
||||
|
||||
struct Greet;
|
||||
impl Module for Greet {
|
||||
fn name(&self) -> String {
|
||||
"Hello".to_string()
|
||||
}
|
||||
|
||||
fn configure_module(&self, cfg: &mut server::web::ServiceConfig) {
|
||||
cfg.service(
|
||||
server::web::resource("/")
|
||||
.route(server::web::get().to(greet))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn greet() -> impl server::Responder {
|
||||
format!("Hello from {}!", config_get!("app.name"))
|
||||
}
|
||||
|
||||
struct GreetWithParam;
|
||||
impl Module for GreetWithParam {
|
||||
fn name(&self) -> String {
|
||||
"Hello World!".to_string()
|
||||
}
|
||||
|
||||
fn configure_module(&self, cfg: &mut server::web::ServiceConfig) {
|
||||
cfg.service(
|
||||
server::web::resource("/{name}")
|
||||
.route(server::web::get().to(greet_with_param))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn greet_with_param(req: server::HttpRequest) -> server::HttpResponse {
|
||||
let name: String = req.match_info().get("name").unwrap_or("World").into();
|
||||
server::HttpResponse::Ok()
|
||||
.body(sycamore::render_to_string(|ctx| sycamore::view! { ctx,
|
||||
p { "Hello " (name) "!" }
|
||||
}))
|
||||
}
|
||||
|
||||
fn bootstrap() {
|
||||
register_module(&Greet);
|
||||
register_module(&GreetWithParam);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
server::run(Some(bootstrap))?.await
|
||||
pagetop::core::server::run(None)?.await
|
||||
}
|
||||
|
|
|
|||
10
src/prelude.rs
Normal file
10
src/prelude.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//! Re-exporta recursos comunes.
|
||||
|
||||
pub use crate::args;
|
||||
pub use crate::config_get;
|
||||
pub use crate::localize;
|
||||
|
||||
pub use crate::core::module::*;
|
||||
|
||||
pub use crate::core::server;
|
||||
pub use crate::core::register_module;
|
||||
Loading…
Add table
Add a link
Reference in a new issue