From 5fed5e866dad28322f401617e373ebba68dde606 Mon Sep 17 00:00:00 2001 From: Ruin0x11 Date: Wed, 15 Sep 2021 14:29:30 -0700 Subject: [PATCH] Update mdBook manual to have information about translations --- guide/src/en/cli/init.md | 14 ++- guide/src/en/for_developers/preprocessors.md | 2 +- guide/src/en/format/configuration/README.md | 4 +- .../en/format/configuration/translations.md | 86 +++++++++++++++++++ guide/src/en/misc/contributors.md | 1 + src/config.rs | 14 ++- src/utils/mod.rs | 52 +++++------ tests/localized_book/src/en/README.md | 2 + tests/localized_book/src/ja/SUMMARY.md | 2 +- .../src/ja/inline-link-fallbacks.md | 2 +- 10 files changed, 139 insertions(+), 40 deletions(-) create mode 100644 guide/src/en/format/configuration/translations.md diff --git a/guide/src/en/cli/init.md b/guide/src/en/cli/init.md index 99c0be09..d4c2a3f6 100644 --- a/guide/src/en/cli/init.md +++ b/guide/src/en/cli/init.md @@ -14,13 +14,19 @@ up for you: ```bash book-test/ ├── book +├── book.toml └── src - ├── chapter_1.md - └── SUMMARY.md + └── en + ├── chapter_1.md + └── SUMMARY.md ``` -- The `src` directory is where you write your book in markdown. It contains all - the source files, configuration files, etc. +- The `src` directory is where you write your book in Markdown. It contains all + the source files for each translation of your book. By default, a directory + for the English translation is created, `src/en`. + +- The `book.toml` file holds configuration about how your book gets rendered. + See the [configuration](../format/config.md) section for more details. - The `book` directory is where your book is rendered. All the output is ready to be uploaded to a server to be seen by your audience. diff --git a/guide/src/en/for_developers/preprocessors.md b/guide/src/en/for_developers/preprocessors.md index 074d1c3c..5c8d2be5 100644 --- a/guide/src/en/for_developers/preprocessors.md +++ b/guide/src/en/for_developers/preprocessors.md @@ -56,7 +56,7 @@ be adapted for other preprocessors. ```rust // nop-preprocessors.rs -{{#include ../../../examples/nop-preprocessor.rs}} +{{#include ../../../../examples/nop-preprocessor.rs}} ``` diff --git a/guide/src/en/format/configuration/README.md b/guide/src/en/format/configuration/README.md index 4dcb5852..6a23cfa0 100644 --- a/guide/src/en/format/configuration/README.md +++ b/guide/src/en/format/configuration/README.md @@ -4,9 +4,11 @@ This section details the configuration options available in the ***book.toml***: - **[General]** configuration including the `book`, `rust`, `build` sections - **[Preprocessor]** configuration for default and custom book preprocessors - **[Renderer]** configuration for the HTML, Markdown and custom renderers +- **[Translations]** configuration for books written in more than one language - **[Environment Variable]** configuration for overriding configuration options in your environment [General]: general.md [Preprocessor]: preprocessors.md [Renderer]: renderers.md -[Environment Variable]: environment-variables.md \ No newline at end of file +[Translations]: translations.md +[Environment Variable]: environment-variables.md diff --git a/guide/src/en/format/configuration/translations.md b/guide/src/en/format/configuration/translations.md new file mode 100644 index 00000000..1962552b --- /dev/null +++ b/guide/src/en/format/configuration/translations.md @@ -0,0 +1,86 @@ +## Translations + +It's possible to write your book in more than one language and bundle all of its +translations into a single output folder, with the ability for readers to switch +between each one in the rendered output. The available languages for your book +are defined in the `[language]` table: + +```toml +[language.en] +name = "English" + +[language.ja] +name = "日本語" +title = "本のサンプル" +description = "この本は実例です。" +authors = ["Ruin0x11"] +``` + +Each language must have a human-readable `name` defined. Also, if the +`[language]` table is defined, you must define `book.language` to be a key of +this table, which will indicate the language whose files will be used for +fallbacks if a page is missing in a translation. + +The `title` and `description` fields, if defined, will override the ones set in +the `[book]` section. This way you can translate the book's title and +description. `authors` provides a list of this translation's authors. + +After defining a new language like `[language.ja]`, add a new subdirectory +`src/ja` and create your `SUMMARY.md` and other files there. + +> **Note:** Whether or not the `[language]` table is defined changes the format +> of the `src` directory that mdBook expects to see. If there is no `[language]` +> table, mdBook will treat the `src` directory as a single translation of the +> book, with `SUMMARY.md` at the root: +> +> ``` +> ├── book.toml +> └── src +> ├── chapter +> │ ├── 1.md +> │ ├── 2.md +> │ └── README.md +> ├── README.md +> └── SUMMARY.md +> ``` +> +> If the `[language]` table is defined, mdBook will instead expect to find +> subdirectories under `src` named after the keys in the table: +> +> ``` +> ├── book.toml +> └── src +> ├── en +> │ ├── chapter +> │ │ ├── 1.md +> │ │ ├── 2.md +> │ │ └── README.md +> │ ├── README.md +> │ └── SUMMARY.md +> └── ja +> ├── chapter +> │ ├── 1.md +> │ ├── 2.md +> │ └── README.md +> ├── README.md +> └── SUMMARY.md +> ``` + +If the `[language]` table is used, you can pass the `-l ` argument +to commands like `mdbook build` to build the book for only a single language. In +this example, `` can be `en` or `ja`. + +Some extra notes on translations: + +- In a translation's `SUMMARY.md` or inside Markdown files, you can link to + pages, images or other files that don't exist in the current translation, but + do exist in the default translation. This is so you can have a fallback in + case new pages get added in the default language that haven't been translated + yet. +- Each translation can have its own `SUMMARY.md` with differing content from + other translations. Even if the translation's summary goes out of sync with + the default language, the links will continue to work so long as the pages + exist in either translation. +- Each translation can have its own pages listed in `SUMMARY.md` that don't + exist in the default translation at all, in case extra information specific to + that language is needed. diff --git a/guide/src/en/misc/contributors.md b/guide/src/en/misc/contributors.md index 362a21fe..a1acd279 100644 --- a/guide/src/en/misc/contributors.md +++ b/guide/src/en/misc/contributors.md @@ -20,5 +20,6 @@ shout-out to them! - Vivek Akupatni ([apatniv](https://github.com/apatniv)) - Eric Huss ([ehuss](https://github.com/ehuss)) - Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg)) +- [Ruin0x11](https://github.com/Ruin0x11) If you feel you're missing from this list, feel free to add yourself in a PR. diff --git a/src/config.rs b/src/config.rs index 98a42a7d..bccc288d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1020,6 +1020,7 @@ mod tests { assert_eq!(got.rust, rust_should_be); assert_eq!(got.html_config().unwrap(), html_should_be); assert_eq!(got.language, language_should_be); + assert_eq!(got.default_language(), Some(String::from("ja"))); } #[test] @@ -1369,7 +1370,18 @@ mod tests { #[test] #[should_panic(expected = "Invalid configuration file")] - fn validate_config_default_language_must_exist_in_languages_table() { + fn book_language_without_languages_table() { + let src = r#" + [book] + language = "en" + "#; + + Config::from_str(src).unwrap(); + } + + #[test] + #[should_panic(expected = "Invalid configuration file")] + fn default_language_must_exist_in_languages_table() { let src = r#" [language.ja] name = "日本語" diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 82056a55..7b9a7596 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -10,7 +10,7 @@ use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; use std::borrow::Cow; use std::fmt::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub use self::string::{ take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines, @@ -99,9 +99,9 @@ pub fn id_from_content(content: &str) -> String { normalize_id(trimmed) } -fn rewrite_if_missing( +fn rewrite_if_missing>( fixed_link: &mut String, - path_to_dest: &PathBuf, + path_to_dest: P, dest: &str, src_dir: &PathBuf, language: &str, @@ -114,7 +114,7 @@ fn rewrite_if_missing( // if the file exists. let mut path_on_disk = src_dir.clone(); path_on_disk.push(language); - path_on_disk.push(path_to_dest); + path_on_disk.push(path_to_dest.as_ref()); path_on_disk.push(dest); debug!("Checking if {} exists", path_on_disk.display()); @@ -122,7 +122,7 @@ fn rewrite_if_missing( // Now see if the file exists in the fallback language directory (like "src/en"). let mut fallback_path = src_dir.clone(); fallback_path.push(fallback_language); - fallback_path.push(path_to_dest); + fallback_path.push(path_to_dest.as_ref()); fallback_path.push(dest); debug!( @@ -133,7 +133,7 @@ fn rewrite_if_missing( // We can fall back to this link. Get enough parent directories to // reach the root source directory, append the fallback language // directory to it, the prepend the whole thing to the link. - let mut relative_path = PathBuf::from(path_to_dest); + let mut relative_path = PathBuf::from(path_to_dest.as_ref()); relative_path.push(dest); let mut path_to_fallback_src = fs::path_to_root(&relative_path); @@ -158,7 +158,6 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a> if base.ends_with(".md") { base.replace_range(base.len() - 3.., ".html"); } - info!("{:?} {:?}", base, dest); return format!("{}{}", base, dest).into(); } else { return dest; @@ -173,6 +172,8 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a> let mut fixed_link = String::new(); if let Some(ctx) = ctx { + let base = ctx.path.parent().expect("path can't be empty"); + // If the book is multilingual, check if the file actually // exists, and if not rewrite the link to the fallback // language's page. @@ -180,7 +181,7 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a> if let Some(fallback_language) = &ctx.fallback_language { rewrite_if_missing( &mut fixed_link, - &ctx.path, + &base, &dest, &ctx.src_dir, &language, @@ -190,13 +191,7 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a> } if ctx.prepend_parent { - let base = ctx - .path - .parent() - .expect("path can't be empty") - .to_str() - .expect("utf-8 paths only"); - + let base = base.to_str().expect("utf-8 paths only"); if !base.is_empty() { write!(fixed_link, "{}/", base).unwrap(); } @@ -562,27 +557,22 @@ more text with spaces ); }; - test("../b/summary.md", "a", true, "../b/summary.html"); - test("../b/summary.md", "a", false, "../../en/../b/summary.html"); - test("../c/summary.md", "a/b", true, "../c/summary.html"); + test("../b/summary.md", "a.md", true, "../b/summary.html"); + test( + "../b/summary.md", + "a.md", + false, + "../../en/../b/summary.html", + ); + test("../c/summary.md", "a/b.md", true, "../c/summary.html"); test( "../c/summary.md", - "a/b", + "a/b.md", false, "../../../en/../c/summary.html", ); - test( - "#translations", - "config.md", - true, - "#translations", - ); - test( - "#translations", - "config.md", - false, - "#translations", - ); + test("#translations", "config.md", true, "#translations"); + test("#translations", "config.md", false, "#translations"); } } diff --git a/tests/localized_book/src/en/README.md b/tests/localized_book/src/en/README.md index c43c8b58..b3f713ba 100644 --- a/tests/localized_book/src/en/README.md +++ b/tests/localized_book/src/en/README.md @@ -1,3 +1,5 @@ # Localized Book This is a test of the book localization features. + +Select a language from the dropdown to see a translation of the current page. diff --git a/tests/localized_book/src/ja/SUMMARY.md b/tests/localized_book/src/ja/SUMMARY.md index 47cfa410..0aacc6e5 100644 --- a/tests/localized_book/src/ja/SUMMARY.md +++ b/tests/localized_book/src/ja/SUMMARY.md @@ -5,5 +5,5 @@ - [第一節](chapter/1.md) - [第二節](chapter/2.md) - [Untranslated Page](untranslated-page.md) -- [日本語専用のページ](translation-local-page.md) - [内部リンクの入れ替え](inline-link-fallbacks.md) +- [日本語専用のページ](translation-local-page.md) diff --git a/tests/localized_book/src/ja/inline-link-fallbacks.md b/tests/localized_book/src/ja/inline-link-fallbacks.md index 009a3865..2da1d166 100644 --- a/tests/localized_book/src/ja/inline-link-fallbacks.md +++ b/tests/localized_book/src/ja/inline-link-fallbacks.md @@ -8,7 +8,7 @@ If inline link substitution works, then an image should appear below, sourced fr Here is an [inline link](translation-local-page.md) to an existing page in this translation. -Here is an [inline link](missing-summary-chapter.md) to a page missing from this translation's SUMMARY.md. It should have been modified to point to the page in the English version of the book. +Here is an [inline link](missing-summary-chapter.md) to a page missing from this translation's `SUMMARY.md`. It should have been modified to point to the page in the English version of the book. Also, here is an [inline link](blah.md) to a page missing from both translations. It should point to this language's 404 page.