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}}
+
+ {{#translation-links}}{{/translation-links}}
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("".as_bytes()));
+ try!(rc.writer.write("".as_bytes()));
+
+ for item in decoded {
+ let empty = "".to_string();
+ let link = item.get("link").unwrap_or(&empty);
+ let code = item.get("code").unwrap_or(&empty);
+
+ // None value in the link becomes an empty string
+ let text = if link.len() == 0 {
+ format!("- {}
", code)
+ } else {
+ format!("- {}
", link, code)
+ };
+
+ try!(rc.writer.write(text.as_bytes()));
+ }
+
+ try!(rc.writer.write("
".as_bytes()));
+
+ Ok(())
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct TranslationIndexesHelper;
+
+impl HelperDef for TranslationIndexesHelper {
+ fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+
+ let translation_indexes = c.navigate(rc.get_path(), &VecDeque::new(), "translation-indexes");
+ let decoded: Vec> = serde_json::from_str(&translation_indexes.to_string()).unwrap();
+
+ if decoded.len() == 0 {
+ return Ok(());
+ }
+
+ try!(rc.writer.write("".as_bytes()));
for item in decoded {
let empty = "".to_string();