@@ -411,6 +411,9 @@ pub struct BookConfig {
411411 pub multilingual : bool ,
412412 /// The main language of the book.
413413 pub language : Option < String > ,
414+ /// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).
415+ /// When not specified, the correct text direction is derived from [BookConfig::language].
416+ pub text_direction : Option < TextDirection > ,
414417}
415418
416419impl Default for BookConfig {
@@ -422,6 +425,44 @@ impl Default for BookConfig {
422425 src : PathBuf :: from ( "src" ) ,
423426 multilingual : false ,
424427 language : Some ( String :: from ( "en" ) ) ,
428+ text_direction : None ,
429+ }
430+ }
431+ }
432+
433+ impl BookConfig {
434+ /// Gets the realized text direction, either from [BookConfig::text_direction]
435+ /// or derived from [BookConfig::language], to be used by templating engines.
436+ pub fn realized_text_direction ( & self ) -> TextDirection {
437+ if let Some ( direction) = self . text_direction {
438+ direction
439+ } else {
440+ TextDirection :: from_lang_code ( & self . language . clone ( ) . unwrap_or_default ( ) )
441+ }
442+ }
443+ }
444+
445+ /// Text direction to use for HTML output
446+ #[ derive( Debug , Copy , Clone , PartialEq , Serialize , Deserialize ) ]
447+ pub enum TextDirection {
448+ /// Left to right.
449+ #[ serde( rename = "ltr" ) ]
450+ LeftToRight ,
451+ /// Right to left
452+ #[ serde( rename = "rtl" ) ]
453+ RightToLeft ,
454+ }
455+
456+ impl TextDirection {
457+ /// Gets the text direction from language code
458+ pub fn from_lang_code ( code : & str ) -> Self {
459+ match code {
460+ // list sourced from here: https://github.com/abarrak/rtl/blob/master/lib/rtl/core.rb#L16
461+ "ar" | "ara" | "arc" | "ae" | "ave" | "egy" | "he" | "heb" | "nqo" | "pal" | "phn"
462+ | "sam" | "syc" | "syr" | "fa" | "per" | "fas" | "ku" | "kur" | "ur" | "urd" => {
463+ TextDirection :: RightToLeft
464+ }
465+ _ => TextDirection :: LeftToRight ,
425466 }
426467 }
427468}
@@ -788,6 +829,7 @@ mod tests {
788829 multilingual : true ,
789830 src : PathBuf :: from ( "source" ) ,
790831 language : Some ( String :: from ( "ja" ) ) ,
832+ text_direction : None ,
791833 } ;
792834 let build_should_be = BuildConfig {
793835 build_dir : PathBuf :: from ( "outputs" ) ,
@@ -1140,6 +1182,184 @@ mod tests {
11401182 assert_eq ! ( & get_404_output_file( & html_config. input_404) , "missing.html" ) ;
11411183 }
11421184
1185+ #[ test]
1186+ fn text_direction_ltr ( ) {
1187+ let src = r#"
1188+ [book]
1189+ text-direction = "ltr"
1190+ "# ;
1191+
1192+ let got = Config :: from_str ( src) . unwrap ( ) ;
1193+ assert_eq ! ( got. book. text_direction, Some ( TextDirection :: LeftToRight ) ) ;
1194+ }
1195+
1196+ #[ test]
1197+ fn text_direction_rtl ( ) {
1198+ let src = r#"
1199+ [book]
1200+ text-direction = "rtl"
1201+ "# ;
1202+
1203+ let got = Config :: from_str ( src) . unwrap ( ) ;
1204+ assert_eq ! ( got. book. text_direction, Some ( TextDirection :: RightToLeft ) ) ;
1205+ }
1206+
1207+ #[ test]
1208+ fn text_direction_none ( ) {
1209+ let src = r#"
1210+ [book]
1211+ "# ;
1212+
1213+ let got = Config :: from_str ( src) . unwrap ( ) ;
1214+ assert_eq ! ( got. book. text_direction, None ) ;
1215+ }
1216+
1217+ #[ test]
1218+ fn test_text_direction ( ) {
1219+ let mut cfg = BookConfig :: default ( ) ;
1220+
1221+ // test deriving the text direction from language codes
1222+ cfg. language = Some ( "ar" . into ( ) ) ;
1223+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: RightToLeft ) ;
1224+
1225+ cfg. language = Some ( "he" . into ( ) ) ;
1226+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: RightToLeft ) ;
1227+
1228+ cfg. language = Some ( "en" . into ( ) ) ;
1229+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: LeftToRight ) ;
1230+
1231+ cfg. language = Some ( "ja" . into ( ) ) ;
1232+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: LeftToRight ) ;
1233+
1234+ // test forced direction
1235+ cfg. language = Some ( "ar" . into ( ) ) ;
1236+ cfg. text_direction = Some ( TextDirection :: LeftToRight ) ;
1237+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: LeftToRight ) ;
1238+
1239+ cfg. language = Some ( "ar" . into ( ) ) ;
1240+ cfg. text_direction = Some ( TextDirection :: RightToLeft ) ;
1241+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: RightToLeft ) ;
1242+
1243+ cfg. language = Some ( "en" . into ( ) ) ;
1244+ cfg. text_direction = Some ( TextDirection :: LeftToRight ) ;
1245+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: LeftToRight ) ;
1246+
1247+ cfg. language = Some ( "en" . into ( ) ) ;
1248+ cfg. text_direction = Some ( TextDirection :: RightToLeft ) ;
1249+ assert_eq ! ( cfg. realized_text_direction( ) , TextDirection :: RightToLeft ) ;
1250+ }
1251+
1252+ #[ test]
1253+ fn text_drection_from_lang_code ( ) {
1254+ // test all right-to-left languages
1255+ assert_eq ! (
1256+ TextDirection :: from_lang_code( "ar" ) ,
1257+ TextDirection :: RightToLeft
1258+ ) ;
1259+ assert_eq ! (
1260+ TextDirection :: from_lang_code( "ara" ) ,
1261+ TextDirection :: RightToLeft
1262+ ) ;
1263+ assert_eq ! (
1264+ TextDirection :: from_lang_code( "arc" ) ,
1265+ TextDirection :: RightToLeft
1266+ ) ;
1267+ assert_eq ! (
1268+ TextDirection :: from_lang_code( "ae" ) ,
1269+ TextDirection :: RightToLeft
1270+ ) ;
1271+ assert_eq ! (
1272+ TextDirection :: from_lang_code( "ave" ) ,
1273+ TextDirection :: RightToLeft
1274+ ) ;
1275+ assert_eq ! (
1276+ TextDirection :: from_lang_code( "egy" ) ,
1277+ TextDirection :: RightToLeft
1278+ ) ;
1279+ assert_eq ! (
1280+ TextDirection :: from_lang_code( "he" ) ,
1281+ TextDirection :: RightToLeft
1282+ ) ;
1283+ assert_eq ! (
1284+ TextDirection :: from_lang_code( "heb" ) ,
1285+ TextDirection :: RightToLeft
1286+ ) ;
1287+ assert_eq ! (
1288+ TextDirection :: from_lang_code( "nqo" ) ,
1289+ TextDirection :: RightToLeft
1290+ ) ;
1291+ assert_eq ! (
1292+ TextDirection :: from_lang_code( "pal" ) ,
1293+ TextDirection :: RightToLeft
1294+ ) ;
1295+ assert_eq ! (
1296+ TextDirection :: from_lang_code( "phn" ) ,
1297+ TextDirection :: RightToLeft
1298+ ) ;
1299+ assert_eq ! (
1300+ TextDirection :: from_lang_code( "sam" ) ,
1301+ TextDirection :: RightToLeft
1302+ ) ;
1303+ assert_eq ! (
1304+ TextDirection :: from_lang_code( "syc" ) ,
1305+ TextDirection :: RightToLeft
1306+ ) ;
1307+ assert_eq ! (
1308+ TextDirection :: from_lang_code( "syr" ) ,
1309+ TextDirection :: RightToLeft
1310+ ) ;
1311+ assert_eq ! (
1312+ TextDirection :: from_lang_code( "fa" ) ,
1313+ TextDirection :: RightToLeft
1314+ ) ;
1315+ assert_eq ! (
1316+ TextDirection :: from_lang_code( "per" ) ,
1317+ TextDirection :: RightToLeft
1318+ ) ;
1319+ assert_eq ! (
1320+ TextDirection :: from_lang_code( "fas" ) ,
1321+ TextDirection :: RightToLeft
1322+ ) ;
1323+ assert_eq ! (
1324+ TextDirection :: from_lang_code( "ku" ) ,
1325+ TextDirection :: RightToLeft
1326+ ) ;
1327+ assert_eq ! (
1328+ TextDirection :: from_lang_code( "kur" ) ,
1329+ TextDirection :: RightToLeft
1330+ ) ;
1331+ assert_eq ! (
1332+ TextDirection :: from_lang_code( "ur" ) ,
1333+ TextDirection :: RightToLeft
1334+ ) ;
1335+ assert_eq ! (
1336+ TextDirection :: from_lang_code( "urd" ) ,
1337+ TextDirection :: RightToLeft
1338+ ) ;
1339+
1340+ // test some left-to-right languages
1341+ assert_eq ! (
1342+ TextDirection :: from_lang_code( "de" ) ,
1343+ TextDirection :: LeftToRight
1344+ ) ;
1345+ assert_eq ! (
1346+ TextDirection :: from_lang_code( "en" ) ,
1347+ TextDirection :: LeftToRight
1348+ ) ;
1349+ assert_eq ! (
1350+ TextDirection :: from_lang_code( "es" ) ,
1351+ TextDirection :: LeftToRight
1352+ ) ;
1353+ assert_eq ! (
1354+ TextDirection :: from_lang_code( "ja" ) ,
1355+ TextDirection :: LeftToRight
1356+ ) ;
1357+ assert_eq ! (
1358+ TextDirection :: from_lang_code( "sv" ) ,
1359+ TextDirection :: LeftToRight
1360+ ) ;
1361+ }
1362+
11431363 #[ test]
11441364 #[ should_panic( expected = "Invalid configuration file" ) ]
11451365 fn invalid_language_type_error ( ) {
0 commit comments