diff --git a/data/_html-template/_layouts/page.hbs b/data/_html-template/_layouts/page.hbs index 457cfac9..1e4a2ffb 100644 --- a/data/_html-template/_layouts/page.hbs +++ b/data/_html-template/_layouts/page.hbs @@ -43,6 +43,10 @@ @@ -55,8 +59,8 @@ -
- {{#translations}}{{/translations}} +

{{ title }}

diff --git a/data/_html-template/_stylus/page.styl b/data/_html-template/_stylus/page.styl index c0bd1883..a8a845f2 100644 --- a/data/_html-template/_stylus/page.styl +++ b/data/_html-template/_stylus/page.styl @@ -54,12 +54,30 @@ img { max-width: 100%; } } -div.translations { - float: left - height: 50px +div.translation-indexes { ul { margin: 10px 0 0 0 - display: inline-block + padding: 0 + text-align: center + list-style-type: none + li { + display: inline-block + padding: 0.2em 0.5em + a { + text-decoration: none + &:hover { text-decoration: underline; } + } + } + } +} + +div.translation-links { + float: left + height: 50px + ul { + margin: 10px 0 0 0 + padding: 0 0 0 1em + text-align: left list-style-type: none li { display: inline-block diff --git a/data/_html-template/css/book.css b/data/_html-template/css/book.css index 5ea46adb..d1a8f589 100644 --- a/data/_html-template/css/book.css +++ b/data/_html-template/css/book.css @@ -151,23 +151,40 @@ table thead td { .content img { max-width: 100%; } -div.translations { - float: left; - height: 50px; -} -div.translations ul { +div.translation-indexes ul { margin: 10px 0 0 0; - display: inline-block; + padding: 0; + text-align: center; list-style-type: none; } -div.translations ul li { +div.translation-indexes ul li { display: inline-block; padding: 0.2em 0.5em; } -div.translations ul li a { +div.translation-indexes ul li a { text-decoration: none; } -div.translations ul li a:hover { +div.translation-indexes ul li a:hover { + text-decoration: underline; +} +div.translation-links { + float: left; + height: 50px; +} +div.translation-links ul { + margin: 10px 0 0 0; + padding: 0 0 0 1em; + text-align: left; + list-style-type: none; +} +div.translation-links ul li { + display: inline-block; + padding: 0.2em 0.5em; +} +div.translation-links ul li a { + text-decoration: none; +} +div.translation-links ul li a:hover { text-decoration: underline; } .menu-bar { diff --git a/src/book/chapter.rs b/src/book/chapter.rs index f0d9e731..dd0c986c 100644 --- a/src/book/chapter.rs +++ b/src/book/chapter.rs @@ -69,6 +69,9 @@ pub struct Chapter { /// Links to the corresponding translations. pub translation_links: Option>, + /// An identifier string that can allow linking translations with different paths. + pub translation_id: Option, + /// The author of the chapter, or the book. pub authors: Option>, @@ -93,6 +96,7 @@ impl Default for Chapter { src_path: None, dest_path: None, translation_links: None, + translation_id: None, authors: None, translators: None, description: None, @@ -196,6 +200,10 @@ impl Chapter { } } + if let Some(a) = data.get("translation_id") { + self.translation_id = Some(a.to_string().replace("\"", "")); + } + // Author name as a hash key. if let Some(a) = data.get("author") { if let Some(b) = a.as_str() { @@ -294,24 +302,35 @@ impl Chapter { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TranslationLink { + /// Language code, such as 'en' or 'fr'. pub code: String, - pub link: String, + /// The `` link to the translation. `None` indicates that the + /// language is part of the book, but there isn't a translation for this + /// chapter. + pub link: Option, } impl Default for TranslationLink { fn default() -> TranslationLink { TranslationLink { - code: "".to_string(), - link: "".to_string(), + code: "--".to_string(), + link: None, } } } impl TranslationLink { - pub fn new(code: String, link: String) -> TranslationLink { + pub fn new(code: String) -> TranslationLink { TranslationLink { code: code, - link: link, + link: None, + } + } + + pub fn new_with_link(code: String, link: String) -> TranslationLink { + TranslationLink { + code: code, + link: Some(link), } } } @@ -323,7 +342,9 @@ impl From for TranslationLink { link.code = x.to_string().replace("\"", ""); } if let Some(x) = data.get("link") { - link.link = x.to_string().replace("\"", ""); + link.link = Some(x.to_string().replace("\"", "")); + } else { + link.link = None; } link } diff --git a/src/book/mod.rs b/src/book/mod.rs index d70e1e87..c6ed943f 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -448,7 +448,7 @@ impl MDBook { } /// prepare a Vec of default links to point to the index.html of each translation - fn translation_index_links(&mut self) -> Vec { + pub fn translation_index_links(&self) -> Option> { let mut default_links: Vec = vec![]; let mut keys = self.translations.keys() @@ -456,6 +456,11 @@ impl MDBook { .collect::>(); keys.sort(); + if keys.len() < 2 { + // There is only one language. No need to display translation links. + return None; + } + for key in keys { let book = self.translations.get(&key).unwrap(); @@ -463,25 +468,37 @@ impl MDBook { let a = book.config.dest.strip_prefix(&z).unwrap(); let b = a.join("index.html"); let c = b.to_str().unwrap(); - let link = TranslationLink::new(key, c.to_string()); + let link = TranslationLink::new_with_link(key, c.to_string()); default_links.push(link); } - default_links + Some(default_links) } fn set_translation_links(&mut self, content: &TocContent) -> TocContent { - let default_links = self.translation_index_links(); - + let mut final_links: BTreeMap = BTreeMap::new(); let mut newcontent: TocContent = content.clone(); - match newcontent.chapter.translation_links { - Some(_) => {}, - None => { - newcontent.chapter.translation_links = Some(default_links); - } + // Start by adding the code of each language but no links. These will + // render as gray tags. + for key in self.translations.keys() { + final_links.insert(key.clone(), TranslationLink::new(key.clone())); } + // Take the links parsed from the chapter's TOML header + + match newcontent.chapter.translation_links { + Some(links) => { + for i in links.iter() { + final_links.insert(i.clone().code, i.clone()); + } + }, + None => {}, + } + + let a: Vec = final_links.values().map(|x| x.clone()).collect(); + newcontent.chapter.translation_links = Some(a); + newcontent } diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 38c157a6..9d4f06b3 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -1,7 +1,7 @@ use renderer::html_handlebars::helpers; use renderer::Renderer; use book::{MDBook, Book}; -use book::chapter::Chapter; +use book::chapter::{Chapter, TranslationLink}; use book::toc::{TocItem, TocContent}; use utils; use FILES; @@ -127,6 +127,8 @@ impl Renderer for HtmlHandlebars { debug!("[fn]: render"); let mut handlebars = Handlebars::new(); + let translation_indexes = book_project.translation_index_links(); + // Render the chapters of each book for (key, book) in &book_project.translations { @@ -164,7 +166,8 @@ impl Renderer for HtmlHandlebars { handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc)); handlebars.register_helper("previous", Box::new(helpers::navigation::previous)); handlebars.register_helper("next", Box::new(helpers::navigation::next)); - handlebars.register_helper("translations", Box::new(helpers::translations::TranslationsHelper)); + handlebars.register_helper("translation-links", Box::new(helpers::translations::TranslationLinksHelper)); + handlebars.register_helper("translation-indexes", Box::new(helpers::translations::TranslationIndexesHelper)); // Check if book's dest directory exists @@ -196,12 +199,12 @@ impl Renderer for HtmlHandlebars { // almost the same as process_chapter(), but we have to // manipulate path_to_root in data and rendered_path - let mut data = try!(make_data(&book, &chapter, &book_project.livereload_script)); + let mut data = try!(make_data(&book, &chapter, &translation_indexes, &book_project.livereload_script)); data.remove("path_to_root"); data.insert("path_to_root".to_owned(), "".to_json()); - // Rendere the handlebars template with the data + // Render the handlebars template with the data debug!("[*]: Render template"); let rendered_content = try!(handlebars.render("page", &data)); @@ -221,7 +224,7 @@ impl Renderer for HtmlHandlebars { } // Render a file for every entry in the book - try!(self.process_items(&book.toc, &book, &book_project.livereload_script, &handlebars)); + try!(self.process_items(&book.toc, &book, &translation_indexes, &book_project.livereload_script, &handlebars)); // Write print.html if let Some(content) = self.collect_print_content_markdown(&book.toc, &book) { @@ -231,7 +234,7 @@ impl Renderer for HtmlHandlebars { chapter.set_dest_path(PathBuf::from("print.html")); chapter.content = Some(content); - try!(self.process_chapter(&chapter, &book, &None, &handlebars)); + try!(self.process_chapter(&chapter, &book, &None, &None, &handlebars)); } } @@ -244,6 +247,7 @@ impl HtmlHandlebars { fn process_items(&self, items: &Vec, book: &Book, + translation_indexes: &Option>, livereload_script: &Option, handlebars: &Handlebars) -> Result<(), Box> { @@ -254,11 +258,11 @@ impl HtmlHandlebars { TocItem::Unnumbered(ref i) | TocItem::Unlisted(ref i) => { if let Some(_) = i.chapter.get_dest_path() { - try!(self.process_chapter(&i.chapter, book, livereload_script, handlebars)); + try!(self.process_chapter(&i.chapter, book, translation_indexes, livereload_script, handlebars)); } if let Some(ref subs) = i.sub_items { - try!(self.process_items(&subs, book, livereload_script, handlebars)); + try!(self.process_items(&subs, book, translation_indexes, livereload_script, handlebars)); } }, @@ -303,11 +307,12 @@ impl HtmlHandlebars { fn process_chapter(&self, chapter: &Chapter, book: &Book, + translation_indexes: &Option>, livereload_script: &Option, handlebars: &Handlebars) -> Result<(), Box> { - let data = try!(make_data(book, chapter, livereload_script)); + let data = try!(make_data(book, chapter, translation_indexes, livereload_script)); // Render the handlebars template with the data debug!("[*]: Render template"); @@ -339,6 +344,7 @@ impl HtmlHandlebars { fn make_data(book: &Book, chapter: &Chapter, + translation_indexes: &Option>, livereload_script: &Option) -> Result, Box> { @@ -402,8 +408,12 @@ fn make_data(book: &Book, }, } + if let Some(ref links) = *translation_indexes { + data.insert("translation-indexes".to_owned(), links.to_json()); + } + if let Some(ref links) = chapter.translation_links { - data.insert("translation_links".to_owned(), links.to_json()); + data.insert("translation-links".to_owned(), links.to_json()); } let chapters = try!(items_to_chapters(&book.toc, &book)); diff --git a/src/renderer/html_handlebars/helpers/translations.rs b/src/renderer/html_handlebars/helpers/translations.rs index 4b11657c..6d009d91 100644 --- a/src/renderer/html_handlebars/helpers/translations.rs +++ b/src/renderer/html_handlebars/helpers/translations.rs @@ -5,19 +5,55 @@ use serde_json; use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context}; #[derive(Clone, Copy)] -pub struct TranslationsHelper; +pub struct TranslationLinksHelper; -impl HelperDef for TranslationsHelper { +impl HelperDef for TranslationLinksHelper { fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let translation_links = c.navigate(rc.get_path(), &VecDeque::new(), "translation_links"); + let translation_links = c.navigate(rc.get_path(), &VecDeque::new(), "translation-links"); let decoded: Vec> = serde_json::from_str(&translation_links.to_string()).unwrap(); if decoded.len() == 0 { return Ok(()); } - try!(rc.writer.write("