From 40b94908422ff65f8b9c5b22b9dcff1150ce2f1c Mon Sep 17 00:00:00 2001 From: Erin Power Date: Sat, 25 Apr 2020 09:09:12 +0200 Subject: [PATCH 1/4] Refactor root_to_path to return a PathBuf instead of a string --- src/utils/fs.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/fs.rs b/src/utils/fs.rs index d4c0cb5f16..ce1e700eb4 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -29,14 +29,14 @@ pub fn write_file>(build_dir: &Path, filename: P, content: &[u8]) /// # use std::path::Path; /// # use mdbook::utils::fs::path_to_root; /// let path = Path::new("some/relative/path"); -/// assert_eq!(path_to_root(path), "../../"); +/// assert_eq!(path_to_root(path), Path::new("../../")); /// ``` /// /// **note:** it's not very fool-proof, if you find a situation where /// it doesn't return the correct path. /// Consider [submitting a new issue](https://github.com/rust-lang/mdBook/issues) /// or a [pull-request](https://github.com/rust-lang/mdBook/pulls) to improve it. -pub fn path_to_root>(path: P) -> String { +pub fn path_to_root>(path: P) -> PathBuf { debug!("path_to_root"); // Remove filename and add "../" for every directory @@ -44,9 +44,9 @@ pub fn path_to_root>(path: P) -> String { .parent() .expect("") .components() - .fold(String::new(), |mut s, c| { + .fold(PathBuf::new(), |mut s, c| { match c { - Component::Normal(_) => s.push_str("../"), + Component::Normal(_) => s.push("../"), _ => { debug!("Other path component... {:?}", c); } From d6ffed930f18c54ba0b46c58939e5c4f23495a69 Mon Sep 17 00:00:00 2001 From: Erin Power Date: Sat, 25 Apr 2020 09:09:52 +0200 Subject: [PATCH 2/4] Implement Fluent Localisation --- Cargo.lock | 434 ++++++++++++++----- Cargo.toml | 2 + src/config.rs | 22 +- src/renderer/html_handlebars/hbs_renderer.rs | 327 ++++++++++---- src/renderer/html_handlebars/helpers/toc.rs | 2 +- src/theme/index.hbs | 18 + 6 files changed, 614 insertions(+), 191 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfcc2531a0..9f35eb3d31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.28" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "atty" @@ -70,24 +70,24 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" +checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" [[package]] name = "bit-set" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" +checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" [[package]] name = "bitflags" @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term", "atty", @@ -177,12 +177,12 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" +checksum = "cf6b25ee9ac1995c54d7adb2eff8cfffb7260bc774fb63c601ec65467f43cd9d" dependencies = [ - "quote 1.0.3", - "syn 1.0.17", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] @@ -200,6 +200,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dtoa" version = "0.4.5" @@ -242,9 +248,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "filetime" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" +checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" dependencies = [ "cfg-if", "libc", @@ -252,11 +258,68 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "fluent" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3b6132d1377d8776409a337c6851d342aee4e85277c96ecd2755c4e0efde1d" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a094d494ab2ed06077e9a95f4e47f446c376de95f6c93045dd88c499bfcd70" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rental", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac0f7e83d14cccbf26e165d8881dcac5891af0d85a88543c09dd72ebd31d91ba" + +[[package]] +name = "fluent-templates" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d09fff6a0345280dd5062fc68a1104d98568392bc433d146d02c5ff2e41bd1c" +dependencies = [ + "fluent", + "fluent-bundle", + "fluent-langneg", + "fluent-syntax", + "handlebars", + "lazy_static", + "serde_json", + "snafu", + "unic-langid", +] + [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fsevent" @@ -311,9 +374,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", @@ -326,9 +389,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", "futures-sink", @@ -336,15 +399,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-executor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", @@ -353,39 +416,42 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] name = "futures-sink" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] [[package]] name = "futures-util" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures-channel", "futures-core", @@ -394,12 +460,22 @@ dependencies = [ "futures-sink", "futures-task", "memchr", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -446,9 +522,9 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "h2" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ "bytes", "fnv", @@ -483,7 +559,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" dependencies = [ - "base64 0.12.0", + "base64 0.12.1", "bitflags", "bytes", "headers-core", @@ -513,9 +589,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -543,9 +619,9 @@ dependencies = [ "log", "mac", "markup5ever 0.10.0", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] @@ -657,6 +733,26 @@ dependencies = [ "bytes", ] +[[package]] +name = "intl-memoizer" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0ed58ba6089d49f8a9a7d5e16fc9b9e2019cdf40ef270f3d465fa244d9630b" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c271cdb1f12a9feb3a017619c3ee681f971f270f6757341d6abe1f9f7a98bc3" +dependencies = [ + "tinystr", + "unic-langid", +] + [[package]] name = "iovec" version = "0.1.4" @@ -696,9 +792,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "log" @@ -783,6 +879,7 @@ dependencies = [ "clap", "elasticlunr-rs", "env_logger", + "fluent-templates", "futures-util", "gitignore", "handlebars", @@ -802,6 +899,7 @@ dependencies = [ "tempfile", "tokio", "toml", + "unic-langid", "walkdir", "warp", ] @@ -830,9 +928,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -873,9 +971,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", @@ -925,6 +1023,12 @@ dependencies = [ "autocfg 1.0.0", ] +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -982,9 +1086,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] @@ -1071,46 +1175,46 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" dependencies = [ - "siphasher 0.3.2", + "siphasher 0.3.3", ] [[package]] name = "pin-project" -version = "0.4.9" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.9" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "precomputed-hash" @@ -1153,18 +1257,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" dependencies = [ "unicode-xid 0.2.0", ] [[package]] name = "pulldown-cmark" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c2d7fd131800e0d63df52aff46201acaab70b431a4a1ec6f0343fe8e64f35a4" +checksum = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00" dependencies = [ "bitflags", "getopts", @@ -1189,11 +1293,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ - "proc-macro2 1.0.10", + "proc-macro2 1.0.15", ] [[package]] @@ -1395,11 +1499,32 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rental" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" +dependencies = [ + "rental-impl", + "stable_deref_trait", +] + +[[package]] +name = "rental-impl" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" +dependencies = [ + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", +] + [[package]] name = "ryu" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "same-file" @@ -1428,26 +1553,26 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.106" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] name = "serde_json" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", @@ -1492,9 +1617,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "siphasher" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e88f89a550c01e4cd809f3df4f52dc9e939f3273a2017eabd5c6d12fd98bb23" +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" [[package]] name = "slab" @@ -1504,9 +1629,36 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.3.0" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "snafu" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5aed652511f5c9123cf2afbe9c244c29db6effa2abb05c866e965c82405ce" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" +checksum = "ebf8f7d5720104a9df0f7076a8682024e958bba0fe9848767bb44f251f3648e9" +dependencies = [ + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", +] + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" [[package]] name = "string_cache" @@ -1544,8 +1696,8 @@ checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" dependencies = [ "phf_generator 0.7.24", "phf_shared 0.7.24", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2 1.0.15", + "quote 1.0.6", "string_cache_shared", ] @@ -1557,8 +1709,8 @@ checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" dependencies = [ "phf_generator 0.8.0", "phf_shared 0.8.0", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2 1.0.15", + "quote 1.0.6", ] [[package]] @@ -1586,9 +1738,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" dependencies = [ "heck", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] @@ -1604,12 +1756,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.17" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2 1.0.15", + "quote 1.0.6", "unicode-xid 0.2.0", ] @@ -1675,11 +1827,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "tinystr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bac79c4b51eda1b090b1edebfb667821bbb51f713855164dc7cec2cb8ac2ba3" + [[package]] name = "tokio" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ef16d072d2b6dc8b4a56c70f5c5ced1a37752116f8e7c1e80c659aa7cb6713" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" dependencies = [ "bytes", "fnv", @@ -1699,9 +1857,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "proc-macro2 1.0.15", + "quote 1.0.6", + "syn 1.0.23", ] [[package]] @@ -1771,6 +1929,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "type-map" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2741b1474c327d95c1f1e3b0a2c3977c8e128409c572a33af2914e7d636717" +dependencies = [ + "fxhash", +] + [[package]] name = "typenum" version = "1.12.0" @@ -1783,6 +1950,49 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +[[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f" +dependencies = [ + "proc-macro-hack", + "quote 1.0.6", + "syn 1.0.23", + "unic-langid-impl", +] + [[package]] name = "unicase" version = "2.6.0" @@ -1859,9 +2069,9 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" @@ -1892,9 +2102,9 @@ dependencies = [ [[package]] name = "warp" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cd1e2b3eb3539284d88b76a9afcf5e20f2ef2fab74db5b21a1c30d7d945e82" +checksum = "0e95175b7a927258ecbb816bdada3cc469cb68593e7940b96a60f4af366a9970" dependencies = [ "bytes", "futures", diff --git a/Cargo.toml b/Cargo.toml index 7c57545dff..80a3047b12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,8 @@ warp = { version = "0.2.2", default-features = false, features = ["websocket"], # Search feature elasticlunr-rs = { version = "2.3", optional = true, default-features = false } ammonia = { version = "3", optional = true } +fluent-templates = { version = "0.4.6", features = ["handlebars"] } +unic-langid = "0.9.0" [dev-dependencies] select = "0.4" diff --git a/src/config.rs b/src/config.rs index 4fb20be008..96c7467eed 100644 --- a/src/config.rs +++ b/src/config.rs @@ -410,7 +410,14 @@ pub struct BookConfig { /// Does this book support more than one language? pub multilingual: bool, /// The main language of the book. - pub language: Option, + pub language: String, + /// The location of the fluent localisation files. + pub locales: PathBuf, + /// The location of the "core" fluent files that are shared across all + /// localisations. + pub shared_locale_resources: Option>, + /// The languages that are ready for public consumption. + pub available_languages: Option>, } impl Default for BookConfig { @@ -421,7 +428,10 @@ impl Default for BookConfig { description: None, src: PathBuf::from("src"), multilingual: false, - language: Some(String::from("en")), + language: String::from("en"), + locales: PathBuf::from("locales"), + shared_locale_resources: None, + available_languages: None, } } } @@ -673,6 +683,9 @@ mod tests { description = "A completely useless book" multilingual = true src = "source" + locales = "locales" + shared-locale-resources = ["locales/core.ftl"] + available-languages = ["ja"] language = "ja" [build] @@ -708,7 +721,10 @@ mod tests { description: Some(String::from("A completely useless book")), multilingual: true, src: PathBuf::from("source"), - language: Some(String::from("ja")), + locales: PathBuf::from("locales"), + available_languages: Some(vec!["ja".to_string()]), + shared_locale_resources: Some(vec![PathBuf::from("locales/core.ftl")]), + language: String::from("ja"), }; let build_should_be = BuildConfig { build_dir: PathBuf::from("outputs"), diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index d58bc805b4..46cfe4aae2 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -14,6 +14,7 @@ use std::path::{Path, PathBuf}; use handlebars::Handlebars; use regex::{Captures, Regex}; +use unic_langid::LanguageIdentifier; #[derive(Default)] pub struct HtmlHandlebars; @@ -26,7 +27,7 @@ impl HtmlHandlebars { fn render_item( &self, item: &BookItem, - mut ctx: RenderItemContext<'_>, + mut ctx: RenderItemContext<'_, '_, '_, '_>, print_content: &mut String, ) -> Result<()> { // FIXME: This should be made DRY-er and rely less on mutable state @@ -36,45 +37,99 @@ impl HtmlHandlebars { _ => return Ok(()), }; - let content = ch.content.clone(); - let content = utils::render_markdown(&content, ctx.html_config.curly_quotes); - - let fixed_content = utils::render_markdown_with_path( - &ch.content, - ctx.html_config.curly_quotes, - Some(&path), - ); - print_content.push_str(&fixed_content); - - // Update the context with data for this file - let ctx_path = path - .to_str() - .with_context(|| "Could not convert path to str")?; - let filepath = Path::new(&ctx_path).with_extension("html"); - // "print.html" is used for the print page. if path == Path::new("print.md") { bail!("{} is reserved for internal use", path.display()); }; - let book_title = ctx - .data - .get("book_title") - .and_then(serde_json::Value::as_str) - .unwrap_or(""); + let filepath = Path::new(path).with_extension("html"); + + let current_language_path = ctx + .locale_data + .paths + .get(ctx.current_language) + .unwrap() + .strip_prefix(ctx.root_path) + .unwrap() + .join(&filepath); + let relative_to_root = utils::fs::path_to_root(¤t_language_path); + + let available_languages = ctx + .locale_data + .languages + .iter() + .map(|locale| { + let relative_path = relative_to_root.join( + ctx.locale_data + .paths + .get(locale) + .unwrap() + .strip_prefix(&ctx.root_path) + .unwrap(), + ); + + json!({ + "identifier": locale.to_string(), + "permalink": relative_path.join(&filepath) + }) + }) + .collect::>(); + + let language = ctx.current_language.to_string(); + ctx.data.insert("lang".to_owned(), json!(language)); + + let locale_content = ctx.handlebars.render_template(&ch.content, &ctx.data)?; + let content = utils::render_markdown(&locale_content, ctx.html_config.curly_quotes); + let path_to_root = ctx + .destination + .strip_prefix(ctx.root_path) + .with_context(|| "Destination outside of root_path")?; + + let fixed_content = utils::render_markdown_with_path( + &locale_content, + ctx.html_config.curly_quotes, + Some(path), + ); + print_content.push_str(&fixed_content); - let title = match book_title { - "" => ch.name.clone(), - _ => ch.name.clone() + " - " + book_title, + let title = { + let chapter_title = ctx + .handlebars + .render_template( + &ch.name, + &json!({ "lang": ctx.current_language.to_string() }), + ) + .with_context(|| "Couldn't render chapter title")?; + let book_title = ctx + .data + .get("book_title") + .and_then(serde_json::Value::as_str) + .unwrap_or(""); + + if book_title.is_empty() { + ch.name.clone() + } else { + format!( + "{chapter} - {book}", + chapter = chapter_title, + book = book_title + ) + } }; + ctx.data.insert( + "language".to_owned(), + json!({ "identifier": language, "permalink": current_language_path }), + ); + ctx.data + .insert("available_languages".to_owned(), json!(available_languages)); ctx.data.insert("path".to_owned(), json!(path)); ctx.data.insert("content".to_owned(), json!(content)); ctx.data.insert("chapter_title".to_owned(), json!(ch.name)); ctx.data.insert("title".to_owned(), json!(title)); ctx.data.insert( "path_to_root".to_owned(), - json!(utils::fs::path_to_root(&path)), + json!(utils::fs::path_to_root(path_to_root.join(path))), ); if let Some(ref section) = ch.number { ctx.data @@ -93,12 +148,15 @@ impl HtmlHandlebars { if ctx.is_index { ctx.data.insert("path".to_owned(), json!("index.md")); - ctx.data.insert("path_to_root".to_owned(), json!("")); + ctx.data.insert( + "path_to_root".to_owned(), + json!(utils::fs::path_to_root(path_to_root.join("index.md"))), + ); ctx.data.insert("is_index".to_owned(), json!("true")); let rendered_index = ctx.handlebars.render("index", &ctx.data)?; let rendered_index = self.post_process(rendered_index, &ctx.html_config.playpen, ctx.edition); - debug!("Creating index.html from {}", ctx_path); + debug!("Creating index.html from {}", path.display()); utils::fs::write_file(&ctx.destination, "index.html", rendered_index.as_bytes())?; } @@ -218,6 +276,7 @@ impl HtmlHandlebars { &self, data: &mut serde_json::Map, print_content: &str, + root_path: &Path, ) { // Make sure that the Print chapter does not display the title from // the last rendered chapter by removing it from its context @@ -227,7 +286,7 @@ impl HtmlHandlebars { data.insert("content".to_owned(), json!(print_content)); data.insert( "path_to_root".to_owned(), - json!(utils::fs::path_to_root(Path::new("print.md"))), + json!(utils::fs::path_to_root(root_path.join("print.md"))), ); } @@ -308,12 +367,40 @@ impl Renderer for HtmlHandlebars { fn render(&self, ctx: &RenderContext) -> Result<()> { let html_config = ctx.config.html_config().unwrap_or_default(); let src_dir = ctx.root.join(&ctx.config.book.src); - let destination = &ctx.destination; + let locale_dir = ctx.root.join(&ctx.config.book.locales); + + let base_destination = &ctx.destination; let book = &ctx.book; + let multilingual = ctx.config.book.multilingual; + let available_languages = &ctx.config.book.available_languages; let build_dir = ctx.root.join(&ctx.config.build.build_dir); - if destination.exists() { - utils::fs::remove_dir_content(destination) + if base_destination.exists() { + utils::fs::remove_dir_content(base_destination) + .with_context(|| "Unable to remove stale HTML output")?; + } + + let shared_locale_resources = + ctx.config + .book + .shared_locale_resources + .clone() + .map(|mut v| { + for path in &mut v { + *path = ctx.root.join(&path).to_path_buf(); + } + v + }); + + let language = ctx + .config + .book + .language + .parse::() + .with_context(|| "Invalid unicode language identifier")?; + + if base_destination.exists() { + utils::fs::remove_dir_content(base_destination) .with_context(|| "Unable to remove stale HTML output")?; } @@ -349,47 +436,110 @@ impl Renderer for HtmlHandlebars { debug!("Register handlebars helpers"); self.register_hbs_helpers(&mut handlebars, &html_config); - let mut data = make_data(&ctx.root, &book, &ctx.config, &html_config)?; + debug!("Register Fluent helpers"); + + let locales = if multilingual { + let loader = fluent_templates::ArcLoader::new(&locale_dir, language.clone()) + .shared_resources(shared_locale_resources.as_deref()) + .build() + .expect("Couldn't load fluent"); + let locales = loader.locales(); + let helper = fluent_templates::FluentHelper::new(loader); + handlebars.register_helper("fluent", Box::new(helper)); + + if let Some(languages) = available_languages { + languages + .iter() + .map(|s| s.parse::()) + .collect::, unic_langid::LanguageIdentifierError>>() + .with_context(|| "Invalid language identifiers")? + } else { + locales + } + } else { + vec![language.clone()] + }; + + let locale_destination_iter = locales.iter().cloned().map(|locale| { + let dir = if locale != language { + base_destination.join(locale.to_string()) + } else { + base_destination.clone() + }; - // Print version - let mut print_content = String::new(); + (locale, dir) + }); - fs::create_dir_all(&destination) + fs::create_dir_all(&base_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, - html_config: html_config.clone(), - edition: ctx.config.rust.edition, - }; - self.render_item(item, ctx, &mut print_content)?; - is_index = false; - } + let locale_data = LocalizationData { + languages: locales + .iter() + .cloned() + .collect::>(), + paths: locale_destination_iter.collect(), + }; - // 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)); - } + for locale in locales.clone() { + let destination = locale_data.paths.get(&locale).unwrap(); + let mut data = make_data( + &ctx.root, + &book, + &handlebars, + &ctx.config, + &html_config, + &locale, + )?; - // Render the handlebars template with the data - debug!("Render template"); - let rendered = handlebars.render("index", &data)?; + // 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(), + root_path: base_destination, + data: data.clone(), + is_index, + locale_data: &locale_data, + current_language: &locale, + html_config: html_config.clone(), + edition: ctx.config.rust.edition, + }; + self.render_item(item, ctx, &mut print_content)?; + is_index = false; + } + + let path_to_root = destination + .strip_prefix(base_destination) + .with_context(|| "destination is outside of root_path")?; + + // Print version + self.configure_print_version(&mut data, &print_content, path_to_root); + if let Some(ref title) = ctx.config.book.title { + data.insert("title".to_owned(), json!(title)); + } - let rendered = self.post_process(rendered, &html_config.playpen, ctx.config.rust.edition); + // Render the handlebars template with the data + debug!("Render template"); + let rendered = handlebars.render("index", &data)?; - utils::fs::write_file(&destination, "print.html", rendered.as_bytes())?; - debug!("Creating print.html ✓"); + let rendered = + self.post_process(rendered, &html_config.playpen, 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) + self.copy_static_files(&base_destination, &theme, &html_config) .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, &base_destination) .with_context(|| "Unable to copy across additional CSS and JS")?; // Render search index @@ -397,12 +547,18 @@ impl Renderer for HtmlHandlebars { { let search = html_config.search.unwrap_or_default(); if search.enable { - super::search::create_files(&search, &destination, &book)?; + super::search::create_files(&search, &base_destination, &book)?; } } // 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"])?; + utils::fs::copy_files_except_ext( + &src_dir, + &base_destination, + true, + Some(&build_dir), + &["md"], + )?; Ok(()) } @@ -411,23 +567,30 @@ impl Renderer for HtmlHandlebars { fn make_data( root: &Path, book: &Book, + handlebars: &Handlebars<'_>, config: &Config, html_config: &HtmlConfig, + locale: &LanguageIdentifier, ) -> Result> { trace!("make_data"); + let locale_json = json!({ "lang": locale.to_string() }); let mut data = serde_json::Map::new(); - data.insert( - "language".to_owned(), - json!(config.book.language.clone().unwrap_or_default()), - ); + data.insert("multilingual".to_owned(), json!(config.book.multilingual)); data.insert( "book_title".to_owned(), - json!(config.book.title.clone().unwrap_or_default()), + json!(handlebars + .render_template(config.book.title.as_deref().unwrap_or(""), &locale_json) + .with_context(|| "Failed to render book title")?), ); data.insert( "description".to_owned(), - json!(config.book.description.clone().unwrap_or_default()), + json!(handlebars + .render_template( + config.book.description.as_deref().unwrap_or(""), + &locale_json, + ) + .with_context(|| "Failed to render book title")?), ); data.insert("favicon".to_owned(), json!("favicon.png")); if let Some(ref livereload) = html_config.livereload_url { @@ -545,12 +708,16 @@ fn make_data( json!((!ch.sub_items.is_empty()).to_string()), ); - chapter.insert("name".to_owned(), json!(ch.name)); - if let Some(ref path) = ch.path { - let p = path + let name = handlebars + .render_template(&ch.name, &locale_json) + .with_context(|| "Couldn't render chapter title")?; + + chapter.insert("name".to_owned(), json!(name)); + if let Some(path) = &ch.path { + let path = path .to_str() .with_context(|| "Could not convert path to str")?; - chapter.insert("path".to_owned(), json!(p)); + chapter.insert("path".to_owned(), json!(path)); } } BookItem::Separator => { @@ -750,15 +917,25 @@ fn partition_source(s: &str) -> (String, String) { (before, after) } -struct RenderItemContext<'a> { +struct RenderItemContext<'a, 'b, 'c, 'd> { handlebars: &'a Handlebars<'a>, destination: PathBuf, + root_path: &'b Path, + locale_data: &'c LocalizationData, + current_language: &'d LanguageIdentifier, data: serde_json::Map, is_index: bool, html_config: HtmlConfig, edition: Option, } +struct LocalizationData { + /// What languages are available. + languages: std::collections::HashSet, + /// The location of each languages build directory. + paths: HashMap, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/renderer/html_handlebars/helpers/toc.rs b/src/renderer/html_handlebars/helpers/toc.rs index 33857d8621..b8c8bc0084 100644 --- a/src/renderer/html_handlebars/helpers/toc.rs +++ b/src/renderer/html_handlebars/helpers/toc.rs @@ -120,7 +120,7 @@ impl HelperDef for RenderToc { .replace("\\", "/"); // Add link - out.write(&utils::fs::path_to_root(¤t_path))?; + out.write(&utils::fs::path_to_root(¤t_path).to_string_lossy())?; out.write(&tmp)?; out.write("\"")?; diff --git a/src/theme/index.hbs b/src/theme/index.hbs index e8a92dd098..553fb21cf3 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -127,6 +127,17 @@

{{ book_title }}

+ {{#if multilingual}} + + {{/if}} @@ -151,6 +162,13 @@
{{/if}} + + +