多语言网站大规模CSS的有效开发和维护

多语言网站大规模CSS的有效开发和维护,css,internationalization,multilingual,right-to-left,Css,Internationalization,Multilingual,Right To Left,考虑这个场景:您正在开发一个多语言web应用程序。如果您所针对的所有语言都是LTR或RTL,则不需要特定于语言的CSS规则。但是,如果目标语言是LTR和RTL语言的混合,则需要为每种语言指定页面的阅读方向 如果将dir='ltr'或dir='rtl'添加到元素中,从逻辑上讲,您应该期望它能够执行必要的操作 但是,您实际上需要在规则中切换所有右和左设置,例如文本方向和边距。您还需要更改规则,如边距:0 10px 0 20px至边距:0 20px 0 10px W3C标准可以通过允许方向相关规则增加

考虑这个场景:您正在开发一个多语言web应用程序。如果您所针对的所有语言都是
LTR
RTL
,则不需要特定于语言的CSS规则。但是,如果目标语言是
LTR
RTL
语言的混合,则需要为每种语言指定页面的阅读方向

如果将
dir='ltr'
dir='rtl'
添加到
元素中,从逻辑上讲,您应该期望它能够执行必要的操作

但是,您实际上需要在规则中切换所有
设置,例如
文本方向
边距
。您还需要更改规则,如
边距:0 10px 0 20px
边距:0 20px 0 10px

W3C标准可以通过允许方向相关规则增加两个值来避免这个问题。换句话说,它们可以允许如下内容,而不是
-right
-left
(如
margin right
margin left
):

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 */
本质上,在当前可以输入
方向词的所有规则/值中,您可以改为在
附近或
远处编写


鉴于当前CSS版本的弱点,我正在寻找一些建议,以简化双向大型web应用程序的创建和维护。

不幸的是,CSS是基于左和右的概念运行的,这是正确的行为。有一些工具可以帮助您将CSS从LTR迁移到RTL,据我所知,这是一个可以翻转-左/右属性的工具,等等。

我认为如果您想要这样做,您必须使用类似的工具

有一个解决方案,但我不知道有任何浏览器支持它。即使他们这样做了,它也很可能会受到传统浏览器支持的阻碍(但这什么时候不是问题?)


撇开所有的技术挑战不谈,一旦完成了,我想可以很好地实现它,您仍然必须确保将每个对左/右的引用外部化,然后使用类似于移动浏览器响应设计中使用媒体查询的方式。

因为您将问题改为预先考虑而不是事后考虑

不太合乎逻辑 您声明,“如果您将
dir='rtl'
dir='ltr'
属性添加到
元素中,逻辑上您希望它发挥神奇作用。”神奇之处显然是转换,“在
文本方向
边距
等规则中,所有
和所有

然而,我不同意你所希望的在所有情况下都是合乎逻辑的结果。有一些
文本方向
边距
的使用不一定与主站点的
文本方向
相关。对于
边距
,这似乎是不言而喻的,因为很多时候
边距
可能是不可否认的e用于对与文本完全无关的元素进行某种形式的定位。
文本方向
不会自动翻转可能不太明显,但仍然有效。假设有一个英文(LTR)站点,其中包含阿拉伯语(RTL)的块引号。现在将主语言转换为希伯来语(RTL)但仍然有阿拉伯语的引语——这不应该自动转换为LTR,因为这是不正确的

浮动元素、绝对定位元素等(使用
值)可能会因为
文本方向
而被定位,也可能不会被定位

因此,基本上,它归结为这样一个事实:当一个人设计一个多语言的网站时,它将翻转
文本方向
,在每个阶段,他必须考虑基于LTR或RTL配置的元素应该做什么

这意味着,在我看来,CSS没有弱点。任何弱点都存在于设计实现中。


设计中的纯人类先见之明 因此,好的方法是选择你的标准方向(比如LTR),并用直接的CSS将其作为你的“基本”计划

然后,对于由于RTL的更改而要翻转的元素,您可以编写额外的CSS来说明这一点,方法是将一个类应用于目标的
,或者使用属性选择器,如
body[dir=RTL]
。然后仔细考虑所做的每个元素是否应该受到该更改的影响,如果是,则添加css(使用附加的方向选择器添加特定性以覆盖):

这就是像or这样的预处理器(Dave在他的回答中较少提及)可能会有帮助的地方(见下面我的更新),但这仍然是一个需要深思熟虑的解决方案

如果您不想让代码充斥着多余的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;
  }  
}
您可以为RTL css创建一个单独的样式表,该样式表在站点确定为该特定方向时加载。其可能的缺点是,它将“切换”代码与原始代码分开,因此维护可能更具挑战性(关于主代码中受RTL转换影响的元素的良好注释文档可以弥补这一缺陷)


(更新)使用更少的帮助? 下面是一个关于通过LESS这样的预处理器使过程更加简洁的想法。这个例子使用了LESS 1.4(目前处于beta版)中的功能

想法1
  • 优点:将所有值更改为一个选择器更改
  • 缺点:在参数中编写值需要更多的工作
构建一个mixin,将所需选择器应用于

.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)  {}
}
生成此成品代码

.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;
}
.test1 {
  .padding-near(10px, 30px, 2px, 40px);
  .margin-near(0, 10px, 0, 20px);
  .float-near(right);
}

.test2 {
  .float-near(left);
}
想法2
  • 进阶
    //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;
      }  
    }
    
    .test1 {
      .padding-near(10px, 30px, 2px, 40px);
      .margin-near(0, 10px, 0, 20px);
      .float-near(right);
    }
    
    .test2 {
      .float-near(left);
    }
    
    .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;
    }
    
    ul, menu, dir {
        -webkit-margin-before: 1em;
        -webkit-margin-after: 1em;
        -webkit-margin-start: 0px;
        -webkit-margin-end: 0px;
        -webkit-padding-start: 40px;
    }