Skip to content

Commit

Permalink
Merge pull request #1641 from cN3rd/rtl
Browse files Browse the repository at this point in the history
Continued work on "Support right-to-left languages"
  • Loading branch information
ehuss authored Sep 2, 2023
2 parents 25aaff0 + 802e7bf commit 09f222b
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 63 deletions.
4 changes: 4 additions & 0 deletions guide/src/format/configuration/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ This is general information about your book.
`src` directly under the root folder. But this is configurable with the `src`
key in the configuration file.
- **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 text direction is derived from the book's `language` attribute.

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

### Rust options
Expand Down
108 changes: 108 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,9 @@ pub struct BookConfig {
pub multilingual: bool,
/// The main language of the book.
pub language: Option<String>,
/// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).
/// When not specified, the text direction is derived from [`BookConfig::language`].
pub text_direction: Option<TextDirection>,
}

impl Default for BookConfig {
Expand All @@ -422,6 +425,43 @@ impl Default for BookConfig {
src: PathBuf::from("src"),
multilingual: false,
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.as_deref().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"
| "pus" | "ps" | "yi" | "yid" => TextDirection::RightToLeft,
_ => TextDirection::LeftToRight,
}
}
}
Expand Down Expand Up @@ -788,6 +828,7 @@ mod tests {
multilingual: true,
src: PathBuf::from("source"),
language: Some(String::from("ja")),
text_direction: None,
};
let build_should_be = BuildConfig {
build_dir: PathBuf::from("outputs"),
Expand Down Expand Up @@ -1140,6 +1181,73 @@ mod tests {
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]
#[should_panic(expected = "Invalid configuration file")]
fn invalid_language_type_error() {
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,10 @@ fn make_data(
"language".to_owned(),
json!(config.book.language.clone().unwrap_or_default()),
);
data.insert(
"text_direction".to_owned(),
json!(config.book.realized_text_direction()),
);
data.insert(
"book_title".to_owned(),
json!(config.book.title.clone().unwrap_or_default()),
Expand Down Expand Up @@ -1088,6 +1092,8 @@ struct RenderItemContext<'a> {

#[cfg(test)]
mod tests {
use crate::config::TextDirection;

use super::*;
use pretty_assertions::assert_eq;

Expand Down Expand Up @@ -1299,4 +1305,10 @@ mod tests {
assert_eq!(&*got, *should_be);
}
}

#[test]
fn test_json_direction() {
assert_eq!(json!(TextDirection::RightToLeft), json!("rtl"));
assert_eq!(json!(TextDirection::LeftToRight), json!("ltr"));
}
}
47 changes: 31 additions & 16 deletions src/theme/book.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,16 +441,16 @@ function playground_text(playground, hidden = true) {
})();

(function sidebar() {
var html = document.querySelector("html");
var body = document.querySelector("body");
var sidebar = document.getElementById("sidebar");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
var firstContact = null;

function showSidebar() {
html.classList.remove('sidebar-hidden')
html.classList.add('sidebar-visible');
body.classList.remove('sidebar-hidden')
body.classList.add('sidebar-visible');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', 0);
});
Expand All @@ -471,8 +471,8 @@ function playground_text(playground, hidden = true) {
});

function hideSidebar() {
html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden');
body.classList.remove('sidebar-visible')
body.classList.add('sidebar-hidden');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', -1);
});
Expand All @@ -483,14 +483,14 @@ function playground_text(playground, hidden = true) {

// Toggle sidebar
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
if (html.classList.contains("sidebar-hidden")) {
if (body.classList.contains("sidebar-hidden")) {
var current_width = parseInt(
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
if (current_width < 150) {
document.documentElement.style.setProperty('--sidebar-width', '150px');
}
showSidebar();
} else if (html.classList.contains("sidebar-visible")) {
} else if (body.classList.contains("sidebar-visible")) {
hideSidebar();
} else {
if (getComputedStyle(sidebar)['transform'] === 'none') {
Expand All @@ -506,14 +506,14 @@ function playground_text(playground, hidden = true) {
function initResize(e) {
window.addEventListener('mousemove', resize, false);
window.addEventListener('mouseup', stopResize, false);
html.classList.add('sidebar-resizing');
body.classList.add('sidebar-resizing');
}
function resize(e) {
var pos = (e.clientX - sidebar.offsetLeft);
if (pos < 20) {
hideSidebar();
} else {
if (html.classList.contains("sidebar-hidden")) {
if (body.classList.contains("sidebar-hidden")) {
showSidebar();
}
pos = Math.min(pos, window.innerWidth - 100);
Expand All @@ -522,7 +522,7 @@ function playground_text(playground, hidden = true) {
}
//on mouseup remove windows functions mousemove & mouseup
function stopResize(e) {
html.classList.remove('sidebar-resizing');
body.classList.remove('sidebar-resizing');
window.removeEventListener('mousemove', resize, false);
window.removeEventListener('mouseup', stopResize, false);
}
Expand Down Expand Up @@ -557,20 +557,35 @@ function playground_text(playground, hidden = true) {
document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
if (window.search && window.search.hasFocus()) { return; }
var html = document.querySelector('html');

function next() {
var nextButton = document.querySelector('.nav-chapters.next');
if (nextButton) {
window.location.href = nextButton.href;
}
}
function prev() {
var previousButton = document.querySelector('.nav-chapters.previous');
if (previousButton) {
window.location.href = previousButton.href;
}
}
switch (e.key) {
case 'ArrowRight':
e.preventDefault();
var nextButton = document.querySelector('.nav-chapters.next');
if (nextButton) {
window.location.href = nextButton.href;
if (html.dir == 'rtl') {
prev();
} else {
next();
}
break;
case 'ArrowLeft':
e.preventDefault();
var previousButton = document.querySelector('.nav-chapters.previous');
if (previousButton) {
window.location.href = previousButton.href;
if (html.dir == 'rtl') {
next();
} else {
prev();
}
break;
}
Expand Down
Loading

0 comments on commit 09f222b

Please sign in to comment.