[localization] Fixes for latest master

This commit is contained in:
Ruin0x11 2021-09-15 15:25:31 -07:00
parent d6c27abc22
commit 92ec3ddc55
12 changed files with 185 additions and 203 deletions

View File

@ -15,13 +15,13 @@
- [General](format/configuration/general.md) - [General](format/configuration/general.md)
- [Preprocessors](format/configuration/preprocessors.md) - [Preprocessors](format/configuration/preprocessors.md)
- [Renderers](format/configuration/renderers.md) - [Renderers](format/configuration/renderers.md)
- [Localization](format/configuration/localization.md)
- [Environment Variables](format/configuration/environment-variables.md) - [Environment Variables](format/configuration/environment-variables.md)
- [Theme](format/theme/README.md) - [Theme](format/theme/README.md)
- [index.hbs](format/theme/index-hbs.md) - [index.hbs](format/theme/index-hbs.md)
- [Syntax highlighting](format/theme/syntax-highlighting.md) - [Syntax highlighting](format/theme/syntax-highlighting.md)
- [Editor](format/theme/editor.md) - [Editor](format/theme/editor.md)
- [MathJax Support](format/mathjax.md) - [MathJax Support](format/mathjax.md)
- [Localization](format/localization.md)
- [mdBook-specific features](format/mdbook.md) - [mdBook-specific features](format/mdbook.md)
- [Continuous Integration](continuous-integration.md) - [Continuous Integration](continuous-integration.md)
- [For Developers](for_developers/README.md) - [For Developers](for_developers/README.md)

View File

@ -4,11 +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 - **[Localization]** 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 [Localization]: localization.md
[Environment Variable]: environment-variables.md [Environment Variable]: environment-variables.md

View File

@ -1 +1,86 @@
# Localization # Localization
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.

View File

@ -1,86 +0,0 @@
## 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.

View File

@ -0,0 +1 @@
# Localization

View File

@ -61,8 +61,8 @@ fn load_single_book_translation<P: AsRef<Path>>(
let summary = parse_summary(&summary_content) let summary = parse_summary(&summary_content)
.with_context(|| format!("Summary parsing failed for file={:?}", summary_md))?; .with_context(|| format!("Summary parsing failed for file={:?}", summary_md))?;
if cfg.create_missing { if cfg.build.create_missing {
create_missing(localized_src_dir, &summary).with_context(|| "Unable to create missing chapters")?; create_missing(&localized_src_dir, &summary).with_context(|| "Unable to create missing chapters")?;
} }
load_book_from_disk(&summary, localized_src_dir, fallback_src_dir, cfg) load_book_from_disk(&summary, localized_src_dir, fallback_src_dir, cfg)
@ -83,17 +83,7 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
if let Some(ref location) = link.location { if let Some(ref location) = link.location {
let filename = src_dir.join(location); let filename = src_dir.join(location);
if !filename.exists() { if !filename.exists() {
if let Some(parent) = filename.parent() { create_missing_link(&filename, link)?;
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
debug!("Creating missing file {}", filename.display());
let mut f = File::create(&filename).with_context(|| {
format!("Unable to create missing file: {}", filename.display())
})?;
writeln!(f, "# {}", link.name)?;
} }
} }
@ -104,6 +94,20 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
Ok(()) Ok(())
} }
fn create_missing_link(filename: &Path, link: &Link) -> Result<()> {
if let Some(parent) = filename.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
debug!("Creating missing file {}", filename.display());
let mut f = File::create(&filename)?;
writeln!(f, "# {}", link.name)?;
Ok(())
}
/// A dumb tree structure representing a book. /// A dumb tree structure representing a book.
/// ///
/// For the moment a book is just a collection of [`BookItems`] which are /// For the moment a book is just a collection of [`BookItems`] which are
@ -117,6 +121,8 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
pub struct Book { pub struct Book {
/// The sections in this book. /// The sections in this book.
pub sections: Vec<BookItem>, pub sections: Vec<BookItem>,
/// Chapter title overrides for this book.
pub chapter_titles: HashMap<PathBuf, String>,
__non_exhaustive: (), __non_exhaustive: (),
} }
@ -360,6 +366,7 @@ pub(crate) fn load_book_from_disk<P: AsRef<Path>>(
Ok(Book { Ok(Book {
sections: chapters, sections: chapters,
chapter_titles: HashMap::new(),
__non_exhaustive: (), __non_exhaustive: (),
}) })
} }
@ -410,8 +417,8 @@ fn load_chapter<P: AsRef<Path>>(
); );
} }
if !location.exists() && cfg.build.create_missing { if !location.exists() && cfg.build.create_missing {
create_missing(&location, &link) create_missing_link(&location, &link)
.with_context(|| "Unable to create missing chapters")?; .with_context(|| "Unable to create missing link reference")?;
} }
let mut f = File::open(&location) let mut f = File::open(&location)
@ -565,6 +572,7 @@ more text.
#[test] #[test]
fn load_a_single_chapter_with_utf8_bom_from_disk() { fn load_a_single_chapter_with_utf8_bom_from_disk() {
let temp_dir = TempFileBuilder::new().prefix("book").tempdir().unwrap(); let temp_dir = TempFileBuilder::new().prefix("book").tempdir().unwrap();
let cfg = Config::default();
let chapter_path = temp_dir.path().join("chapter_1.md"); let chapter_path = temp_dir.path().join("chapter_1.md");
File::create(&chapter_path) File::create(&chapter_path)
@ -581,7 +589,7 @@ more text.
Vec::new(), Vec::new(),
); );
let got = load_chapter(&link, temp_dir.path(), Vec::new()).unwrap(); let got = load_chapter(&link, temp_dir.path(), temp_dir.path(), Vec::new(), &cfg).unwrap();
assert_eq!(got, should_be); assert_eq!(got, should_be);
} }
@ -832,6 +840,7 @@ more text.
name: String::from("Chapter 1"), name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC), content: String::from(DUMMY_SRC),
path: Some(PathBuf::from("chapter_1.md")), path: Some(PathBuf::from("chapter_1.md")),
source_path: Some(PathBuf::from("chapter_1.md")),
..Default::default() ..Default::default()
})], })],
..Default::default() ..Default::default()

View File

@ -213,6 +213,25 @@ impl MDBook {
Ok(()) Ok(())
} }
fn preprocess(
&self,
preprocess_ctx: &PreprocessorContext,
renderer: &dyn Renderer,
book: Book,
) -> Result<Book> {
let mut preprocessed_book = book;
for preprocessor in &self.preprocessors {
if preprocessor_should_run(&**preprocessor, renderer, &self.config) {
debug!("Running the {} preprocessor.", preprocessor.name());
preprocessed_book = preprocessor.run(&preprocess_ctx, preprocessed_book)?;
}
}
preprocessed_book
.chapter_titles
.extend(preprocess_ctx.chapter_titles.borrow_mut().drain());
Ok(preprocessed_book)
}
/// Run the entire build process for a particular [`Renderer`]. /// 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) -> Result<()> {
let preprocessed_books = match &self.book { let preprocessed_books = match &self.book {
@ -248,19 +267,20 @@ impl MDBook {
} }
}; };
self.render(&preprocessed_books, renderer)
}
fn render(&self, preprocessed_books: &LoadedBook, renderer: &dyn Renderer) -> Result<()> {
let name = renderer.name(); let name = renderer.name();
let build_dir = self.build_dir_for(name); let build_dir = self.build_dir_for(name);
let mut render_context = RenderContext::new( let render_context = RenderContext::new(
self.root.clone(), self.root.clone(),
preprocessed_books.clone(), preprocessed_books.clone(),
self.build_opts.clone(), self.build_opts.clone(),
self.config.clone(), self.config.clone(),
build_dir, build_dir,
); );
render_context
.chapter_titles
.extend(preprocess_ctx.chapter_titles.borrow_mut().drain());
info!("Running the {} backend", renderer.name()); info!("Running the {} backend", renderer.name());
renderer renderer
@ -297,6 +317,7 @@ impl MDBook {
self.config.clone(), self.config.clone(),
"test".to_string(), "test".to_string(),
); );
let book = LinkPreprocessor::new().run(&preprocess_context, book.clone())?; let book = LinkPreprocessor::new().run(&preprocess_context, book.clone())?;
// Index Preprocessor is disabled so that chapter paths continue to point to the // Index Preprocessor is disabled so that chapter paths continue to point to the
// actual markdown files. // actual markdown files.
@ -354,6 +375,30 @@ impl MDBook {
Ok(()) Ok(())
} }
/// Run `rustdoc` tests on the book, linking against the provided libraries.
pub fn test(&self, library_paths: Vec<&str>) -> Result<()> {
let library_args: Vec<&str> = (0..library_paths.len())
.map(|_| "-L")
.zip(library_paths.into_iter())
.flat_map(|x| vec![x.0, x.1])
.collect();
let temp_dir = TempFileBuilder::new().prefix("mdbook-").tempdir()?;
match self.book {
LoadedBook::Localized(ref books) => {
for (language_ident, book) in books.0.iter() {
self.test_book(book, &temp_dir, &library_args, Some(language_ident.clone()))?;
}
}
LoadedBook::Single(ref book) => {
self.test_book(&book, &temp_dir, &library_args, None)?
}
}
Ok(())
}
/// The logic for determining where a backend should put its build /// The logic for determining where a backend should put its build
/// artefacts. /// artefacts.
/// ///

View File

@ -479,15 +479,13 @@ impl<'de> Deserialize<'de> for Config {
.unwrap_or_default(); .unwrap_or_default();
if !language.0.is_empty() { if !language.0.is_empty() {
let default_languages = language.0.iter().filter(|(_, lang)| lang.default).count(); if book.language.is_none() {
if default_languages != 1 {
return Err(D::Error::custom( return Err(D::Error::custom(
"If the [language] table is specified, then `book.language` must be declared", "If the [language] table is specified, then `book.language` must be declared",
)); ));
} }
let language_ident = book.language.clone().unwrap(); let language_ident = book.language.clone().unwrap();
if language.0.get(&language_ident).is_none() { if language.0.get(&language_ident).is_none() {
use serde::de::Error;
return Err(D::Error::custom(format!( return Err(D::Error::custom(format!(
"Expected [language.{}] to be declared in book.toml", "Expected [language.{}] to be declared in book.toml",
language_ident language_ident
@ -495,7 +493,6 @@ impl<'de> Deserialize<'de> for Config {
} }
for (ident, language) in language.0.iter() { for (ident, language) in language.0.iter() {
if language.name.is_empty() { if language.name.is_empty() {
use serde::de::Error;
return Err(D::Error::custom(format!( return Err(D::Error::custom(format!(
"`name` property for [language.{}] must be non-empty", "`name` property for [language.{}] must be non-empty",
ident ident
@ -910,7 +907,6 @@ mod tests {
title = "Some Book" title = "Some Book"
authors = ["Michael-F-Bryan <michaelfbryan@gmail.com>"] authors = ["Michael-F-Bryan <michaelfbryan@gmail.com>"]
description = "A completely useless book" description = "A completely useless book"
multilingual = true
src = "source" src = "source"
language = "ja" language = "ja"
@ -1369,14 +1365,14 @@ mod tests {
} }
#[test] #[test]
#[should_panic(expected = "Invalid configuration file")]
fn book_language_without_languages_table() { fn book_language_without_languages_table() {
let src = r#" let src = r#"
[book] [book]
language = "en" language = "en"
"#; "#;
Config::from_str(src).unwrap(); let got = Config::from_str(src).unwrap();
assert_eq!(got.default_language(), None);
} }
#[test] #[test]

View File

@ -327,9 +327,9 @@ impl<'a> Link<'a> {
}) })
} }
fn render_with_path<P: AsRef<Path>>( fn render_with_path<P1: AsRef<Path>, P2: AsRef<Path>>(
&self, &self,
base: P, base: P1,
fallback: Option<P2>, fallback: Option<P2>,
chapter_title: &mut String, chapter_title: &mut String,
) -> Result<String> { ) -> Result<String> {

View File

@ -80,6 +80,7 @@ impl HtmlHandlebars {
handlebars: &mut Handlebars<'a>, handlebars: &mut Handlebars<'a>,
theme: &Theme, theme: &Theme,
) -> Result<()> { ) -> Result<()> {
let book_config = &ctx.config.book;
let build_dir = ctx.root.join(build_dir); let build_dir = ctx.root.join(build_dir);
let mut data = make_data( let mut data = make_data(
&ctx.root, &ctx.root,
@ -104,8 +105,10 @@ impl HtmlHandlebars {
destination: destination.to_path_buf(), destination: destination.to_path_buf(),
data: data.clone(), data: data.clone(),
is_index, is_index,
book_config: book_config.clone(),
html_config: html_config.clone(), html_config: html_config.clone(),
edition: ctx.config.rust.edition, edition: ctx.config.rust.edition,
chapter_titles: &book.chapter_titles,
}; };
self.render_item( self.render_item(
item, item,
@ -138,19 +141,21 @@ impl HtmlHandlebars {
} }
// Render the handlebars template with the data // Render the handlebars template with the data
debug!("Render template"); if html_config.print.enable {
let rendered = handlebars.render("index", &data)?; debug!("Render template");
let rendered = handlebars.render("index", &data)?;
let rendered = let rendered =
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition); self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
utils::fs::write_file(&destination, "print.html", rendered.as_bytes())?; utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
debug!("Creating print.html ✓"); debug!("Creating print.html ✓");
}
debug!("Copy static files"); debug!("Copy static files");
self.copy_static_files(&destination, &theme, &html_config) self.copy_static_files(destination, &theme, &html_config)
.with_context(|| "Unable to copy across static files")?; .with_context(|| "Unable to copy across static files")?;
self.copy_additional_css_and_js(&html_config, &ctx.root, &destination) self.copy_additional_css_and_js(&html_config, &ctx.root, destination)
.with_context(|| "Unable to copy across additional CSS and JS")?; .with_context(|| "Unable to copy across additional CSS and JS")?;
// Render search index // Render search index
@ -158,11 +163,11 @@ impl HtmlHandlebars {
{ {
let search = html_config.search.clone().unwrap_or_default(); let search = html_config.search.clone().unwrap_or_default();
if search.enable { if search.enable {
super::search::create_files(&search, &destination, &book)?; super::search::create_files(&search, destination, book)?;
} }
} }
self.emit_redirects(&ctx.destination, handlebars, &html_config.redirect) self.emit_redirects(&ctx.destination, &handlebars, &html_config.redirect)
.context("Unable to emit redirects")?; .context("Unable to emit redirects")?;
// `src_dir` points to the root source directory. If this book // `src_dir` points to the root source directory. If this book
@ -674,7 +679,6 @@ impl Renderer for HtmlHandlebars {
} }
fn render(&self, ctx: &RenderContext) -> Result<()> { fn render(&self, ctx: &RenderContext) -> Result<()> {
let book_config = &ctx.config.book;
let html_config = ctx.config.html_config().unwrap_or_default(); let html_config = ctx.config.html_config().unwrap_or_default();
let src_dir = ctx.source_dir(); let src_dir = ctx.source_dir();
let destination = &ctx.destination; let destination = &ctx.destination;
@ -720,75 +724,7 @@ impl Renderer for HtmlHandlebars {
debug!("Register handlebars helpers"); debug!("Register handlebars helpers");
self.register_hbs_helpers(&mut handlebars, &html_config); self.register_hbs_helpers(&mut handlebars, &html_config);
let mut data = make_data(&ctx.root, book, &ctx.config, &html_config, &theme)?; self.render_books(ctx, &src_dir, &html_config, &mut handlebars, &theme)
// Print version
let mut print_content = String::new();
fs::create_dir_all(&destination)
.with_context(|| "Unexpected error when constructing destination path")?;
let mut is_index = true;
for item in book.iter() {
let ctx = RenderItemContext {
handlebars: &handlebars,
destination: destination.to_path_buf(),
data: data.clone(),
is_index,
book_config: book_config.clone(),
html_config: html_config.clone(),
edition: ctx.config.rust.edition,
chapter_titles: &ctx.chapter_titles,
};
self.render_item(item, ctx, &mut print_content)?;
is_index = false;
}
// Render 404 page
if html_config.input_404 != Some("".to_string()) {
self.render_404(ctx, &html_config, &src_dir, &mut handlebars, &mut data)?;
}
// Print version
self.configure_print_version(&mut data, &print_content);
if let Some(ref title) = ctx.config.book.title {
data.insert("title".to_owned(), json!(title));
}
// Render the handlebars template with the data
if html_config.print.enable {
debug!("Render template");
let rendered = handlebars.render("index", &data)?;
let rendered =
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
debug!("Creating print.html ✓");
}
debug!("Copy static files");
self.copy_static_files(destination, &theme, &html_config)
.with_context(|| "Unable to copy across static files")?;
self.copy_additional_css_and_js(&html_config, &ctx.root, destination)
.with_context(|| "Unable to copy across additional CSS and JS")?;
// Render search index
#[cfg(feature = "search")]
{
let search = html_config.search.unwrap_or_default();
if search.enable {
super::search::create_files(&search, destination, book)?;
}
}
self.emit_redirects(&ctx.destination, &handlebars, &html_config.redirect)
.context("Unable to emit redirects")?;
// Copy all remaining files, avoid a recursive copy from/to the book build dir
utils::fs::copy_files_except_ext(&src_dir, destination, true, Some(&build_dir), &["md"])?;
Ok(())
} }
} }

View File

@ -18,7 +18,6 @@ mod html_handlebars;
mod markdown_renderer; mod markdown_renderer;
use shlex::Shlex; use shlex::Shlex;
use std::collections::HashMap;
use std::fs; use std::fs;
use std::io::{self, ErrorKind, Read}; use std::io::{self, ErrorKind, Read};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -70,8 +69,6 @@ pub struct RenderContext {
/// guaranteed to be empty or even exist. /// guaranteed to be empty or even exist.
pub destination: PathBuf, pub destination: PathBuf,
#[serde(skip)] #[serde(skip)]
pub(crate) chapter_titles: HashMap<PathBuf, String>,
#[serde(skip)]
__non_exhaustive: (), __non_exhaustive: (),
} }
@ -95,7 +92,6 @@ impl RenderContext {
version: crate::MDBOOK_VERSION.to_string(), version: crate::MDBOOK_VERSION.to_string(),
root: root.into(), root: root.into(),
destination: destination.into(), destination: destination.into(),
chapter_titles: HashMap::new(),
__non_exhaustive: (), __non_exhaustive: (),
} }
} }

View File

@ -107,7 +107,7 @@ fn run_mdbook_init_with_custom_book_and_src_locations() {
let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap(); let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap();
assert_eq!( assert_eq!(
contents, contents,
"[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"in\"\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nuse-default-preprocessors = true\n" "[book]\nauthors = []\nlanguage = \"en\"\nsrc = \"in\"\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nuse-default-preprocessors = true\n[language.en]\nname = \"English\"\n"
); );
} }