From 0fced131c56563a5e9ec182b92b92e87741a3b59 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 27 Sep 2019 16:21:10 +0200 Subject: [PATCH] i18n: working i18n based on gettext --- dist/email-templates/manage.htm.hbs | 20 ++- dist/email-templates/manage.txt.hbs | 18 +-- dist/templates/400-plain.html.hbs | 2 +- dist/templates/500.html.hbs | 6 +- dist/templates/about/usage.html.hbs | 4 +- dist/templates/found.html.hbs | 30 ++-- dist/templates/index.html.hbs | 25 ++- dist/templates/layout.html.hbs | 13 +- dist/templates/maintenance.html.hbs | 3 +- dist/templates/manage/manage.html.hbs | 17 ++- dist/templates/manage/manage_key.html.hbs | 16 +- .../manage/manage_link_sent.html.hbs | 4 +- dist/templates/search-form.html.hbs | 10 -- .../upload/already-verified.html.hbs | 4 +- dist/templates/upload/publish-result.html.hbs | 15 +- .../upload/upload-ok-multiple.html.hbs | 8 +- dist/templates/upload/upload-ok.html.hbs | 51 +++---- dist/templates/upload/upload.html.hbs | 8 +- .../upload/verification-form.html.hbs | 10 +- src/gettext_strings.rs | 56 +++++++ src/i18n.rs | 102 +++++++++++++ src/main.rs | 2 + src/web/manage.rs | 8 +- src/web/mod.rs | 143 ++++++++++-------- src/web/vks_web.rs | 37 +---- 25 files changed, 382 insertions(+), 230 deletions(-) delete mode 100644 dist/templates/search-form.html.hbs create mode 100644 src/gettext_strings.rs create mode 100644 src/i18n.rs diff --git a/dist/email-templates/manage.htm.hbs b/dist/email-templates/manage.htm.hbs index 2b0922e..27913df 100644 --- a/dist/email-templates/manage.htm.hbs +++ b/dist/email-templates/manage.htm.hbs @@ -1,26 +1,24 @@ - - + - Manage your key on {{domain}} + {{ text "Manage your key on {{domain}}" rerender }}

- Hi, + {{ text "Hi," }}

- this is an automated message from {{domain}}. If you didn't - request this message, please ignore it. + {{ text "this is an automated message from {{ domain }}." rerender }} + {{ text "If you didn't request this message, please ignore it." }}

- OpenPGP key: {{primary_fp}} + {{ text "OpenPGP key: {{ primary_fp }}" rerender }}

- To manage and delete listed addresses on this key, please follow - the link below: + {{ text "To manage and delete listed addresses on this key, please follow the link below:" }}

{{uri}}

- You can find more info at {{domain}}/about. + {{ text "You can find more info at {{ domain }}/about." rerender }}

- Greetings from the keys.openpgp.org team + {{ text "Greetings from the keys.openpgp.org team" rerender }} diff --git a/dist/email-templates/manage.txt.hbs b/dist/email-templates/manage.txt.hbs index 443c77f..33f0262 100644 --- a/dist/email-templates/manage.txt.hbs +++ b/dist/email-templates/manage.txt.hbs @@ -1,16 +1,16 @@ -Hi, +{{ text "Hi," }} -this is an automated message from {{domain}}. If you didn't -request this message, please ignore it. +{{ text "this is an automated message from {{domain}}. If you didn't" rerender }} +{{ text "request this message, please ignore it." }} -OpenPGP key: {{primary_fp}} +{{ text "OpenPGP key: {{ primary_fp }}" rerender }} -To manage and delete listed addresses on this key, please follow -the link below: +{{ text "To manage and delete listed addresses on this key, please follow" }} +{{ text "the link below:" }} - {{uri}} + {{ uri }} -You can find more info at {{base_uri}}/about +{{ text "You can find more info at {{ base_uri }}/about" }} -Greetings from the keys.openpgp.org team +{{ text "Greetings from the keys.openpgp.org team" }} diff --git a/dist/templates/400-plain.html.hbs b/dist/templates/400-plain.html.hbs index 23da51c..e271106 100644 --- a/dist/templates/400-plain.html.hbs +++ b/dist/templates/400-plain.html.hbs @@ -1 +1 @@ -Bad request: {{error}} +Bad request: {{ page/error }} diff --git a/dist/templates/500.html.hbs b/dist/templates/500.html.hbs index eb01d9d..1078c61 100644 --- a/dist/templates/500.html.hbs +++ b/dist/templates/500.html.hbs @@ -1,7 +1,7 @@ {{#> layout }} -

Error

+

{{ text "Error" }}

-

Looks like something went wrong :(

+

{{ text "Looks like something went wrong :(" }}

-

Error: {{internal_error}}

+

{{ text "Error: {{ internal_error }}" }}

{{/layout}} diff --git a/dist/templates/about/usage.html.hbs b/dist/templates/about/usage.html.hbs index a984626..53aa2c5 100644 --- a/dist/templates/about/usage.html.hbs +++ b/dist/templates/about/usage.html.hbs @@ -1,4 +1,5 @@ {{#> layout }} + {{#with page}}

About | News | Usage | FAQ | Stats | Privacy

@@ -93,7 +94,7 @@ You can try this shortcut for uploading your key, which outputs a direct link to the verification page:
- gpg --export your_address@example.net | curl -T - {{base_uri}} + gpg --export your_address@example.net | curl -T - {{ ../base_uri }}
  • @@ -156,4 +157,5 @@

  • + {{/with}} {{/layout}} diff --git a/dist/templates/found.html.hbs b/dist/templates/found.html.hbs index 6a41f39..5b032d4 100644 --- a/dist/templates/found.html.hbs +++ b/dist/templates/found.html.hbs @@ -1,20 +1,20 @@ {{#> layout }} -
    -

    - We found an entry for : -

    + {{#with page}} +
    +

    + {{ text "We found an entry for {{ query }}:" rerender }} +

    -

    - {{base_uri}}/vks/v1/by-fingerprint/{{ fpr }} -

    +

    + {{ ../base_uri }}/vks/v1/by-fingerprint/{{ fpr }} +

    -

    - Hint: It's more convenient to use - keys.openpgp.org from your OpenPGP software.
    - Take a look at our usage guide for details. -

    - -
    + {{/with}} {{/layout}} diff --git a/dist/templates/index.html.hbs b/dist/templates/index.html.hbs index e2ffb46..061ddfe 100644 --- a/dist/templates/index.html.hbs +++ b/dist/templates/index.html.hbs @@ -1,14 +1,31 @@ {{#> layout }} - {{> search-form}} -

    You can also upload or manage your key.

    + {{#with page}} + + {{#with error}} +

    Error: {{ this }}

    + {{/with}} + +
    + +

    - Find out more about this service. + {{ text "You can also upload or manage your key." }} +

    + +

    + {{ text "Find out more about this service." }}


    - News: Three months after launch ✨ (2019-09-12) + {{ text "News:" }} Three months after launch ✨ (2019-09-12)

    + {{/with}} {{/layout}} diff --git a/dist/templates/layout.html.hbs b/dist/templates/layout.html.hbs index 9510f61..80a803c 100644 --- a/dist/templates/layout.html.hbs +++ b/dist/templates/layout.html.hbs @@ -1,27 +1,24 @@ - + keys.openpgp.org - +

    keys.openpgp.org

    - {{#with error }} -

    Error: {{ this }}

    - {{/with}} {{> @partial-block }}

    Hagrid - v{{ version }} built from + {{ text "v{{ version }} built from" rerender }} {{ commit }}

    -

    Powered by Sequoia-PGP

    -

    Background image retrieved from Subtle Patterns under CC BY-SA 3.0

    +

    {{ text "Powered by Sequoia-PGP" }}

    +

    {{ text "Background image retrieved from Subtle Patterns under CC BY-SA 3.0" }}

    diff --git a/dist/templates/maintenance.html.hbs b/dist/templates/maintenance.html.hbs index 1e7d895..7f24687 100644 --- a/dist/templates/maintenance.html.hbs +++ b/dist/templates/maintenance.html.hbs @@ -1,8 +1,7 @@ {{#> layout }} -

    Maintenance Mode

    +

    {{ text "Maintenance Mode" }}

    {{message}}

    - {{/layout}} diff --git a/dist/templates/manage/manage.html.hbs b/dist/templates/manage/manage.html.hbs index 4736212..6268305 100644 --- a/dist/templates/manage/manage.html.hbs +++ b/dist/templates/manage/manage.html.hbs @@ -1,18 +1,25 @@ {{#> layout }} -

    Manage your key

    + {{#with page}} + + {{#with error }} +

    Error: {{ this }}

    + {{/with}} + +

    {{ text "Manage your key" }}

    + placeholder="{{ text "Enter any verified e-mail address of your key" }}">

    - We will send you an e-mail with a link you can use to remove any of your - e-mail addresses from search. + {{ text "We will send you an e-mail with a link you can use to remove any of your e-mail addresses from search." }}

    + + {{/with}} {{/layout}} diff --git a/dist/templates/manage/manage_key.html.hbs b/dist/templates/manage/manage_key.html.hbs index d8a3504..f4e7bc3 100644 --- a/dist/templates/manage/manage_key.html.hbs +++ b/dist/templates/manage/manage_key.html.hbs @@ -1,11 +1,12 @@ {{#> layout }} + {{#with page}}

    - Managing the key {{key_fpr}}. + {{ text "Managing the key {{ key_fpr }}." rerender }}

    {{#if uid_status}}

    - Your key is published with the following identity information: + {{ text "Your key is published with the following identity information:" }}

    {{#each uid_status}} @@ -14,7 +15,7 @@
    - +

    @@ -24,19 +25,18 @@ {{/each}}

    - Clicking "delete" on any address will remove it from this key. It will no longer appear in a search.
    - To add another address, upload the key again. + {{ text "Clicking \"delete\" on any address will remove it from this key. It will no longer appear in a search.
    To add another address, upload the key again." }}

    {{else}}

    - Your key is published as only non-identity information. - (what does this mean?) + {{ text "Your key is published as only non-identity information. (what does this mean?)" }}

    - To add an address, upload the key again. + {{ text "To add an address, upload the key again." }}

    {{/if}} + {{/with}} {{/layout}} diff --git a/dist/templates/manage/manage_link_sent.html.hbs b/dist/templates/manage/manage_link_sent.html.hbs index 1becb62..9df35c1 100644 --- a/dist/templates/manage/manage_link_sent.html.hbs +++ b/dist/templates/manage/manage_link_sent.html.hbs @@ -1,5 +1,7 @@ {{#> layout }} + {{#with page}}

    - We have sent an email with further instructions to . + {{ text "We have sent an email with further instructions to {{ address }}" rerender }}.

    + {{/with}} {{/layout}} diff --git a/dist/templates/search-form.html.hbs b/dist/templates/search-form.html.hbs deleted file mode 100644 index de5a6f0..0000000 --- a/dist/templates/search-form.html.hbs +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    - -
    -
    diff --git a/dist/templates/upload/already-verified.html.hbs b/dist/templates/upload/already-verified.html.hbs index 1aadc79..b9f1fec 100644 --- a/dist/templates/upload/already-verified.html.hbs +++ b/dist/templates/upload/already-verified.html.hbs @@ -1,3 +1,5 @@ {{#> layout }} -

    This address was already verified.

    + {{#with page}} +

    {{ text "This address was already verified." }}

    + {{/with}} {{/layout}} diff --git a/dist/templates/upload/publish-result.html.hbs b/dist/templates/upload/publish-result.html.hbs index b2da52b..54c27b4 100644 --- a/dist/templates/upload/publish-result.html.hbs +++ b/dist/templates/upload/publish-result.html.hbs @@ -1,20 +1,19 @@ {{#> layout}} -
    - {{#if verified }} + {{#with page}} +
    + {{#if verified}}

    - Your key - {{key_fpr}} - is now published - for the identity . + {{ text "Your key {{ key_fpr }} is now published for the identity {{ userid }}." rerender }}

    {{else}}

    - Verification failed! Perhaps the link you used was expired? + {{ text "Verification failed! Perhaps the link you used was expired?" }}

    - You can try uploading again. + {{ text "You can try uploading again." }}

    {{/if}}
    + {{/with}} {{/layout}} diff --git a/dist/templates/upload/upload-ok-multiple.html.hbs b/dist/templates/upload/upload-ok-multiple.html.hbs index 1f863bb..72b195c 100644 --- a/dist/templates/upload/upload-ok-multiple.html.hbs +++ b/dist/templates/upload/upload-ok-multiple.html.hbs @@ -1,15 +1,17 @@ {{#> layout }} + {{#with page}}

    - Your keys have been successfully uploaded: + {{ text "Your keys have been successfully uploaded:" }}

    - Note: To make keys searchable by address, you must upload them individually. + {{ text "Note: To make keys searchable by address, you must upload them individually." }}

    + {{/with}} {{/layout}} diff --git a/dist/templates/upload/upload-ok.html.hbs b/dist/templates/upload/upload-ok.html.hbs index c25b2fa..511f81f 100644 --- a/dist/templates/upload/upload-ok.html.hbs +++ b/dist/templates/upload/upload-ok.html.hbs @@ -1,75 +1,66 @@ {{#> layout }} + {{#with page}} +

    - You uploaded the key {{key_fpr}}. + {{ text "You uploaded the key {{ key_fpr }}." rerender }}

    {{#if is_revoked}}

    - This key is revoked. - It is published without identity information - (what does this mean?), - and can't be made available for search - by e-mail address. + {{ text "This key is revoked." }} + {{ text "It is published without identity information and can't be made available for search by e-mail address" }} + ({{ text "what does this mean?" }}).

    {{else}} {{#if email_published}}

    - This key is now published with the following identity information (what does this mean?): + {{ text "This key is now published with the following identity information (what does this mean?):" }}

    {{#each email_published}}
    -
    Published
    +
    {{ text "Published" }}

    {{/each}} {{else}}

    - This key is now published with only non-identity information (what does this mean?) + {{ text "This key is now published with only non-identity information (what does this mean?)" }}

    {{/if}} {{#if email_unpublished}}

    - To make the key available for search by e-mail address, you can verify it belongs to you: + {{ text "To make the key available for search by e-mail address, you can verify it belongs to you:" }}

    {{#each email_unpublished}}
    {{#if requested}} - Verification Pending + {{ text "Verification Pending" }} {{else}}
    - +
    {{/if}}
    -

    +

    {{/each}}

    - Note: Some providers delay e-mails for up to 15 minutes - to prevent spam. Please be patient. + {{ text "Note: Some providers delay e-mails for up to 15 minutes to prevent spam. Please be patient." }}

    {{/if}} {{#if count_unparsed}} {{#if count_unparsed_one}}

    - This key contains one identity that could not be parsed as an email - address.
    - This identity can't be published - on keys.openpgp.org. - (why?) + {{ text "This key contains one identity that could not be parsed as an email address.
    This identity can't be published on keys.openpgp.org. (why?)" }}

    {{else}}

    - This key contains {{count_unparsed}} identities that could not be parsed - as an email address.
    - These identities can't be published - on keys.openpgp.org. - (why?) + {{ text "This key contains {{ count_unparsed }} identities that could not be parsed as an email address.
    These identities can't be published on keys.openpgp.org. (why?)" rerender }}

    {{/if}} {{/if}} @@ -77,18 +68,18 @@ {{#if count_revoked}} {{#if count_revoked_one}}

    - This key contains one revoked identity, which is not published. - (Why?) + {{ text "This key contains one revoked identity, which is not published." }} + {{ text "(Why?)" }}

    {{else}}

    - This key contains {{count_revoked}} revoked identities, which are not - published. - (Why?) + {{ text "This key contains {{ count_revoked }} revoked identities, which are not published." rerender }} + {{ text "(Why?)" }}

    {{/if}} {{/if}} {{/if}} + {{/with}} {{/layout}} diff --git a/dist/templates/upload/upload.html.hbs b/dist/templates/upload/upload.html.hbs index 3fd342c..c8a4edf 100644 --- a/dist/templates/upload/upload.html.hbs +++ b/dist/templates/upload/upload.html.hbs @@ -1,18 +1,18 @@ {{#> layout }}
    -

    Upload your key

    +

    {{ text "Upload your key" }}

    - +

    - Need more info? Check our intro and usage guide! + {{ text "Need more info? Check our intro and usage guide!" }}

    + {{/with}} {{/layout}} diff --git a/src/gettext_strings.rs b/src/gettext_strings.rs new file mode 100644 index 0000000..6f4c50f --- /dev/null +++ b/src/gettext_strings.rs @@ -0,0 +1,56 @@ +use gettext_macros::t; + +fn _dummy() { + t!("Error"); + t!("Looks like something went wrong :("); + t!("Error: {{ internal_error }}"); + t!("We found an entry for {{ query }}:"); + t!("debug info"); + t!("Search by Email Address / Key ID / Fingerprint"); + t!("Search"); + t!("You can also upload or manage your key."); + t!("Find out more about this service."); + t!("News:"); + t!("v{{ version }} built from"); + t!("Powered by Sequoia-PGP"); + t!("Background image retrieved from Subtle Patterns under CC BY-SA 3.0"); + t!("Maintenance Mode"); + t!("Manage your key"); + t!("Enter any verified e-mail address of your key"); + t!("Send link"); + t!("We will send you an e-mail with a link you can use to remove any of your e-mail addresses from search."); + t!("Managing the key {{ key_fpr }}."); + t!("Your key is published with the following identity information:"); + t!("Clicking \"delete\" on any address will remove it from this key. It will no longer appear in a search.
    To add another address, upload the key again."); + t!("Your key is published as only non-identity information. (what does this mean?)"); + t!("To add an address, upload the key again."); + t!("We have sent an email with further instructions to {{ address }}"); + t!("This address was already verified."); + t!("Your key {{ key_fpr }} is now published for the identity {{ userid }}."); + t!("Verification failed! Perhaps the link you used was expired?"); + t!("You can try uploading again."); + t!("Your public key"); + t!("Upload"); + t!("Need more info? Check our intro and usage guide!"); + t!("You uploaded the key {{ key_fpr }}."); + t!("This key is revoked."); + t!("It is published without identity information and can't be made available for search by e-mail address"); + t!("what does this mean?"); + t!("This key is now published with the following identity information (what does this mean?):"); + t!("Published"); + t!("This key is now published with only non-identity information (what does this mean?)"); + t!("To make the key available for search by e-mail address, you can verify it belongs to you:"); + t!("Verification Pending"); + t!("Note: Some providers delay e-mails for up to 15 minutes to prevent spam. Please be patient."); + t!("Send Verification Mail"); + t!("This key contains one identity that could not be parsed as an email address.
    This identity can't be published on keys.openpgp.org. (why?)"); + t!("This key contains {{ count_unparsed }} identities that could not be parsed as an email address.
    These identities can't be published on keys.openpgp.org. (why?)"); + t!("This key contains one revoked identity, which is not published."); + t!("(Why?)"); + t!("This key contains {{ count_revoked }} revoked identities, which are not published."); + t!("(Why?)"); + t!("Your keys have been successfully uploaded:"); + t!("Note: To make keys searchable by address, you must upload them individually."); + t!("Verifying your email address…"); + t!("If the process doesn't complete after a few seconds, ."); +} diff --git a/src/i18n.rs b/src/i18n.rs new file mode 100644 index 0000000..94b6e27 --- /dev/null +++ b/src/i18n.rs @@ -0,0 +1,102 @@ +use handlebars::{ + Context, Handlebars, Helper, HelperDef, HelperResult, Output, RenderContext, RenderError +}; + +use std::io; + + +pub struct I18NHelper { + catalogs: Vec<(&'static str, gettext::Catalog)>, +} + +impl I18NHelper { + pub fn new(catalogs: Vec<(&'static str, gettext::Catalog)>) -> Self { + Self { catalogs } + } + + pub fn get_catalog( + &self, + lang: &str, + ) -> &gettext::Catalog { + let (_, ref catalog) = self.catalogs + .iter() + .find(|(candidate, _)| *candidate == lang) + .unwrap_or_else(|| self.catalogs.get(0).unwrap()); + catalog + } + + // Traverse the fallback chain, + pub fn lookup<'a>( + &'a self, + lang: &str, + text_id: &'a str, + // args: Option<&HashMap<&str, FluentValue>>, + ) -> &'a str { + let catalog = self.get_catalog(lang); + catalog.gettext(text_id) + // format!("Unknown localization {}", text_id) + } +} + +#[derive(Default)] +struct StringOutput { + pub s: String, +} + +impl Output for StringOutput { + fn write(&mut self, seg: &str) -> Result<(), io::Error> { + self.s.push_str(seg); + Ok(()) + } +} + +impl HelperDef for I18NHelper { + fn call<'reg: 'rc, 'rc>( + &self, + h: &Helper<'reg, 'rc>, + reg: &'reg Handlebars, + context: &'rc Context, + rcx: &mut RenderContext<'reg>, + out: &mut dyn Output, + ) -> HelperResult { + let id = if let Some(id) = h.param(0) { + id + } else { + return Err(RenderError::new( + "{{text}} must have at least one parameter", + )); + }; + + let id = if let Some(id) = id.value().as_str() { + id + } else { + return Err(RenderError::new("{{text}} takes an identifier parameter")); + }; + + let rerender = h + .param(1) + .and_then(|p| p + .path() + .map(|v| v == "rerender") + ).unwrap_or(false); + + let lang = context + .data() + .get("lang") + .expect("Language not set in context") + .as_str() + .expect("Language must be string"); + + let response = self.lookup(lang, &id); + if rerender { + let data = rcx.evaluate(context, ".", false).unwrap(); + let response = reg.render_template(&response, data) + .map_err(RenderError::with)?; + out.write(&response).map_err(RenderError::with)?; + } else { + out.write(&response).map_err(RenderError::with)?; + } + Ok(()) + } +} + diff --git a/src/main.rs b/src/main.rs index 21ddfb2..962ce23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,8 @@ mod sealed_state; mod rate_limiter; mod dump; mod counters; +mod i18n; +mod gettext_strings; mod web; fn main() { diff --git a/src/web/manage.rs b/src/web/manage.rs index dc0d5bd..54ff3ac 100644 --- a/src/web/manage.rs +++ b/src/web/manage.rs @@ -5,7 +5,7 @@ use rocket_i18n::I18n; use failure::Fallible as Result; -use crate::web::{RequestOrigin, MyResponse, templates::General}; +use crate::web::{RequestOrigin, MyResponse}; use crate::web::vks_web; use crate::database::{Database, KeyDatabase, types::Email, types::Fingerprint}; use crate::mail; @@ -28,8 +28,6 @@ mod templates { pub base_uri: String, pub uid_status: Vec, pub token: String, - pub commit: String, - pub version: String, } #[derive(Serialize)] @@ -59,7 +57,7 @@ pub mod forms { #[get("/manage")] pub fn vks_manage() -> Result { - Ok(MyResponse::ok("manage/manage", General::default())) + Ok(MyResponse::ok_bare("manage/manage")) } #[get("/manage/")] @@ -94,8 +92,6 @@ pub fn vks_manage_key( uid_status, token, base_uri: request_origin.get_base_uri().to_owned(), - version: env!("VERGEN_SEMVER").to_string(), - commit: env!("VERGEN_SHA_SHORT").to_string(), }; MyResponse::ok("manage/manage_key", context) }, diff --git a/src/web/mod.rs b/src/web/mod.rs index 59061a8..59f3ce1 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -2,12 +2,13 @@ use rocket; use rocket::http::{Header, Status}; use rocket::request; use rocket::outcome::Outcome; -use rocket::response::NamedFile; +use rocket::response::{NamedFile, Responder, Response}; use rocket::config::Config; -use rocket_contrib::templates::Template; +use rocket_contrib::templates::{Template, Engines}; use rocket::http::uri::Uri; use rocket_contrib::json::JsonValue; use rocket::response::status::Custom; +use rocket_i18n::I18n; use rocket_prometheus::PrometheusMetrics; @@ -20,6 +21,7 @@ use std::path::PathBuf; use crate::mail; use crate::tokens; use crate::counters; +use crate::i18n::I18NHelper; use crate::rate_limiter::RateLimiter; use crate::database::{Database, KeyDatabase, Query}; @@ -40,10 +42,22 @@ use crate::web::maintenance::MaintenanceMode; use rocket::http::hyper::header::ContentDisposition; +pub struct HagridTemplate(&'static str, serde_json::Value); + +impl Responder<'static> for HagridTemplate { + fn respond_to(self, req: &rocket::Request) -> std::result::Result, Status> { + let HagridTemplate(tmpl, ctx) = self; + let i18n: I18n = req.guard().expect("Error parsing language"); + let origin: RequestOrigin = req.guard().expect("Error determining request origin"); + let layout_context = templates::HagridLayout::new(ctx, i18n, origin); + Template::render(tmpl, layout_context).respond_to(req) + } +} + #[derive(Responder)] pub enum MyResponse { #[response(status = 200, content_type = "html")] - Success(Template), + Success(HagridTemplate), #[response(status = 200, content_type = "plain")] Plain(String), #[response(status = 200, content_type = "application/pgp-keys")] @@ -53,11 +67,11 @@ pub enum MyResponse { #[response(status = 500, content_type = "html")] ServerError(Template), #[response(status = 404, content_type = "html")] - NotFound(Template), + NotFound(HagridTemplate), #[response(status = 404, content_type = "html")] NotFoundPlain(String), #[response(status = 400, content_type = "html")] - BadRequest(Template), + BadRequest(HagridTemplate), #[response(status = 400, content_type = "html")] BadRequestPlain(String), #[response(status = 503, content_type = "html")] @@ -69,8 +83,14 @@ pub enum MyResponse { } impl MyResponse { - pub fn ok(tmpl: &'static str, ctx: S) -> Self { - MyResponse::Success(Template::render(tmpl, ctx)) + pub fn ok(tmpl: &'static str, ctx: impl Serialize) -> Self { + let context_json = serde_json::to_value(ctx).unwrap(); + MyResponse::Success(HagridTemplate(tmpl, context_json)) + } + + pub fn ok_bare(tmpl: &'static str) -> Self { + let context_json = serde_json::to_value(templates::Bare { dummy: () }).unwrap(); + MyResponse::Success(HagridTemplate(tmpl, context_json)) } pub fn plain(s: String) -> Self { @@ -110,7 +130,7 @@ impl MyResponse { pub fn ise(e: failure::Error) -> Self { eprintln!("Internal error: {:?}", e); - let ctx = templates::FiveHundred{ + let ctx = templates::FiveHundred { internal_error: e.to_string(), version: env!("VERGEN_SEMVER").to_string(), commit: env!("VERGEN_SHA_SHORT").to_string(), @@ -119,12 +139,9 @@ impl MyResponse { } pub fn bad_request(template: &'static str, e: failure::Error) -> Self { - let ctx = templates::General { - error: Some(format!("{}", e)), - version: env!("VERGEN_SEMVER").to_string(), - commit: env!("VERGEN_SHA_SHORT").to_string(), - }; - MyResponse::BadRequest(Template::render(template, ctx)) + let ctx = templates::Error { error: format!("{}", e) }; + let context_json = serde_json::to_value(ctx).unwrap(); + MyResponse::BadRequest(HagridTemplate(template, context_json)) } pub fn bad_request_plain(message: impl Into) -> Self { @@ -137,18 +154,18 @@ impl MyResponse { pub fn not_found( tmpl: Option<&'static str>, - message: impl Into> + message: impl Into>, ) -> Self { - MyResponse::NotFound( - Template::render( - tmpl.unwrap_or("index"), - templates::General::new( - Some(message.into() - .unwrap_or_else(|| "Key not found".to_owned()))))) + let ctx = templates::Error { error: message.into() + .unwrap_or_else(|| "Key not found".to_owned()) }; + let context_json = serde_json::to_value(ctx).unwrap(); + MyResponse::NotFound(HagridTemplate(tmpl.unwrap_or("index"), context_json)) } } mod templates { + use super::{I18n, RequestOrigin}; + #[derive(Serialize)] pub struct FiveHundred { pub internal_error: String, @@ -157,44 +174,38 @@ mod templates { } #[derive(Serialize)] - pub struct General { + pub struct HagridLayout { pub error: Option, pub commit: String, pub version: String, + pub base_uri: String, + pub lang: String, + pub page: T, } #[derive(Serialize)] - pub struct About { - pub base_uri: String, - pub commit: String, - pub version: String, + pub struct Error { + pub error: String, } - impl About { - pub fn new(base_uri: impl Into) -> Self { + #[derive(Serialize)] + pub struct Bare { + // Dummy value to make sure {{#with page}} always passes + pub dummy: (), + } + + impl HagridLayout { + pub fn new(page: T, i18n: I18n, origin: RequestOrigin) -> Self { Self { - base_uri: base_uri.into(), + error: None, version: env!("VERGEN_SEMVER").to_string(), commit: env!("VERGEN_SHA_SHORT").to_string(), + base_uri: origin.get_base_uri().to_string(), + page: page, + lang: i18n.lang.to_string(), } } } - - impl General { - pub fn new(error: Option) -> Self { - Self { - error: error, - version: env!("VERGEN_SEMVER").to_string(), - commit: env!("VERGEN_SHA_SHORT").to_string(), - } - } - } - - impl Default for General { - fn default() -> Self { - Self::new(None) - } - } } pub struct HagridState { @@ -276,47 +287,49 @@ fn files(file: PathBuf, state: rocket::State) -> Option } #[get("/")] -fn root() -> Template { - Template::render("index", templates::General::default()) +fn root() -> MyResponse { + MyResponse::ok_bare("index") } #[get("/about")] -fn about() -> Template { - Template::render("about/about", templates::General::default()) +fn about() -> MyResponse { + MyResponse::ok_bare("about/about") } #[get("/about/news")] -fn news() -> Template { - Template::render("about/news", templates::General::default()) +fn news() -> MyResponse { + MyResponse::ok_bare("about/news") } #[get("/about/faq")] -fn faq() -> Template { - Template::render("about/faq", templates::General::default()) +fn faq() -> MyResponse { + MyResponse::ok_bare("about/faq") } #[get("/about/usage")] -fn usage(state: rocket::State) -> Template { - Template::render("about/usage", templates::About::new(state.base_uri.clone())) +fn usage() -> MyResponse { + MyResponse::ok_bare("about/usage") } #[get("/about/privacy")] -fn privacy() -> Template { - Template::render("about/privacy", templates::General::default()) +fn privacy() -> MyResponse { + MyResponse::ok_bare("about/privacy") } #[get("/about/api")] -fn apidoc() -> Template { - Template::render("about/api", templates::General::default()) +fn apidoc() -> MyResponse { + MyResponse::ok_bare("about/api") } #[get("/about/stats")] -fn stats() -> Template { - Template::render("about/stats", templates::General::default()) +fn stats() -> MyResponse { + MyResponse::ok_bare("about/stats") } #[get("/errors//