Update mdBook manual to have information about translations
This commit is contained in:
parent
09a8b66e87
commit
5fed5e866d
@ -14,13 +14,19 @@ up for you:
|
|||||||
```bash
|
```bash
|
||||||
book-test/
|
book-test/
|
||||||
├── book
|
├── book
|
||||||
|
├── book.toml
|
||||||
└── src
|
└── src
|
||||||
|
└── en
|
||||||
├── chapter_1.md
|
├── chapter_1.md
|
||||||
└── SUMMARY.md
|
└── SUMMARY.md
|
||||||
```
|
```
|
||||||
|
|
||||||
- The `src` directory is where you write your book in markdown. It contains all
|
- The `src` directory is where you write your book in Markdown. It contains all
|
||||||
the source files, configuration files, etc.
|
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
|
- 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.
|
to be uploaded to a server to be seen by your audience.
|
||||||
|
@ -56,7 +56,7 @@ be adapted for other preprocessors.
|
|||||||
```rust
|
```rust
|
||||||
// nop-preprocessors.rs
|
// nop-preprocessors.rs
|
||||||
|
|
||||||
{{#include ../../../examples/nop-preprocessor.rs}}
|
{{#include ../../../../examples/nop-preprocessor.rs}}
|
||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -4,9 +4,11 @@ This section details the configuration options available in the ***book.toml***:
|
|||||||
- **[General]** configuration including the `book`, `rust`, `build` sections
|
- **[General]** configuration including the `book`, `rust`, `build` sections
|
||||||
- **[Preprocessor]** configuration for default and custom book preprocessors
|
- **[Preprocessor]** configuration for default and custom book preprocessors
|
||||||
- **[Renderer]** configuration for the HTML, Markdown and custom renderers
|
- **[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
|
- **[Environment Variable]** configuration for overriding configuration options in your environment
|
||||||
|
|
||||||
[General]: general.md
|
[General]: general.md
|
||||||
[Preprocessor]: preprocessors.md
|
[Preprocessor]: preprocessors.md
|
||||||
[Renderer]: renderers.md
|
[Renderer]: renderers.md
|
||||||
|
[Translations]: translations.md
|
||||||
[Environment Variable]: environment-variables.md
|
[Environment Variable]: environment-variables.md
|
86
guide/src/en/format/configuration/translations.md
Normal file
86
guide/src/en/format/configuration/translations.md
Normal file
@ -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 <language id>` argument
|
||||||
|
to commands like `mdbook build` to build the book for only a single language. In
|
||||||
|
this example, `<language id>` 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.
|
@ -20,5 +20,6 @@ shout-out to them!
|
|||||||
- Vivek Akupatni ([apatniv](https://github.com/apatniv))
|
- Vivek Akupatni ([apatniv](https://github.com/apatniv))
|
||||||
- Eric Huss ([ehuss](https://github.com/ehuss))
|
- Eric Huss ([ehuss](https://github.com/ehuss))
|
||||||
- Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg))
|
- 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.
|
If you feel you're missing from this list, feel free to add yourself in a PR.
|
||||||
|
@ -1020,6 +1020,7 @@ mod tests {
|
|||||||
assert_eq!(got.rust, rust_should_be);
|
assert_eq!(got.rust, rust_should_be);
|
||||||
assert_eq!(got.html_config().unwrap(), html_should_be);
|
assert_eq!(got.html_config().unwrap(), html_should_be);
|
||||||
assert_eq!(got.language, language_should_be);
|
assert_eq!(got.language, language_should_be);
|
||||||
|
assert_eq!(got.default_language(), Some(String::from("ja")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1369,7 +1370,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Invalid configuration file")]
|
#[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#"
|
let src = r#"
|
||||||
[language.ja]
|
[language.ja]
|
||||||
name = "日本語"
|
name = "日本語"
|
||||||
|
@ -10,7 +10,7 @@ use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
|
|||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub use self::string::{
|
pub use self::string::{
|
||||||
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
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)
|
normalize_id(trimmed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_if_missing(
|
fn rewrite_if_missing<P: AsRef<Path>>(
|
||||||
fixed_link: &mut String,
|
fixed_link: &mut String,
|
||||||
path_to_dest: &PathBuf,
|
path_to_dest: P,
|
||||||
dest: &str,
|
dest: &str,
|
||||||
src_dir: &PathBuf,
|
src_dir: &PathBuf,
|
||||||
language: &str,
|
language: &str,
|
||||||
@ -114,7 +114,7 @@ fn rewrite_if_missing(
|
|||||||
// if the file exists.
|
// if the file exists.
|
||||||
let mut path_on_disk = src_dir.clone();
|
let mut path_on_disk = src_dir.clone();
|
||||||
path_on_disk.push(language);
|
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);
|
path_on_disk.push(dest);
|
||||||
|
|
||||||
debug!("Checking if {} exists", path_on_disk.display());
|
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").
|
// Now see if the file exists in the fallback language directory (like "src/en").
|
||||||
let mut fallback_path = src_dir.clone();
|
let mut fallback_path = src_dir.clone();
|
||||||
fallback_path.push(fallback_language);
|
fallback_path.push(fallback_language);
|
||||||
fallback_path.push(path_to_dest);
|
fallback_path.push(path_to_dest.as_ref());
|
||||||
fallback_path.push(dest);
|
fallback_path.push(dest);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
@ -133,7 +133,7 @@ fn rewrite_if_missing(
|
|||||||
// We can fall back to this link. Get enough parent directories to
|
// We can fall back to this link. Get enough parent directories to
|
||||||
// reach the root source directory, append the fallback language
|
// reach the root source directory, append the fallback language
|
||||||
// directory to it, the prepend the whole thing to the link.
|
// 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);
|
relative_path.push(dest);
|
||||||
|
|
||||||
let mut path_to_fallback_src = fs::path_to_root(&relative_path);
|
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") {
|
if base.ends_with(".md") {
|
||||||
base.replace_range(base.len() - 3.., ".html");
|
base.replace_range(base.len() - 3.., ".html");
|
||||||
}
|
}
|
||||||
info!("{:?} {:?}", base, dest);
|
|
||||||
return format!("{}{}", base, dest).into();
|
return format!("{}{}", base, dest).into();
|
||||||
} else {
|
} else {
|
||||||
return dest;
|
return dest;
|
||||||
@ -173,6 +172,8 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a>
|
|||||||
let mut fixed_link = String::new();
|
let mut fixed_link = String::new();
|
||||||
|
|
||||||
if let Some(ctx) = ctx {
|
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
|
// If the book is multilingual, check if the file actually
|
||||||
// exists, and if not rewrite the link to the fallback
|
// exists, and if not rewrite the link to the fallback
|
||||||
// language's page.
|
// 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 {
|
if let Some(fallback_language) = &ctx.fallback_language {
|
||||||
rewrite_if_missing(
|
rewrite_if_missing(
|
||||||
&mut fixed_link,
|
&mut fixed_link,
|
||||||
&ctx.path,
|
&base,
|
||||||
&dest,
|
&dest,
|
||||||
&ctx.src_dir,
|
&ctx.src_dir,
|
||||||
&language,
|
&language,
|
||||||
@ -190,13 +191,7 @@ fn fix<'a>(dest: CowStr<'a>, ctx: Option<&RenderMarkdownContext>) -> CowStr<'a>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.prepend_parent {
|
if ctx.prepend_parent {
|
||||||
let base = ctx
|
let base = base.to_str().expect("utf-8 paths only");
|
||||||
.path
|
|
||||||
.parent()
|
|
||||||
.expect("path can't be empty")
|
|
||||||
.to_str()
|
|
||||||
.expect("utf-8 paths only");
|
|
||||||
|
|
||||||
if !base.is_empty() {
|
if !base.is_empty() {
|
||||||
write!(fixed_link, "{}/", base).unwrap();
|
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.md", true, "../b/summary.html");
|
||||||
test("../b/summary.md", "a", false, "../../en/../b/summary.html");
|
test(
|
||||||
test("../c/summary.md", "a/b", true, "../c/summary.html");
|
"../b/summary.md",
|
||||||
|
"a.md",
|
||||||
|
false,
|
||||||
|
"../../en/../b/summary.html",
|
||||||
|
);
|
||||||
|
test("../c/summary.md", "a/b.md", true, "../c/summary.html");
|
||||||
test(
|
test(
|
||||||
"../c/summary.md",
|
"../c/summary.md",
|
||||||
"a/b",
|
"a/b.md",
|
||||||
false,
|
false,
|
||||||
"../../../en/../c/summary.html",
|
"../../../en/../c/summary.html",
|
||||||
);
|
);
|
||||||
test(
|
test("#translations", "config.md", true, "#translations");
|
||||||
"#translations",
|
test("#translations", "config.md", false, "#translations");
|
||||||
"config.md",
|
|
||||||
true,
|
|
||||||
"#translations",
|
|
||||||
);
|
|
||||||
test(
|
|
||||||
"#translations",
|
|
||||||
"config.md",
|
|
||||||
false,
|
|
||||||
"#translations",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
# Localized Book
|
# Localized Book
|
||||||
|
|
||||||
This is a test of the book localization features.
|
This is a test of the book localization features.
|
||||||
|
|
||||||
|
Select a language from the dropdown to see a translation of the current page.
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
- [第一節](chapter/1.md)
|
- [第一節](chapter/1.md)
|
||||||
- [第二節](chapter/2.md)
|
- [第二節](chapter/2.md)
|
||||||
- [Untranslated Page](untranslated-page.md)
|
- [Untranslated Page](untranslated-page.md)
|
||||||
- [日本語専用のページ](translation-local-page.md)
|
|
||||||
- [内部リンクの入れ替え](inline-link-fallbacks.md)
|
- [内部リンクの入れ替え](inline-link-fallbacks.md)
|
||||||
|
- [日本語専用のページ](translation-local-page.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](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.
|
Also, here is an [inline link](blah.md) to a page missing from both translations. It should point to this language's 404 page.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user