CSS 國際化指南
大廠技術(shù)??高級前端??Node進階
點擊上方?程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
本人翻譯者系奇舞團前端工程師
原文標題:CSS for internationalization
原文作者:Chen Hui jing
原文地址:https://chenhuijing.com/blog/css-for-i18n
我遇到過一些人,他們根本不認為CSS與國際化有關(guān),但如果你仔細想想,國際化不僅僅是將你網(wǎng)站上的內(nèi)容翻譯成多種語言,然后一勞永逸。這些內(nèi)容的呈現(xiàn)方式存在各種細微差別,影響母語人士使用您網(wǎng)站的體驗。
國際化沒有單一的規(guī)范定義,但W3C提供了以下指導(dǎo):
"國際化是一種產(chǎn)品、應(yīng)用程序或文檔的設(shè)計和開發(fā),可以方便地為不同文化、地區(qū)或語言的目標受眾進行本地化。"
從Unicode和字符編碼的使用,到為翻譯內(nèi)容提供服務(wù)的技術(shù)實現(xiàn),以及所述內(nèi)容的呈現(xiàn),這是一個需要覆蓋的領(lǐng)域。今天,我將只討論多語言支持的CSS相關(guān)方面。
CSS通過告訴瀏覽器頁面上的元素應(yīng)該如何設(shè)置樣式和布局來描述網(wǎng)頁的顯示。在使用CSS的多語言頁面上,我們可以使用幾種方法將不同的樣式應(yīng)用于不同的語言。
除此之外,CSS屬性還為腳本和書寫系統(tǒng)提供了布局和排版功能,而不僅僅是基于拉丁語的自上而下的水平排版功能,這種功能如今主要出現(xiàn)在WEB上。
所以,請做好準備,因為這可能會是一篇相當長的文章。ˉ_(ツ)_/ˉ
與語言相關(guān)的樣式
你有沒有想過Chrome怎么會問你是否想翻譯網(wǎng)頁的內(nèi)容?額...不好吧,也許只有我一個人關(guān)注過這類問題?這是因為HTML元素上的lang屬性。

lang屬性是一個非常重要的屬性,因為它識別WEB上文本內(nèi)容的語言,并且該信息在許多地方都被使用。前面提到的Chrome內(nèi)置翻譯,針對特定語言內(nèi)容的搜索引擎,以及屏幕閱讀器。
啊哈,也許你沒有想到屏幕閱讀器,但如果你不是一個屏幕閱讀器用戶或認識這樣的人,它可能不會放在心上。屏幕閱讀器利用語言信息,以適當?shù)目谝艉驼_的發(fā)音讀出內(nèi)容。
與語言相關(guān)的樣式化的關(guān)鍵在于在頁面標記中適當使用lang屬性。lang屬性將ISO 639[1]語言代碼識別為值。
更新:Tobias Bengfort[2]指出lang屬性使用了一個名為BCP 47的IETF規(guī)范[3],該規(guī)范很大程度上基于ISO 639標準。
在大多數(shù)情況下,您將使用像zh這樣的兩個字母的代碼來表示中文,但是中文(包括阿拉伯語等其他語言)被認為是一種宏語言,它由許多具有更具體的主語言子標記的語言組成。
有關(guān)如何構(gòu)造語言標記的深入解釋,請參閱HTML和XML中的語言標記[4]。
一般的指導(dǎo)是html元素必須總是有一個lang屬性集,然后由所有其他元素繼承。
"zh">
在同一頁上看到不同語言的內(nèi)容并不少見。在本例中,您將使用span或div包裝該內(nèi)容,并將正確的lang屬性應(yīng)用于包裝元素。
The?fourth?animal?in?the?Chinese?Zodiac?is?Rabbit?("zh">兔子).
既然我們已經(jīng)對其進行了分類,下面的技術(shù)將假設(shè)lang屬性已經(jīng)得到了負責任的實現(xiàn)。
:lang()偽類選擇器
事實證明:lang()偽類選擇器并不是那么有名。
但是這個偽類選擇器非常酷,因為它可以識別內(nèi)容的語言,即使該語言是在元素之外聲明的。
例如,一行包含兩種語言的標記,如下所示:
<p>
??We?use?<em>italicsem>?to?emphasise?words?in?English,?<span?lang="zh">但是中文則是用<em>著重號em>span>.
p>
可采用以下樣式:
em:lang(zh)?{
??font-style:?normal;
??text-emphasis:?dot;
}
如果您的瀏覽器支持text-emphasis的CSS屬性,您應(yīng)該能夠看到在em中的每個漢字中添加的強調(diào)標記(傳統(tǒng)上用于強調(diào)一系列東亞文本的排版符號)。Chrome需要-webkit-前綴(此處噓聲一片)。
我們用斜體字強調(diào)英語中的單詞,但是中文則是用著重號.
但關(guān)鍵是,lang屬性不是應(yīng)用于em元素,而是應(yīng)用于其父元素。偽類仍然有效。如果我們使用更常見的屬性選擇器,例如[lang=“zh],則該屬性必須位于em元素上才能生效。
使用屬性選擇器
這就引出了我們的下一項技術(shù),使用屬性選擇器。這些允許我們選擇具有特定屬性或具有特定值的屬性的元素。(插件時間,要了解更多關(guān)于屬性選擇器的信息,請嘗試您自己編寫的Codrops CSS參考條目[5])
有七種方法可以匹配屬性選擇器,但我只討論那些我認為與匹配lang屬性更相關(guān)的方法。我所有的例子都使用中文作為目標語言,所以zh及其變體。
更新:Amelia Bellamy Royds[6]指出,我的示例使屬性選擇器似乎是進行部分語言標記匹配所必需的,但:lang()偽類已經(jīng)涵蓋了該用例。
首先,我們可以使用以下語法精確匹配lang屬性值:
[lang="zh"]
/*?will?match?only?zh?*/
我之前提到過,漢語被認為是一種宏語言,這意味著它的語言標簽可以由額外的細節(jié)組成,例如腳本子標簽Hans或Hant(W3C說,如果需要區(qū)分,只使用腳本子標簽,否則不使用)、區(qū)域子標簽HK或TW等等。
重點是,語言標簽可以比兩個字母長。但最一般化的類別總是排在第一位,所以要針對以特定字符串開頭的屬性值,我們使用以下涉及^的語法:
[lang^="zh"]
/*將匹配zh,zh?HK,zh?Hans,zhong,zh123…
*基本上是以zh作為前兩個字符的任何內(nèi)容*/
還有另一種涉及|的語法,它將匹配選擇器中的確切值,或者匹配一個以值開頭,緊接著是-的值。這似乎只是為了語言子代碼匹配,不是嗎?
[lang|="zh"]
/*?將匹配?zh,?zh-HK,?zh-Hans,?zh-amazing,?zh-123?*/
請記住,對于屬性選擇器,屬性必須位于您想要設(shè)置樣式的元素上,如果它位于父級或祖先級,則它將不起作用。請注意,我提出的部分語言標記匹配示例已經(jīng)可以通過:lang()偽類完成。
換句話說,除了lang=“en”之外,:lang(en)還將匹配lang=“en US”、lang=“en GB”等等。當我能想出更好的例子時,我會更新這些例子。同時,使用:lang()偽類。
普通類或ID如何?
是的。可以使用普通類或ID。盡管你不會再利用你元素上已有的便利。(再一次,我的假設(shè)是lang屬性被正確且負責任地應(yīng)用)但當然,請繼續(xù),并為應(yīng)用特定語言相關(guān)樣式的元素提供類名。如果你真的想,沒有人會阻止你。
CSS屬性
好的,選擇器被覆蓋了。讓我們來談?wù)勎覀兿M麘?yīng)用于與這些選擇器匹配的元素的樣式。
Writing mode
writing-mode的默認值為horizontal-tb。完全合乎邏輯,因為網(wǎng)絡(luò)誕生于歐洲核子研究中心,那里的官方語言是英語和法語。此外,我認為大多數(shù)網(wǎng)絡(luò)技術(shù)都是在說英語的國家首創(chuàng)的。
但人類的神奇給了我們3000多種文字,文字和書寫方向遠不止從上到下的水平方向這一種方式。
傳統(tǒng)的蒙古語從左到右垂直書寫,而日語、漢語和韓語等東亞語言在垂直書寫時從右到左。允許您這樣做的寫入模式屬性分別是垂直lr和垂直rl。

還有sideways-lr和sideways-rl的值,它們將符號側(cè)向旋轉(zhuǎn)。每個Unicode字符都有一個垂直方向?qū)傩裕嬷尸F(xiàn)引擎在默認情況下字形應(yīng)該如何定向。
我們可以通過text-orientation屬性來改變字符的朝向。當您使用垂直排版的東亞文本,并點綴以拉丁語為基礎(chǔ)的單詞或字符時,這通常會發(fā)揮作用。對于縮寫,您可以選擇使用text-combination-vertical將字母壓縮到一個字符空間中。

有些人可能想知道從右到左的語言,如阿拉伯語、希伯來語或波斯語(僅舉幾例),以及CSS是否也適用于這些腳本。簡而言之,CSS不應(yīng)該用于雙向樣式。W3C的指導(dǎo)如下:
因為方向性是文檔結(jié)構(gòu)的組成部分,所以應(yīng)該使用標記來設(shè)置文檔或信息塊的方向性,或者標識文本中僅使用Unicode雙向算法不足以實現(xiàn)所需方向性的位置。
這是因為通過CSS應(yīng)用的樣式有可能被關(guān)閉,被覆蓋,無法識別,或者在不同的上下文中被更改/替換。相反,建議使用dir屬性來設(shè)置顯示文本的基本方向。
我強烈建議參考HTML中的結(jié)構(gòu)化標記和從右到左的文本[7],CSS vs.標記的bidi支持[8],內(nèi)聯(lián)標記和HTML中的雙向文本[9],以獲得更詳細的解釋和實現(xiàn)細節(jié)。
邏輯屬性
網(wǎng)頁上的所有東西都是一個盒子,CSS總是使用頂部、底部、左側(cè)和右側(cè)的物理方向來指示我們的目標盒子的哪一邊。但是,當writing-mode不是默認的從上到下的水平方向時,這些值往往會令人困惑。
因為規(guī)范仍然處于草案狀態(tài),所以語法可能會繼續(xù)更改。即使現(xiàn)在,當前的瀏覽器實現(xiàn)與規(guī)范中的不同,所以一定要用MDN: CSS邏輯屬性和值[10]對最新的語法進行雙重檢查。
更新:David Baron指出,我使用的是規(guī)范的前一個版本中的舊語法,而在瀏覽器中實現(xiàn)的語法實際上是編輯草案中的語法。表已相應(yīng)更新。
用于定位的盒子的物理邊和邏輯邊的書寫方向及其對應(yīng)值的矩陣如下(該表從寫作時的規(guī)范中移除):

容器的邏輯頂部使用inset-block-start,而容器的邏輯底部使用inset-block-end。容器的邏輯左側(cè)使用inset-inline-start,而容器的邏輯右側(cè)使用inset-inline-end。
也有相應(yīng)的邊界、邊距和填充的映射,它們是:
top to block-start right to inline-end bottom to block-end left to inline-start
<h1>A?comparison?of?physical?and?logical?directions?for?bordersh1>
<p>
??Given?the?requirement?is?to?have?a?box?with?a?run?of?text?within?it?with?the
??following?characteristics:
p>
<ol>
??<li>
????The?border?colour?at?the?top?edge?<strong>of?the?run?of?textstrong>?should
????be?red.
??li>
??<li>
????The?border?colour?at?the?right?edge
????<strong>of?the?run?of?textstrong>?should?be?green.
??li>
??<li>
????The?border?colour?at?the?bottom?edge
????<strong>of?the?run?of?textstrong>?should?be?blue.
??li>
??<li>
????The?border?colour?at?the?left?edge
????<strong>of?the?run?of?textstrong>?should?be?yellow.
??li>
ol>
<p>
??Using?physical?directions?requires?a?modification?every?time?the?writing
??direction?changes,?whereas?using?logical?properties?allows?the?same?properties
??and?values?for?all?six?use?cases.
p>
<hr?/>
<section>
??<h1>Physical?directionsh1>
??<div?class="phy-boxes">
????<article>
??????<div?class="phy-box1">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-top-color:?tomato;
border-right-color:?limegreen;
border-bottom-color:?dodgerblue;
border-left-color:?gold;code>pre>
????article>
????<article>
??????<div?class="phy-box2"?dir="rtl">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-top-color:?tomato;
border-left-color:?limegreen;
border-bottom-color:?dodgerblue;
border-right-color:?gold;code>pre>
????article>
????<article>
??????<div?class="vlr?phy-box3">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-left-color:?tomato;
border-bottom-color:?limegreen;
border-right-color:?dodgerblue;
border-top-color:?gold;code>pre>
????article>
????<article>
??????<div?class="vlr?phy-box4"?dir="rtl">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-left-color:?tomato;
border-top-color:?limegreen;
border-right-color:?dodgerblue;
border-bottom-color:?gold;code>pre>
????article>
????<article>
??????<div?class="vrl?phy-box5">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-right-color:?tomato;
border-bottom-color:?limegreen;
border-left-color:?dodgerblue;
border-top-color:?gold;code>pre>
????article>
????<article>
??????<div?class="vrl?phy-box6"?dir="rtl">
????????<p>This?is?a?sentence.p>
??????div>
??????<pre><code>border-right-color:?tomato;
border-top-color:?limegreen;
border-left-color:?dodgerblue;
border-bottom-color:?gold;code>pre>
????article>
??div>
section>
<hr?/>
<section>
??<h1>Logical?directionsh1>
??<div?class="log-boxes">
????<div?class="log-box">
??????<p>This?is?a?sentence.p>
????div>
????<div?class="log-box"?dir="rtl">
??????<p>This?is?a?sentence.p>
????div>
????<div?class="vlr?log-box">
??????<p>This?is?a?sentence.p>
????div>
????<div?class="vlr?log-box"?dir="rtl">
??????<p>This?is?a?sentence.p>
????div>
????<div?class="vrl?log-box">
??????<p>This?is?a?sentence.p>
????div>
????<div?class="vrl?log-box"?dir="rtl">
??????<p>This?is?a?sentence.p>
????div>
??div>
??<pre><code>border-block-start-color:?tomato;
border-inline-end-color:?limegreen;
border-block-end-color:?dodgerblue;
border-inline-start-color:?gold;code>pre>
section>
[class$="boxes"]?{
??display:?flex;
??flex-wrap:?wrap;
??justify-content:?space-around;
??gap:?1em;
}
article?{
??margin-bottom:?1em;
}
article?>?div,
[class$="box"]?{
??width:?200px;
??height:?200px;
??border:?1em?solid;
??position:?relative;
??margin:?1em;
}
.phy-box1?{
??border-top-color:?tomato;
??border-right-color:?limegreen;
??border-bottom-color:?dodgerblue;
??border-left-color:?gold;
}
.phy-box2?{
??border-top-color:?tomato;
??border-left-color:?limegreen;
??border-bottom-color:?dodgerblue;
??border-right-color:?gold;
}
.phy-box3?{
??border-left-color:?tomato;
??border-bottom-color:?limegreen;
??border-right-color:?dodgerblue;
??border-top-color:?gold;
}
.phy-box4?{
??border-left-color:?tomato;
??border-top-color:?limegreen;
??border-right-color:?dodgerblue;
??border-bottom-color:?gold;
}
.phy-box5?{
??border-right-color:?tomato;
??border-bottom-color:?limegreen;
??border-left-color:?dodgerblue;
??border-top-color:?gold;
}
.phy-box6?{
??border-right-color:?tomato;
??border-top-color:?limegreen;
??border-left-color:?dodgerblue;
??border-bottom-color:?gold;
}
.log-box?{
??border-block-start-color:?tomato;
??border-inline-end-color:?limegreen;
??border-block-end-color:?dodgerblue;
??border-inline-start-color:?gold;
}
.vlr?{
??writing-mode:?vertical-lr;
}
.vrl?{
??writing-mode:?vertical-rl;
}
pre?{
??background:?#2d2d2d;
??padding:?1em;
?margin:?.5em?0;
?overflow:?auto;
??color:?#ccc;
??border-radius:?4px;
??width:?max-content;
??margin:?auto;
}
邊框的物理和邏輯方向的比較
給定的要求是有一個框內(nèi)的文本運行與以下特征:
文本運行的頂部邊緣的邊框顏色應(yīng)為紅色。 文本右邊緣的邊框顏色應(yīng)為綠色。 文本運行的底部邊緣的邊框顏色應(yīng)為藍色。 文本的左邊緣的邊框顏色應(yīng)為黃色。 使用物理方向需要在每次寫入方向改變時進行修改,而使用邏輯屬性則允許所有六個用例具有相同的屬性和值。
物理方向

邏輯方向
大小的映射如下:寬度到inline-size,高度到block-size。
列表和計數(shù)器
數(shù)字系統(tǒng)是用來表示數(shù)字的書寫系統(tǒng),即使最常用的數(shù)字系統(tǒng)是印度-阿拉伯數(shù)字系統(tǒng)(0,1,2,3等等),CSS也允許我們用其他數(shù)字系統(tǒng)顯示有序列表。
預(yù)定義的計數(shù)器樣式可以與list-style-type屬性一起使用,該屬性涵蓋了從阿法爾語到烏爾都語的174個數(shù)字系統(tǒng)。你可以在MDN[11]上查看完整的列表。
如果你對CSS計數(shù)器感興趣,我在去年的某個時候?qū)懥?span style="font-weight: bold;color: #35b378;">一篇關(guān)于它們的文章[12],在文中我探索了在傳統(tǒng)中文環(huán)境中使用的“天干”和“地支”數(shù)字系統(tǒng)(以及CSS中一個非常流行的實現(xiàn),為什么不呢?)
text-decoration
如前所述,東亞語言沒有斜體的概念。相反,我們有強調(diào)點。它們可以放在字符的上方或下方,以強調(diào)文本,加強語氣或避免歧義。
漢字橫寫時,這些點放在漢字的下方,豎寫時,這些點放在漢字的右側(cè)。

另一方面,日語在水平書寫模式下,在字符上方放置強調(diào)點。為了使CSS屬性更加通用,在CSS text-decoration 模塊第3級[13]中引入了text-emphasis-style、text-emphasis-position和text-emphasis-color。

除了點之外,你可以使用不同的符號,比如圓、三角形,甚至是單個字符作為字符串。位置和顏色也可以根據(jù)各自的屬性進行調(diào)整。

線條裝飾也包含在同一規(guī)范中,為開發(fā)者提供了對下劃線和上劃線的更精細的控制(在規(guī)范的第4級)。但是,這對于那些經(jīng)常溢出基線的升序或降序腳本尤其有用。
CSS text-decoration 模塊第4級[14]涵蓋了text-decoration-skip,該模塊控制當覆蓋線和下劃線跨越字形時如何繪制覆蓋線和下劃線。同樣,對于像英語這樣的語言來說,這種情況發(fā)生的頻率較低,但對像緬甸語這樣的腳本的美學影響很大。
字體變化
訪問OpenType特性有兩類CSS屬性,高級屬性和低級屬性。本規(guī)范建議盡可能使用高級屬性。這主要取決于瀏覽器支持。
例如,東亞字體變體允許控制具有變體的字符的字形形式,例如簡體中文字形和繁體中文字形。這是同一個字符,但它們可以寫得不同。


還有字體變體連字,它為連字和上下文形式提供了許多預(yù)定義的選項,如任意連字、歷史連字或上下文連字。
低級屬性是通過字體功能設(shè)置訪問的,您可以使用4個字母的OpenType標記來切換所需的功能(這取決于您的字體是否有這些功能,但假設(shè)有)。
共有141個功能標簽,從可選分數(shù)到對正替換,從Ruby符號形式到斜線零。這些CSS屬性與字體文件本身的功能密切相關(guān),因此外部依賴性取決于字體的選擇。
結(jié)束
這篇文章太長了,所以我將在第二部分更詳細地介紹我們?nèi)绾问褂弥疤岬降倪x擇器來構(gòu)建布局,以確保即使語言發(fā)生變化,我們的布局也能保持健壯。像Flexbox和Grid這樣的現(xiàn)代布局屬性非常適合這樣的用例。
關(guān)于CSS,我發(fā)現(xiàn)最有趣的事情之一是,我們?nèi)绾我圆煌姆绞綄⑺鼈兘M合起來,以實現(xiàn)無數(shù)的結(jié)果,并且有超過500個CSS屬性存在,這是很多可能性。我并不是說什么都可以,因為通常情況下,有很多方法可以達到相同的結(jié)果,有些方法比其他方法更合適。
然而,我們需要通過理解每種技術(shù)背后的機制,其優(yōu)缺點,并意識到為什么我們會選擇某種方式去做事情,從而做出最適合自己的決定。
我仍然相信,30多年過去了,網(wǎng)絡(luò)仍然是一個信息媒介,內(nèi)容是關(guān)鍵。因此,無論使用何種語言或腳本,都應(yīng)該優(yōu)化內(nèi)容的呈現(xiàn)。我很高興CSS的不斷發(fā)展為開發(fā)人員提供了實現(xiàn)這一目標的方法。
總之,敬請期待第二部分。
參考資料
ISO 639-1 codes: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
[2]Tobias Bengfort : http://tobib.spline.de/xi/
[3]BCP 47: https://www.w3.org/International/questions/qa-html-language-declarations
[4]Language tags in HTML and XML: https://www.w3.org/International/articles/language-tags/
[5]Codrops CSS reference entry: https://tympanus.net/codrops/css_reference/attribute-selectors/
[6]Amelia Bellamy Royds: https://twitter.com/AmeliasBrain/status/1253053272585146368
[7]HTML中的結(jié)構(gòu)化標記和從右到左的文本: https://www.w3.org/International/questions/qa-html-dir
[8]CSS vs.標記的bidi支持: https://www.w3.org/International/questions/qa-bidi-css-markup
[9]Inline markup and bidirectional text in HTML: https://www.w3.org/International/articles/inline-bidi-markup/
[10]MDN: CSS Logical Properties and Values: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties
[11]list-style-type: https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type
[12]CSS計數(shù)器的奇妙世界: https://chenhuijing.com/blog/the-wondrous-world-of-css-counters
[13]CSS Text Decoration Module Level 3: https://drafts.csswg.org/css-text-decor-3/#emphasis-marks
[14]CSS Text Decoration Module Level 4: https://drafts.csswg.org/css-text-decor-4/#text-decoration-skipping
Node 社群
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學習、共建。下方加 考拉 好友回復(fù)「Node」即可。
如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個小忙:
1. 點個「在看」,讓更多人也能看到這篇文章 2. 訂閱官方博客?www.inode.club?讓我們一起成長 點贊和在看就是最大的支持??
