From 376b8f9bfbf6a5a26804a129a5df1f6c25d9c1f1 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Tue, 21 Feb 2023 14:29:24 +0900 Subject: [PATCH 1/5] Add `book.translations` When set, `build` runs a renderer for each translations after setting `book.language` to a subdirectory. For example, when it's set `["ko", "ja"]`, `mdbook build -d book` will generate the output: ``` book/ (default output) ko/ (ko output) ja/ (ja output) ``` This enables translation to be handled by preprocessors. For example, it could be PO-file based translation based on `book.language`. --- src/book/mod.rs | 27 ++++++++++++++++++++++----- src/config.rs | 3 +++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 75bbcc7144..a129d1826e 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -190,18 +190,27 @@ impl MDBook { info!("Book building has started"); for renderer in &self.renderers { - self.execute_build_process(&**renderer)?; + // build for the default language + self.execute_build_process(&**renderer, None)?; + // build for each translation language + for translation in &self.config.book.translations { + self.execute_build_process(&**renderer, Some(translation))?; + } } Ok(()) } /// Run the entire build process for a particular [`Renderer`]. - pub fn execute_build_process(&self, renderer: &dyn Renderer) -> Result<()> { + pub fn execute_build_process(&self, renderer: &dyn Renderer, translation: Option<&str>) -> Result<()> { let mut preprocessed_book = self.book.clone(); + let mut config = self.config.clone(); + if let Some(translation) = translation { + config.book.language = Some(translation.to_string()); + } let preprocess_ctx = PreprocessorContext::new( self.root.clone(), - self.config.clone(), + config, renderer.name().to_string(), ); @@ -213,7 +222,11 @@ impl MDBook { } let name = renderer.name(); - let build_dir = self.build_dir_for(name); + let mut build_dir = self.build_dir_for(name); + // Use sub-dir for each translation + if let Some(translation) = translation { + build_dir = build_dir.join(translation); + } let mut render_context = RenderContext::new( self.root.clone(), @@ -225,7 +238,11 @@ impl MDBook { .chapter_titles .extend(preprocess_ctx.chapter_titles.borrow_mut().drain()); - info!("Running the {} backend", renderer.name()); + info!("Running the {}{} backend", renderer.name(), if let Some(translation) = translation { + format!("[{}]", translation) + } else { + "".to_string() + }); renderer .render(&render_context) .with_context(|| "Rendering failed") diff --git a/src/config.rs b/src/config.rs index 0c367d8481..c1a4ae02ee 100644 --- a/src/config.rs +++ b/src/config.rs @@ -411,6 +411,8 @@ pub struct BookConfig { pub multilingual: bool, /// The main language of the book. pub language: Option, + /// The book's translations. + pub translations: Vec, } impl Default for BookConfig { @@ -422,6 +424,7 @@ impl Default for BookConfig { src: PathBuf::from("src"), multilingual: false, language: Some(String::from("en")), + translations: Vec::new(), } } } From 88289f5fadb1ccbb57660fc584753ee754e02be3 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Wed, 22 Feb 2023 15:19:13 +0900 Subject: [PATCH 2/5] Pass updated config to renderer --- src/book/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index a129d1826e..61b073ba08 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -210,7 +210,7 @@ impl MDBook { } let preprocess_ctx = PreprocessorContext::new( self.root.clone(), - config, + config.clone(), renderer.name().to_string(), ); @@ -231,7 +231,7 @@ impl MDBook { let mut render_context = RenderContext::new( self.root.clone(), preprocessed_book, - self.config.clone(), + config, build_dir, ); render_context From 96eab23c72e946cecb45a850785d7806eb7b7a8e Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Wed, 22 Feb 2023 15:30:42 +0900 Subject: [PATCH 3/5] Pass `translations` to HTML renderer --- src/renderer/html_handlebars/hbs_renderer.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index e170e2fcda..e1e99fd6f9 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -635,6 +635,12 @@ fn make_data( "language".to_owned(), json!(config.book.language.clone().unwrap_or_default()), ); + if !config.book.translations.is_empty() { + data.insert( + "translations".to_owned(), + json!(config.book.translations), + ); + } data.insert( "book_title".to_owned(), json!(config.book.title.clone().unwrap_or_default()), From e166a1f2a4fc523741c27d4ae4dade870459d563 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Wed, 22 Feb 2023 16:28:55 +0900 Subject: [PATCH 4/5] Pass `path_html` to HTML renderer When linking between translations, we can use `path_html` instead of changing .md to .html with `path`. --- src/renderer/html_handlebars/hbs_renderer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index e1e99fd6f9..a6874ffca9 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -94,6 +94,7 @@ impl HtmlHandlebars { }; ctx.data.insert("path".to_owned(), json!(path)); + ctx.data.insert("path_html".to_owned(), json!(filepath)); ctx.data.insert("content".to_owned(), json!(content)); ctx.data.insert("chapter_title".to_owned(), json!(ch.name)); ctx.data.insert("title".to_owned(), json!(title)); From ac295bbfcc9cd93007206dd23a29efd7bd5dc2ac Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Wed, 22 Feb 2023 16:30:36 +0900 Subject: [PATCH 5/5] Fixing tests and format `cargo test` and `cargo fmt --check` pass. --- src/book/mod.rs | 28 +++++++++++--------- src/config.rs | 1 + src/renderer/html_handlebars/hbs_renderer.rs | 5 +--- tests/init.rs | 4 +-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 61b073ba08..f7df3c93ac 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -202,7 +202,11 @@ impl MDBook { } /// Run the entire build process for a particular [`Renderer`]. - pub fn execute_build_process(&self, renderer: &dyn Renderer, translation: Option<&str>) -> Result<()> { + pub fn execute_build_process( + &self, + renderer: &dyn Renderer, + translation: Option<&str>, + ) -> Result<()> { let mut preprocessed_book = self.book.clone(); let mut config = self.config.clone(); if let Some(translation) = translation { @@ -228,21 +232,21 @@ impl MDBook { build_dir = build_dir.join(translation); } - let mut render_context = RenderContext::new( - self.root.clone(), - preprocessed_book, - config, - build_dir, - ); + let mut render_context = + RenderContext::new(self.root.clone(), preprocessed_book, config, build_dir); render_context .chapter_titles .extend(preprocess_ctx.chapter_titles.borrow_mut().drain()); - info!("Running the {}{} backend", renderer.name(), if let Some(translation) = translation { - format!("[{}]", translation) - } else { - "".to_string() - }); + info!( + "Running the {}{} backend", + renderer.name(), + if let Some(translation) = translation { + format!("[{}]", translation) + } else { + "".to_string() + } + ); renderer .render(&render_context) .with_context(|| "Rendering failed") diff --git a/src/config.rs b/src/config.rs index c1a4ae02ee..c2ac6247c3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -772,6 +772,7 @@ mod tests { multilingual: true, src: PathBuf::from("source"), language: Some(String::from("ja")), + ..Default::default() }; let build_should_be = BuildConfig { build_dir: PathBuf::from("outputs"), diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index a6874ffca9..0d7adce92e 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -637,10 +637,7 @@ fn make_data( json!(config.book.language.clone().unwrap_or_default()), ); if !config.book.translations.is_empty() { - data.insert( - "translations".to_owned(), - json!(config.book.translations), - ); + data.insert("translations".to_owned(), json!(config.book.translations)); } data.insert( "book_title".to_owned(), diff --git a/tests/init.rs b/tests/init.rs index 2b6ad507ce..98d7ae3806 100644 --- a/tests/init.rs +++ b/tests/init.rs @@ -29,7 +29,7 @@ fn base_mdbook_init_should_create_default_content() { let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap(); assert_eq!( contents, - "[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"src\"\n" + "[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"src\"\ntranslations = []\n" ); } @@ -96,7 +96,7 @@ fn run_mdbook_init_with_custom_book_and_src_locations() { let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap(); assert_eq!( contents, - "[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"in\"\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nextra-watch-dirs = []\nuse-default-preprocessors = true\n" + "[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"in\"\ntranslations = []\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nextra-watch-dirs = []\nuse-default-preprocessors = true\n" ); }