diff --git a/locales/en.ftl b/locales/en.ftl deleted file mode 100644 index 37f83cc..0000000 --- a/locales/en.ftl +++ /dev/null @@ -1,5 +0,0 @@ -zone_records_title = Zone { $zone_name } records - -record_type_address = Addresses -record_type_mailserver = E-mail servers -record_type_nameserver = Name servers diff --git a/locales/en/main.ftl b/locales/en/main.ftl new file mode 100644 index 0000000..197b9fc --- /dev/null +++ b/locales/en/main.ftl @@ -0,0 +1,21 @@ +zone-header = Zone { $zone_name } + +zone-content-title = Zone content +zone-content-records-header = Records +zone-content-aliases-header = Aliases + +zone-content-section-web-header = Web +zone-content-section-mail-header = E-mail +zone-content-section-general-header = General + +zone-content-record-type-address = + .type-name = Addresses + +zone-content-record-type-mailserver = + .type-name = E-mail servers + .data-preference = Preference: { $preference } + +zone-content-record-type-nameserver = + .type-name = Name servers + +zone-content-new-record-button = New record diff --git a/locales/fr.ftl b/locales/fr.ftl deleted file mode 100644 index e03c103..0000000 --- a/locales/fr.ftl +++ /dev/null @@ -1,5 +0,0 @@ -zone_records_title = Enregistrements de la zone { $zone_name } - -record_type_address = Adresses -record_type_mailserver = Serveurs de courriel -record_type_nameserver = Serveurs de noms diff --git a/locales/fr/main.ftl b/locales/fr/main.ftl new file mode 100644 index 0000000..33d21ee --- /dev/null +++ b/locales/fr/main.ftl @@ -0,0 +1,22 @@ + +zone-header = Zone { $zone_name } + +zone-content-title = Contenu de la zone +zone-content-records-header = Enregistrements +zone-content-aliases-header = Alias + +zone-content-section-web-header = Web +zone-content-section-mail-header = Courriel +zone-content-section-general-header = Général + +zone-content-record-type-address = + .type-name = Adresses + +zone-content-record-type-mailserver = + .type-name = Serveurs de courriel + .data-preference = Préférence : { $preference } + +zone-content-record-type-nameserver = + .type-name = Serveurs de noms + +zone-content-new-record-button = Nouvel enregistrement diff --git a/src/locale.rs b/src/localization.rs similarity index 69% rename from src/locale.rs rename to src/localization.rs index dfc15b8..d74e7d6 100644 --- a/src/locale.rs +++ b/src/localization.rs @@ -1,35 +1,53 @@ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, fs, path::Path, str::FromStr}; use fluent_bundle::{concurrent::FluentBundle, FluentArgs, FluentResource, FluentValue}; use unic_langid::LanguageIdentifier; -const SOURCES: &[(&str, &str)] = &[ - ("en", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/locales/en.ftl"))), - ("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/locales/fr.ftl"))), -]; - #[derive(Clone)] pub struct Localization { bundles: std::sync::Arc>>, } impl Localization { - pub fn init() -> Self { + pub fn init(locale_directory: &Path) -> Self { let mut bundles = HashMap::new(); - for (locale, translations) in SOURCES { - let res = FluentResource::try_new(translations.to_string()).expect("Failed to parse an FTL string."); - let langid: LanguageIdentifier = locale.parse().expect("Parsing failed"); + let directory_content =fs::read_dir(locale_directory) + .expect("Unable to read locales directory"); - let mut bundle = FluentBundle::new_concurrent(vec![langid.clone()]); + for lang_dir in directory_content { + let lang_dir = lang_dir.expect("I/O error"); + if lang_dir.path().is_dir() { + let langid: LanguageIdentifier = lang_dir.file_name() + .into_string() + .expect("String convertion failed") + .parse() + .expect("Failed to parse language tag"); - bundle - .add_resource(res) - .expect("Failed to add FTL resources to the bundle."); + let mut bundle = FluentBundle::new_concurrent(vec![langid.clone()]); - bundles.insert(langid, bundle); + let directory_content = fs::read_dir(lang_dir.path()) + .expect("Unable to read locales directory"); + for resource in directory_content { + let resource = resource.expect("I/O error"); + + if resource.path().is_file() { + let resource = fs::read_to_string(resource.path()) + .expect("Failed to open file"); + + let resource = FluentResource::try_new(resource) + .expect("Failed to parse an FTL string."); + + bundle + .add_resource(resource) + .expect("Failed to add FTL resources to the bundle."); + } + + } + + bundles.insert(langid, bundle); + } } - Localization { bundles: std::sync::Arc::new(bundles), } @@ -54,20 +72,29 @@ impl tera::Function for Localization { .and_then(|lang| lang.as_str()) .ok_or(tera::Error::msg("localize: Missing msg parameter"))?; + let attribute = args.get("attr") + .and_then(|lang| lang.as_str()); + let bundle = self.bundles.get(&locale) - .ok_or_else(|| tera::Error::msg("localize: Could not find locale {locale}"))?; + .ok_or_else(|| tera::Error::msg(format!("localize: Could not find locale {locale}")))?; let message = bundle.get_message(msg) - .ok_or_else(|| tera::Error::msg("localize: Could not find message {msg}"))?; + .ok_or_else(|| tera::Error::msg(format!("localize: Could not find message {msg}")))?; - let pattern = message.value() - .ok_or_else(|| tera::Error::msg("localize: Message {msg} has no value"))?; + let pattern = if let Some(attribute) = attribute { + message.get_attribute(attribute) + .map(|attribute| attribute.value()) + .ok_or_else(|| tera::Error::msg(format!("localize: Attribute {msg}.{attribute} has no value")))? + } else { + message.value() + .ok_or_else(|| tera::Error::msg(format!("localize: Message {msg} has no value")))? + }; let mut msg_args = FluentArgs::new(); for (key, value) in args { - if key != "msg" && key != "lang" { + if key != "msg" && key != "lang" && key != "attr" { msg_args.set(key, fluent_value_from_tera(value, key)?); } } diff --git a/src/main.rs b/src/main.rs index 1e444d4..4af4872 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod validation; mod macros; mod template; mod proto; -mod locale; +mod localization; use std::sync::Arc; @@ -33,7 +33,7 @@ pub struct AppState { #[tokio::main] async fn main() { - let localization = locale::Localization::init(); + let localization = localization::Localization::init(std::path::Path::new("./locales")); let template_engine = TemplateEngine::new( std::path::Path::new("./templates"), diff --git a/src/resources/dns/external/mod.rs b/src/resources/dns/external/mod.rs index 55840a5..306f48a 100644 --- a/src/resources/dns/external/mod.rs +++ b/src/resources/dns/external/mod.rs @@ -1,5 +1,4 @@ pub mod rdata; pub mod record; -pub use rdata::*; pub use record::*; diff --git a/src/routes/ui/zones.rs b/src/routes/ui/zones.rs index 36bda72..d6d35c8 100644 --- a/src/routes/ui/zones.rs +++ b/src/routes/ui/zones.rs @@ -1,4 +1,3 @@ -use axum::extract::Request; use axum::extract::{Query, Path, State, OriginalUri}; use axum::Extension; use serde::Deserialize; @@ -21,8 +20,6 @@ pub async fn get_records_page( let records = zone.get_records(app.records).await?; let records = friendly::FriendlyRecords::from(records.clone()); - println!("{}", lang); - Ok(Template::new( "pages/records.html", app.template_engine, diff --git a/src/template.rs b/src/template.rs index 0e59f1b..60ab82c 100644 --- a/src/template.rs +++ b/src/template.rs @@ -6,7 +6,7 @@ use axum::response::{Html, IntoResponse}; use serde::Serialize; use tera::{Tera, Context}; -use crate::{errors::Error, locale::Localization}; +use crate::{errors::Error, localization::Localization}; #[derive(Clone)] diff --git a/src/validation.rs b/src/validation.rs index 3c361ba..6ae52ba 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -19,8 +19,8 @@ pub fn normalize_domain(domain_name: &str) -> Result { let domain = domain_name.strip_suffix('.').unwrap_or(domain_name).to_lowercase(); - if domain.as_bytes().len() > 255 { - Err(Error::from(DomainValidationError::DomainTooLong { length: domain.as_bytes().len() })) + if domain.len() > 255 { + Err(Error::from(DomainValidationError::DomainTooLong { length: domain.len() })) } else { let labels = domain.split('.').collect::>(); @@ -40,10 +40,10 @@ pub fn normalize_domain(domain_name: &str) -> Result { ); } - if label.as_bytes().len() > 63 { + if label.len() > 63 { return Err(Error::from(DomainValidationError::LabelToolLong { label: label.into(), - length: label.as_bytes().len() + length: label.len() })); } } diff --git a/templates/macros.html b/templates/macros.html index 98df93b..3386d21 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -1,7 +1,7 @@ {% macro rrset(rtype, ttl, data, zone, lang) %}
  • - {{ tr(msg="record_type_" ~ rtype, lang=lang) }} + {{ tr(msg="zone-content-record-type-" ~ rtype, attr="type-name", lang=lang) }}
    - Preference: {{ data.preference }} + {{ tr(msg="zone-content-record-type-mailserver", attr="data-preference", preference=data.preference, lang=lang) }}
    {% elif rtype == "nameserver" %} diff --git a/templates/pages/records.html b/templates/pages/records.html index 09b4f1b..23f853f 100644 --- a/templates/pages/records.html +++ b/templates/pages/records.html @@ -1,10 +1,10 @@ {% import "macros.html" as macros %} {% extends "bases/app.html" %} -{% block title %}Records - {{ current_zone }} - {% endblock title %} +{% block title %}{{ tr(msg="zone-content-title", lang=lang) }} - {{ current_zone }} - {% endblock title %} {% block main %} -

    {{ tr(msg="zone_records_title", lang=lang, zone_name="" ~ current_zoned ~ "") | safe }}

    +

    {{ tr(msg="zone-header", lang=lang, zone_name="" ~ current_zone ~ "") | safe }}

    -

    Records

    +

    {{ tr(msg="zone-content-records-header", lang=lang) }}

    {% for group in records.records %}
    @@ -24,12 +24,12 @@ - Add record + {{ tr(msg="zone-content-new-record-button", lang=lang) }}
    {% if group.web %} -

    Web

    +

    {{ tr(msg="zone-content-section-web-header", lang=lang) }}

      {% if group.web.addresses %} {{ macros::rrset( @@ -43,7 +43,7 @@ {% endif %} {% if group.mail %} -

      E-mails

      +

      {{ tr(msg="zone-content-section-mail-header", lang=lang) }}

        {% if group.mail.servers %} {{ macros::rrset( @@ -65,7 +65,7 @@ {% endif %} {% if group.general_records %} -

        General

        +

        {{ tr(msg="zone-content-section-general-header", lang=lang) }}

          {% for rrset in group.general_records %} {{ macros::rrset( @@ -83,7 +83,7 @@ {% endfor %}
    -

    Aliases

    +

    {{ tr(msg="zone-content-aliases-header", lang=lang) }}

      {% for alias in records.aliases %}