Add `text_direction` property in general book metadata

Text direction can selected in the config via the `text_direction` attribute in `book.toml`,
or be derived from the book's language.
This commit is contained in:
cN3rd 2021-09-03 18:31:39 +03:00 committed by Eric Huss
parent 3a99899114
commit 819a108f07
2 changed files with 224 additions and 0 deletions

View File

@ -46,6 +46,9 @@ This is general information about your book.
`src` directly under the root folder. But this is configurable with the `src` `src` directly under the root folder. But this is configurable with the `src`
key in the configuration file. key in the configuration file.
- **language:** The main language of the book, which is used as a language attribute `<html lang="en">` for example. - **language:** The main language of the book, which is used as a language attribute `<html lang="en">` for example.
This is also used to derive the direction of text (RTL, LTR) within the book.
- **text_direction**: The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). Possible values: `ltr`, `rtl`.
When not specified, the correct text direction is derived from the book's `language` attribute.
**book.toml** **book.toml**
```toml ```toml
@ -55,6 +58,7 @@ authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples." description = "The example book covers examples."
src = "my-src" # the source files will be found in `root/my-src` instead of `root/src` src = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
language = "en" language = "en"
text-direction = "ltr"
``` ```
### Rust options ### Rust options

View File

@ -411,6 +411,9 @@ pub struct BookConfig {
pub multilingual: bool, pub multilingual: bool,
/// The main language of the book. /// The main language of the book.
pub language: Option<String>, pub language: Option<String>,
/// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).
/// When not specified, the correct text direction is derived from [BookConfig::language].
pub text_direction: Option<TextDirection>,
} }
impl Default for BookConfig { impl Default for BookConfig {
@ -422,6 +425,44 @@ impl Default for BookConfig {
src: PathBuf::from("src"), src: PathBuf::from("src"),
multilingual: false, multilingual: false,
language: Some(String::from("en")), language: Some(String::from("en")),
text_direction: None,
}
}
}
impl BookConfig {
/// Gets the realized text direction, either from [BookConfig::text_direction]
/// or derived from [BookConfig::language], to be used by templating engines.
pub fn realized_text_direction(&self) -> TextDirection {
if let Some(direction) = self.text_direction {
direction
} else {
TextDirection::from_lang_code(&self.language.clone().unwrap_or_default())
}
}
}
/// Text direction to use for HTML output
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum TextDirection {
/// Left to right.
#[serde(rename = "ltr")]
LeftToRight,
/// Right to left
#[serde(rename = "rtl")]
RightToLeft,
}
impl TextDirection {
/// Gets the text direction from language code
pub fn from_lang_code(code: &str) -> Self {
match code {
// list sourced from here: https://github.com/abarrak/rtl/blob/master/lib/rtl/core.rb#L16
"ar" | "ara" | "arc" | "ae" | "ave" | "egy" | "he" | "heb" | "nqo" | "pal" | "phn"
| "sam" | "syc" | "syr" | "fa" | "per" | "fas" | "ku" | "kur" | "ur" | "urd" => {
TextDirection::RightToLeft
}
_ => TextDirection::LeftToRight,
} }
} }
} }
@ -788,6 +829,7 @@ mod tests {
multilingual: true, multilingual: true,
src: PathBuf::from("source"), src: PathBuf::from("source"),
language: Some(String::from("ja")), language: Some(String::from("ja")),
text_direction: None,
}; };
let build_should_be = BuildConfig { let build_should_be = BuildConfig {
build_dir: PathBuf::from("outputs"), build_dir: PathBuf::from("outputs"),
@ -1140,6 +1182,184 @@ mod tests {
assert_eq!(&get_404_output_file(&html_config.input_404), "missing.html"); assert_eq!(&get_404_output_file(&html_config.input_404), "missing.html");
} }
#[test]
fn text_direction_ltr() {
let src = r#"
[book]
text-direction = "ltr"
"#;
let got = Config::from_str(src).unwrap();
assert_eq!(got.book.text_direction, Some(TextDirection::LeftToRight));
}
#[test]
fn text_direction_rtl() {
let src = r#"
[book]
text-direction = "rtl"
"#;
let got = Config::from_str(src).unwrap();
assert_eq!(got.book.text_direction, Some(TextDirection::RightToLeft));
}
#[test]
fn text_direction_none() {
let src = r#"
[book]
"#;
let got = Config::from_str(src).unwrap();
assert_eq!(got.book.text_direction, None);
}
#[test]
fn test_text_direction() {
let mut cfg = BookConfig::default();
// test deriving the text direction from language codes
cfg.language = Some("ar".into());
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
cfg.language = Some("he".into());
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
cfg.language = Some("en".into());
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
cfg.language = Some("ja".into());
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
// test forced direction
cfg.language = Some("ar".into());
cfg.text_direction = Some(TextDirection::LeftToRight);
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
cfg.language = Some("ar".into());
cfg.text_direction = Some(TextDirection::RightToLeft);
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
cfg.language = Some("en".into());
cfg.text_direction = Some(TextDirection::LeftToRight);
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
cfg.language = Some("en".into());
cfg.text_direction = Some(TextDirection::RightToLeft);
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
}
#[test]
fn text_drection_from_lang_code() {
// test all right-to-left languages
assert_eq!(
TextDirection::from_lang_code("ar"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("ara"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("arc"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("ae"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("ave"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("egy"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("he"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("heb"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("nqo"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("pal"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("phn"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("sam"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("syc"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("syr"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("fa"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("per"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("fas"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("ku"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("kur"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("ur"),
TextDirection::RightToLeft
);
assert_eq!(
TextDirection::from_lang_code("urd"),
TextDirection::RightToLeft
);
// test some left-to-right languages
assert_eq!(
TextDirection::from_lang_code("de"),
TextDirection::LeftToRight
);
assert_eq!(
TextDirection::from_lang_code("en"),
TextDirection::LeftToRight
);
assert_eq!(
TextDirection::from_lang_code("es"),
TextDirection::LeftToRight
);
assert_eq!(
TextDirection::from_lang_code("ja"),
TextDirection::LeftToRight
);
assert_eq!(
TextDirection::from_lang_code("sv"),
TextDirection::LeftToRight
);
}
#[test] #[test]
#[should_panic(expected = "Invalid configuration file")] #[should_panic(expected = "Invalid configuration file")]
fn invalid_language_type_error() { fn invalid_language_type_error() {