19

Consider this scenario: You are developing a multilingual web application. If all of languages you are targeting are either LTR or RTL, you have no need for language-specific CSS rules. However, if your target languages are a mix of LTR and RTL languages, you need to specify the page's reading-direction for each language.

If you add dir='ltr' or dir='rtl' to the <body> element, you logically should expect it should do the necessary magics.

However, you actually need to switch all right and left settings in rules such as text-direction and margin. You also need to change rules like margin: 0 10px 0 20px; to margin: 0 20px 0 10px;

The W3C standard could avoid this issue by permitting two more values for direction-related rules. In other words, instead of -right and -left (as in margin-right and margin-left), they could allow something like the following:

div.foo { margin-near: 100px; } 
/* This would be equivalent to margin-left in LTR, and margin-right in RTL */

div.bar { margin-far: 100px; } 
/* This would be equivalent to margin-right in LTR, and margin-left in RTL */

In essence, in all rules/values where you can currently enter a left or right direction-based word, you could instead write a near or far.

Given the current weaknesses in the current version of CSS, I am looking for some suggestions to streamline the creation and maintenance bi-direction large web-apps.

Handsome Nerd
  • 17,114
  • 22
  • 95
  • 173
  • 2
    I think the solution will be to dynamically generate your CSS, using something server-side. Then you could use variable data like this and easily change it all in one place. – Brad Oct 13 '12 at 18:35
  • 1
    I thought about it too, years ago. I thinks the names should be "near" and "far", or "home" and "end". – Yaakov Shoham Oct 13 '12 at 18:42
  • @Y.Shoham Thanks,"near" and "far" are even better candidate from "normal" and "reversed". I updated the question. – Handsome Nerd Oct 13 '12 at 18:53
  • CSS only has a weakness when considered based off this part of your question: "wrote ... then you decided." Obviously, the best approach is to plan for RTL from the start if that is an intended goal of the site. In that case, you would be designing the CSS as you go for both LTR and RTL, and CSS is more than capable of handling that. But yes, not planning ahead is going to create a headache on how to convert the code to something else. A good suggestion on that has been given below, but it would be hard to create a perfect "switch" that does not involve human effort to see that all looks good. – ScottS Apr 23 '13 at 14:00
  • I see you changed your question (perhaps based off my above comment). Originally the question was a conversion to RTL as an after thought, now you've made the question as if it were a fore thought. That expands the set of answers that are valid, so I think I'll post an answer. – ScottS Apr 24 '13 at 12:20
  • Per your update in your question on what you are seeking, I added an update to my answer regarding some ideas how LESS might be used to streamline the process. – ScottS Apr 26 '13 at 20:25

7 Answers7

7

Since you changed the question to one of forethought rather than afterthought.

Not quite so logical

You state, "If you add dir='rtl' or dir='ltr' attribute to <body> element logically you expect that it do the magic." The magic apparently being to convert, "all right with left and all left with right in rules like text-direction and margin."

However, I do not agree that in all cases what you desire is necessarily a logical outcome. There are instances of text-direction and margin use that are not necessarily related to the text-direction of the main site. For margin, this seems self evident, as many times margin may be used for some form of positioning of elements wholly unrelated to the text. That text-direction would not automatically flip may be less obvious, but still valid. Assume one has an English (LTR) site with a block quote of Arabic (RTL) in it. Now one converts the main language to Hebrew (RTL) but still has the quote in Arabic--that should not automatically flip to LTR as it would be incorrect.

Things like floated elements, absolutely positioned elements, etc. (which use right and left values) may or may not be positioned as they are because of the text-direction.

So basically, it boils down to the fact that as one designs a site that is intended to be multilingual in a way that will flip text-direction, one must think through at each stage what the element should be doing based on a LTR or RTL configuration.

What this means is, in my opinion, CSS does not have a weakness. Any weakness is in design implementation.


Pure Human Forethought in Design

So the good method would be to pick your standard direction (say LTR) and have that be your "base" plan with straight CSS.

Then for elements you want to flip because of a change to RTL, you code additional CSS to account for that by either applying a class to the <body> to target with or using an attribute selector like body[dir=rtl]. Then you think through each element you make whether it should be affected by that change, and if it is you add css (with the additional directional selector adding specificity to override):

.someClass {
    color: red;
    margin: 0 10px 0 20px;
    float: right;
}

body[dir=rtl] .someClass {
    margin: 0 20px 0 10px;
    /* kept float as right */
}

.someOtherClass {
    border: 1px 10px 1px 1px;
    margin: 0 30px 0 50px;
    float: left;
}

body[dir=rtl] .someOtherClass {
    border: 1px 1px 1px 10px; /* changed border for text */
    margin: 0 50px 0 30px;
    float: right;
}

This is where a preprocessor like LESS or SASS (SCSS) (Dave mentioned LESS in his answer) could prove helpful (see my update below), but that is still a solution that requires forethought.

If you don't want code riddled with excess CSS

You could have a separate style sheet for the RTL css that is loaded at the time the site is determined to be that particular direction. The possible disadvantage of this is that it separates out the "switching" code from the original code so maintenance could be more of a challenge (good comment documentation about elements that are affected by RTL conversion in the main code could compensate for this).


(UPDATE) Using LESS to Help?

Here is a thought on making the process more concise through a preprocessor like LESS. This example is using functionality from LESS 1.4 (currently in beta).

Idea 1

  • Advantage: keeps all values changed to one selector change.
  • Disadvantage: more work to code values in arguments.

Build a mixin to apply desired selector to

.rtl(...) {
  //getting the number of arguments 
  //(weeding out nested commas in parenthesis to do it)
  //they are expected to be grouped in pairs of TWO, 
  //as (property, value, property, value, etc.)
  @mainArgs: @arguments;
  @numArgs: unit(`"@{mainArgs}".replace(/\([^)]*\)/g,"").split(',').length`);

  //keep everything in one selector
  body[dir=rtl] & {
     //start the loop at 1
    .rtlPropLoop(@numArgs);
  }
  //loop to change all properties
  .rtlPropLoop(@total; @index: 1; @prop: extract(@mainArgs, @index); @value: extract(@mainArgs, (@index + 1))) when (@index =< @total) {
      //need to define all properties that could be switched
      //I've done just four here

      .setProp(ML) { //margin left
        margin-left: @value;
      }
      .setProp(MR) { //margin right
        margin-right: @value;
      }
      .setProp(FL) { //float
        float: @value;
      }
      .setProp(TD) { //text direction
        text-direction: @value;
      }
     //... define more possible values to switch

      //call setProp
      .setProp(@prop);
      //continue loop
      .rtlPropLoop(@total, (@index + 2));
  }
  //end loop
  .rtlPropLoop(@total, @index) when (@index > @total)  {}
}

Then use it as needed in other selectors

.test {
  margin: 0 20px 0 10px;
  float: right;
  .rtl(ML, 20px, MR, 10px, FL, left);
}

.test2 a span {
  float: left;
  text-direction: rtl;
  .rtl(TD, ltr, FL, right);
}

Producing this finished code

.test {
  margin: 0 20px 0 10px;
  float: right;
}
body[dir=rtl] .test {
  margin-left: 20px;
  margin-right: 10px;
  float: left;
}
.test2 a span {
  float: left;
  text-direction: rtl;
}
body[dir=rtl] .test2 a span {
  text-direction: ltr;
  float: right;
}

Idea 2

  • Advantage: allows you to use syntax similar to what you desire; could be easily modified to work with LESS 1.3.3.
  • Disadvantage: produces excess output css if multiple values are changed in one selector.

Build some helper mixins with syntax like what you expect in CSS

//define global variable for opposite direction
@oppDir: rtl;

//generic helper mixins used inside other helpers
//to auto flip right/left values
.flipSides(left) {
  @newSide: right;
}
.flipSides(right) {
  @newSide: left;
}

//specific property helper mixins
.padding-near(@top, @right, @bottom, @left) {
  padding: @top @right @bottom @left;
  body[dir=@{oppDir}] & {
    padding: @top @left @bottom @right;
  }
}
.margin-near(@top, @right, @bottom, @left) {
  margin: @top @right @bottom @left;
  body[dir=@{oppDir}] & {
    margin: @top @left @bottom @right;
  }
}
.float-near(@side) {
  float: @side;
  .flipSides(@side);
  body[dir=@{oppDir}] & {
    float: @newSide;
  }  
}

Use them to define both near and far for each direction at once

.test1 {
  .padding-near(10px, 30px, 2px, 40px);
  .margin-near(0, 10px, 0, 20px);
  .float-near(right);
}

.test2 {
  .float-near(left);
}

Producing this finished code (note repetition of .test1 for opposite direction)

.test1 {
  padding: 10px 30px 2px 40px;
  margin: 0 10px 0 20px;
  float: right;
}
body[dir=rtl] .test1 {
  padding: 10px 40px 2px 30px;
}
body[dir=rtl] .test1 {
  margin: 0 20px 0 10px;
}
body[dir=rtl] .test1 {
  float: left;
}
.test2 {
  float: left;
}
body[dir=rtl] .test2 {
  float: right;
}
ScottS
  • 71,703
  • 13
  • 126
  • 146
  • Brilliant answer, Scott. +1 for the concise example of `body[dir=rtl]` to override *specific* cascading styles. – Troy Alford Apr 25 '13 at 20:06
  • Thanks for you ideas, Did you also see http://anasnakawa.github.io/bi-app-sass/ that was mentioned in question? – Handsome Nerd Apr 27 '13 at 02:09
  • No, I did not view the link. Looks like it has some similar concepts about mixin use, and has set it up to load two different style sheets (which is probably best). Something along these lines is likely to be your best option for creation, maintenance, and efficiency. I'm not sure what else you are looking for. – ScottS Apr 27 '13 at 11:10
6

Unfortunately, CSS operates on the notion of left and right, and this is the correct behaviour. There are tools which will help you with migrating CSS from LTR to RTL - one that I know of is CSS Janus, which will flip the -left/-right properties, and more.

Jakub Wasilewski
  • 2,916
  • 22
  • 27
4

I think if you want this, you have to use something like lesscss.

There is a standard in the works to address this but I'm not aware of any browsers that support it. Even when they do, it is likely going to be held back by legacy browser support (but when hasn't this been a problem?).

All of the technical challenges aside, once this is done, I imagine to implement it well, you would still have to make sure to externalize every reference to left/right and then use something similar to the way media queries are used in responsive design for mobile browsers.

Dave
  • 6,141
  • 2
  • 38
  • 65
3

I use Closure stylesheets, and this stuff is as simple as a command-line flag.

I define everything as LTR (using direction:ltr where necessary), then using --output-orientation=RTL during the compile, I get the flipped CSS generated for me. Finally, when determining the locale (on the server), I also determine the CSS to inject into my template.

More info the LR flipping in Closure is at https://code.google.com/p/closure-stylesheets/#RTL_Flipping

lizard
  • 191
  • 1
  • 4
0

For additional background on one solution which attempted to overcome the weakness using SASS/LESS, check out http://anasnakawa.github.io/bi-app-sass/

Handsome Nerd
  • 17,114
  • 22
  • 95
  • 173
0

After three year of asking this question, know I see a trend in using logical direction vocabulary instead of 4-directional words.

CSS Logical Properties now address this issue. Flex model use start/end keywords Blink now supports start/end/before/after keywords. e.i.

ul, menu, dir {
    -webkit-margin-before: 1em;
    -webkit-margin-after: 1em;
    -webkit-margin-start: 0px;
    -webkit-margin-end: 0px;
    -webkit-padding-start: 40px;
}
Handsome Nerd
  • 17,114
  • 22
  • 95
  • 173
0

After around four years after posting this question, this need lastly is going to be address by w3. https://drafts.csswg.org/css-logical/

Properties that accept physical directional keyword values (top, bottom, left, or right) are redefined to also accept the appropriate flow-relative directional keywords. In such cases, the flow-relative values can be used in place of the corresponding physical values. For properties that take multiple keywords, combinations of flow-relative and physical values are not allowed (unless otherwise specified in a future specification).

Handsome Nerd
  • 17,114
  • 22
  • 95
  • 173