@@ -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
422425impl 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