HTTP報文可以承載任何語言表示的內容的。因爲對HTTP來說,實體主體真實二進制信息的容器而已。
在HTTP中爲了支持國際性,服務器返回內容的同時需要告知客戶端文檔是用的什麼字母表和語言等信息,這樣客戶端才能正確的解析出信息並顯示字符。服務器可以通過Content-Type
中的charset
參數和Content-Language
首部告知客戶端字母表和語言信息。
同時,客戶端並不是所有的字母表和語言都能進行處理,所以客戶端在發起請求的時候,也可以通過發送Accept-Charset
和Accept-Language
首部,告知服務端自己所能處理的編碼類型和語言。這兩個首部也支持優先級,可以通過q
參數設置優先級。
所以,HTTP中的國際化,也就是本篇要介紹的內容主要涉及到字符集編碼(character set encoding)和語言標記(language tag)
1 字符集和HTTP
1.1 字符集的概念
所謂字符集起始就是把字符轉爲二進制碼的編碼。HTTP 字符集的值說明如何把實體內容的二進制碼轉換爲特定字母表中的字符。每個字符集標記都命名了一種把二進制碼轉換爲字符的算法(反之亦然)。字符集標記在由 IANA 維護MIME字符集註冊機構進行了標準化。一般通過Content-Type
中的charset
參數進行指定。如下:
Content-Type: text/html; charset=utf-8
其中utf-8就是一種編碼算法,後面會有相關介紹。
1.2 字符集與編碼
上面說了,字符集和編碼目的就是爲了將二進制信息同我們的字符進行轉換。轉換的具體流程一般經過兩個步驟完成(這裏以將二進制信息轉爲字符過程爲例):
- 首先需要將二進制信息轉爲字符代碼(某個字符集中的某個字符的特定編號);
- 得到這個代碼後,我們再跟進這個代碼從字符集中找到該代碼對應的字符。
經過上面兩個步驟,就能獲得正確的字符了。而我們上面兩個步驟都依賴於正確的charset
值。如果該值不正確,同樣一段二進制信息可能就會得出錯誤的字符代碼,同樣第二步中根據charset
的值不同,字符代碼和字符的對應關係也就不同。所以平時的亂碼現象,有時候就是因爲charset
的值錯誤而引起。
charset
的值使用的標準化的MIME charset標記。比如:utf-8, iso-8895-1, gbk等。這裏不對每種編碼做敘述,後續會對一些常見的編碼方式做介紹。另外一點,該值一般來說是大小寫不敏感的,在處理的時候需要注意這點。
1.3 客戶端對字符集的處理
正常情況下,服務器會通過在Content-Type
首部中使用charset
參數告知客戶端MIME字符集。但是如果服務器沒有顯示的返回信息,我們客戶端可能就需要儘可能自己從文檔內容判斷出字符集。一般在HTML文檔中,會通過<META HTTP-EQUIV="Content-Type">
描述所用字符集信息。如下:
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gbk">
<META LANG="jp">
<TITLE>A Japanese Document</TITLE>
</HEAD>
<BODY>
...
該部分類容說明該html文檔使用的gbk
編碼。
全世界範圍類字符集編碼非常多,服務器並不一定都是返回的客戶端能正確處理的字符集。所以爲了避免服務器端返回一些客戶端無法處理的字符集,客戶端在發送請求的時候,儘量通過Accept-Charset
首部告知服務器,我們需要或者想要什麼字符集,可以處理哪些字符集。
2 字符編碼簡介
前面我們介紹了服務器和客戶端之間怎麼協商處理字符集。本節是簡單對字符編碼的過程的一個簡單入門介紹。
2.1 字符集術語
這裏我們將一組把一系列 8 位字節轉換爲一系列字符的規則的集合稱爲字符集。其包括編碼方案和編碼後的字符集。在介紹字符集的時候會用到一下一些術語:
- 字符: 字符是指字母、數字、標點、表意文字(比如漢語)、符號,或其他文本形式的 書寫“原子”。
- 字形:字形是字符的一種表現形式,同一個字符可以有多重字形,比如同樣一個漢字,宋體和楷體表現出的字形是有區別的。
- 編碼後的字符:分配給字符的唯一數字編號
- 代碼空間:計劃用於字符代碼值的整數範圍
- 代碼寬度:每個字符代碼所用的位數
- 字符庫:特定的工作字符集
- 編碼後的字符集:組成字符庫的已經編碼的字符集,併爲每個字符分配代碼空間中的一個代碼。
- 字符編碼方案:把數字化的字符代碼編碼爲二進制碼的算法。
2.2 字符編碼方案
上面我們介紹了一些相關的術語,理解到其意思後。我們來看下一些常見的字符編碼方案。目前字符編碼方案主要有以下3種:
- 固定寬度
固定寬度方式的編碼用固定數量的比特表示每個編碼後的字符。這種方式處理速度較快,但是會浪費一些空間 - 可變寬度(無模態)
顧名思義,可變寬度就是根據字符代碼所需要的比特位,採用不同的寬度的比特進行存儲。這樣可以節約資源,而且還能根據需要利用多個字節來表示不同的文字,比如中午就需要兩個字節 - 可變寬度(有模態)
有模態的編碼使用特殊的“轉義”模式在不同的模態之間切換(這個我也沒有太理解其含義)。
常見編碼示例:
- 8位固定寬度
位固定寬度恆等編碼把每個字符代碼編碼爲相應的 8 位二進制值。它只能支持有 256 個字符代碼範圍的字符集。iso-8859 字符集家族系列使用的就是 8 位恆等編碼。 - UTF-8
這個應該是目前比較流行的一種通用編碼了,能夠處理很多語言。是一種無模態的可變寬度編碼。第一字節的高位表示編碼後的字符所用的字節數,所需的每個後續字節都含有 6 位的代碼 值,其編碼方式可以和ASCII編碼兼容。如下表:
字符代碼的二進制位 | 字節 1 | 字節 2 | 字節 3 | 字節 4 | 字節 5 | 字節 6 |
---|---|---|---|---|---|---|
0-7 | 0ccccccc | - | - | - | - | - |
8-11 | 110ccccc | 10cccccc | - | - | - | - |
12-16 | 1110cccc | 10cccccc | 10cccccc | - | - | - |
17-21 | 11110ccc | 10cccccc | 10cccccc | 10cccccc | - | - |
22-26 | 111110cc | 10cccccc | 10cccccc | 10cccccc | 10cccccc | - |
27-31 | 1111110c | 10cccccc | 10cccccc | 10cccccc | 10cccccc | 10cccccc |
3 語言標記與HTTP
語言標記是命名口語的標準化字符串短語。同時,我們需要對這個標記的字符串進行標準化,否者每個人都有自己的命名習慣,字符串格式都不一樣,就無法從標記中提取出語言信息。
3.1 Content-Language和Accept-Language首部
實體的 Content-Language 首部字段描述實體的目標受衆語言,其不限於文本文檔。音頻片段、電影以及應用程序都有可能是面向特定語言受衆的。任何面向特定語言受衆的媒體類型都可以有 Content-Language 首部。同時我們也可以在該首部指定多個語言,如:Content-Language: en, fr
。
相對應的,客戶端也可以通過Accept-Language告知服務端我們能夠接受的語言種類。其優先順序從左至右
3.2 語言標記的類型
在 RFC 3066,“Tags for the Identification of Languages”(標識語言的標記)中記錄了語言標記的標準化語法。可以用語言標記來表示:
- 一般的語言分類(比如 es 代表西班牙語);
- 特定國家的語言(比如 en-GB 代表英國英語);
- 語言的方言(比如 no-bok 指挪威的書面語);
- 地區性的語言(比如 sgn-US-MA 代表美國馬撒葡萄園島上的手語);
- 標準化的非變種語言(比如 i-navajo);
- 非標準的語言(比如 x-snowboarder-slang)。
標記一般由一個或多個部分組成,中間由"-"分隔,示例如下:
其中:
- 第一個標記是稱爲主子標記,其值是標準化的
- 第二個子標記是可選,遵循它自己的命名規則
- 其他尾隨的子標記是未註冊的
第一子標記和第二子標記都是有專門的文檔及相關組織專門維護和命名的,後面會介紹一些。其他的那些沒有專門維護的,一般需要在IANA進行註冊。這裏所有的標記都是不區分大小寫的。我們在解析的時候應該注意這點。
3.2.1 第一個子標記
第一個子標記通常是標準化的語言記號,選自 ISO 639 中的語言標準集合。不過也可以用字母 i 來標識在 IANA 中註冊的名字,或用 x 表示私有的或者擴展的名字。其規則如下:
- 如果只有2個字符,那就是來自 ISO 639 和 639-1 標準的語言代碼,如ar, en等
- 有3個字符,那就是來自 ISO 639-210 標準及其擴展的語言代碼,如ara,eng等
- 字母i,該語言標記是在 IANA 顯式註冊的
- 字母 x,該語言標記是私有的、非標準的,或擴展的子標記。
關於ISO639,ISO639-2等文檔這裏就不詳細介紹了,有興趣的自己搜索來看看就清楚了。
3.2.2 第二個子標記
第二個子標記通常是標準化的國家記號,選自 ISO 3166 中的國家代碼和地區標準集合。不過也可以是在 IANA 註冊過的其他字符串,其規則如下:
- 2 個字符,那就是 ISO 316611 中定義的國家 / 地區;如:CN,FR
- 3~8 個字符,可能是在 IANA 中註冊的值;
- 單個字符,這是非法的情況。
4 國際化URI
本小節感覺瞭解的內容居多,所以這裏就沒有詳細記錄了。一般知道下面幾點即可:
- URI允許使用ASCII字符集的子集
- URI字符被分爲轉義、保留和未保留三種,具體如下表所示:
字符類別 | 字符列表 |
---|---|
未保留 | [A-Za-z0-9] |
保留 | “;” |
轉義 | “%” <HEX> <HEX> |