Skip to content

Commit d5276e7

Browse files
committed
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.
1 parent 79134a4 commit d5276e7

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

guide/src/format/configuration/general.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ This is general information about your book.
4646
`src` directly under the root folder. But this is configurable with the `src`
4747
key in the configuration file.
4848
- **language:** The main language of the book, which is used as a language attribute `<html lang="en">` for example.
49+
This is also used to derive the direction of text (RTL, LTR) within the book.
50+
- **text_direction**: The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). Possible values: `ltr`, `rtl`.
51+
When not specified, the correct text direction is derived from the book's `language` attribute.
4952

5053
**book.toml**
5154
```toml
@@ -55,6 +58,7 @@ authors = ["John Doe", "Jane Doe"]
5558
description = "The example book covers examples."
5659
src = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
5760
language = "en"
61+
text-direction = "ltr"
5862
```
5963

6064
### Rust options

src/config.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ pub struct BookConfig {
417417
pub multilingual: bool,
418418
/// The main language of the book.
419419
pub language: Option<String>,
420+
/// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).
421+
/// When not specified, the correct text direction is derived from [BookConfig::language].
422+
pub text_direction: Option<TextDirection>,
420423
}
421424

422425
impl Default for BookConfig {
@@ -428,6 +431,44 @@ impl Default for BookConfig {
428431
src: PathBuf::from("src"),
429432
multilingual: false,
430433
language: Some(String::from("en")),
434+
text_direction: None,
435+
}
436+
}
437+
}
438+
439+
impl BookConfig {
440+
/// Gets the realized text direction, either from [BookConfig::text_direction]
441+
/// or derived from [BookConfig::language], to be used by templating engines.
442+
pub fn realized_text_direction(&self) -> TextDirection {
443+
if let Some(direction) = self.text_direction {
444+
direction
445+
} else {
446+
TextDirection::from_lang_code(&self.language.clone().unwrap_or_default())
447+
}
448+
}
449+
}
450+
451+
/// Text direction to use for HTML output
452+
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
453+
pub enum TextDirection {
454+
/// Left to right.
455+
#[serde(rename = "ltr")]
456+
LeftToRight,
457+
/// Right to left
458+
#[serde(rename = "rtl")]
459+
RightToLeft,
460+
}
461+
462+
impl TextDirection {
463+
/// Gets the text direction from language code
464+
pub fn from_lang_code(code: &str) -> Self {
465+
match code {
466+
// list sourced from here: https://github.com/abarrak/rtl/blob/master/lib/rtl/core.rb#L16
467+
"ar" | "ara" | "arc" | "ae" | "ave" | "egy" | "he" | "heb" | "nqo" | "pal" | "phn"
468+
| "sam" | "syc" | "syr" | "fa" | "per" | "fas" | "ku" | "kur" | "ur" | "urd" => {
469+
TextDirection::RightToLeft
470+
}
471+
_ => TextDirection::LeftToRight,
431472
}
432473
}
433474
}
@@ -764,6 +805,7 @@ mod tests {
764805
multilingual: true,
765806
src: PathBuf::from("source"),
766807
language: Some(String::from("ja")),
808+
text_direction: None,
767809
};
768810
let build_should_be = BuildConfig {
769811
build_dir: PathBuf::from("outputs"),
@@ -1098,6 +1140,184 @@ mod tests {
10981140
assert_eq!(&get_404_output_file(&html_config.input_404), "missing.html");
10991141
}
11001142

1143+
#[test]
1144+
fn text_direction_ltr() {
1145+
let src = r#"
1146+
[book]
1147+
text-direction = "ltr"
1148+
"#;
1149+
1150+
let got = Config::from_str(src).unwrap();
1151+
assert_eq!(got.book.text_direction, Some(TextDirection::LeftToRight));
1152+
}
1153+
1154+
#[test]
1155+
fn text_direction_rtl() {
1156+
let src = r#"
1157+
[book]
1158+
text-direction = "rtl"
1159+
"#;
1160+
1161+
let got = Config::from_str(src).unwrap();
1162+
assert_eq!(got.book.text_direction, Some(TextDirection::RightToLeft));
1163+
}
1164+
1165+
#[test]
1166+
fn text_direction_none() {
1167+
let src = r#"
1168+
[book]
1169+
"#;
1170+
1171+
let got = Config::from_str(src).unwrap();
1172+
assert_eq!(got.book.text_direction, None);
1173+
}
1174+
1175+
#[test]
1176+
fn test_text_direction() {
1177+
let mut cfg = BookConfig::default();
1178+
1179+
// test deriving the text direction from language codes
1180+
cfg.language = Some("ar".into());
1181+
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
1182+
1183+
cfg.language = Some("he".into());
1184+
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
1185+
1186+
cfg.language = Some("en".into());
1187+
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
1188+
1189+
cfg.language = Some("ja".into());
1190+
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
1191+
1192+
// test forced direction
1193+
cfg.language = Some("ar".into());
1194+
cfg.text_direction = Some(TextDirection::LeftToRight);
1195+
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
1196+
1197+
cfg.language = Some("ar".into());
1198+
cfg.text_direction = Some(TextDirection::RightToLeft);
1199+
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
1200+
1201+
cfg.language = Some("en".into());
1202+
cfg.text_direction = Some(TextDirection::LeftToRight);
1203+
assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);
1204+
1205+
cfg.language = Some("en".into());
1206+
cfg.text_direction = Some(TextDirection::RightToLeft);
1207+
assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);
1208+
}
1209+
1210+
#[test]
1211+
fn text_drection_from_lang_code() {
1212+
// test all right-to-left languages
1213+
assert_eq!(
1214+
TextDirection::from_lang_code("ar"),
1215+
TextDirection::RightToLeft
1216+
);
1217+
assert_eq!(
1218+
TextDirection::from_lang_code("ara"),
1219+
TextDirection::RightToLeft
1220+
);
1221+
assert_eq!(
1222+
TextDirection::from_lang_code("arc"),
1223+
TextDirection::RightToLeft
1224+
);
1225+
assert_eq!(
1226+
TextDirection::from_lang_code("ae"),
1227+
TextDirection::RightToLeft
1228+
);
1229+
assert_eq!(
1230+
TextDirection::from_lang_code("ave"),
1231+
TextDirection::RightToLeft
1232+
);
1233+
assert_eq!(
1234+
TextDirection::from_lang_code("egy"),
1235+
TextDirection::RightToLeft
1236+
);
1237+
assert_eq!(
1238+
TextDirection::from_lang_code("he"),
1239+
TextDirection::RightToLeft
1240+
);
1241+
assert_eq!(
1242+
TextDirection::from_lang_code("heb"),
1243+
TextDirection::RightToLeft
1244+
);
1245+
assert_eq!(
1246+
TextDirection::from_lang_code("nqo"),
1247+
TextDirection::RightToLeft
1248+
);
1249+
assert_eq!(
1250+
TextDirection::from_lang_code("pal"),
1251+
TextDirection::RightToLeft
1252+
);
1253+
assert_eq!(
1254+
TextDirection::from_lang_code("phn"),
1255+
TextDirection::RightToLeft
1256+
);
1257+
assert_eq!(
1258+
TextDirection::from_lang_code("sam"),
1259+
TextDirection::RightToLeft
1260+
);
1261+
assert_eq!(
1262+
TextDirection::from_lang_code("syc"),
1263+
TextDirection::RightToLeft
1264+
);
1265+
assert_eq!(
1266+
TextDirection::from_lang_code("syr"),
1267+
TextDirection::RightToLeft
1268+
);
1269+
assert_eq!(
1270+
TextDirection::from_lang_code("fa"),
1271+
TextDirection::RightToLeft
1272+
);
1273+
assert_eq!(
1274+
TextDirection::from_lang_code("per"),
1275+
TextDirection::RightToLeft
1276+
);
1277+
assert_eq!(
1278+
TextDirection::from_lang_code("fas"),
1279+
TextDirection::RightToLeft
1280+
);
1281+
assert_eq!(
1282+
TextDirection::from_lang_code("ku"),
1283+
TextDirection::RightToLeft
1284+
);
1285+
assert_eq!(
1286+
TextDirection::from_lang_code("kur"),
1287+
TextDirection::RightToLeft
1288+
);
1289+
assert_eq!(
1290+
TextDirection::from_lang_code("ur"),
1291+
TextDirection::RightToLeft
1292+
);
1293+
assert_eq!(
1294+
TextDirection::from_lang_code("urd"),
1295+
TextDirection::RightToLeft
1296+
);
1297+
1298+
// test some left-to-right languages
1299+
assert_eq!(
1300+
TextDirection::from_lang_code("de"),
1301+
TextDirection::LeftToRight
1302+
);
1303+
assert_eq!(
1304+
TextDirection::from_lang_code("en"),
1305+
TextDirection::LeftToRight
1306+
);
1307+
assert_eq!(
1308+
TextDirection::from_lang_code("es"),
1309+
TextDirection::LeftToRight
1310+
);
1311+
assert_eq!(
1312+
TextDirection::from_lang_code("ja"),
1313+
TextDirection::LeftToRight
1314+
);
1315+
assert_eq!(
1316+
TextDirection::from_lang_code("sv"),
1317+
TextDirection::LeftToRight
1318+
);
1319+
}
1320+
11011321
#[test]
11021322
#[should_panic(expected = "Invalid configuration file")]
11031323
fn invalid_language_type_error() {

0 commit comments

Comments
 (0)