
記得去年有個朋友跟我吐槽,說他花了重金做了個多語言網站,結果上線后日本客戶打開頁面全是亂碼,俄國客戶的用戶名保存不了,阿拉伯語直接顯示成從右往左的「鬼畫符」。他問我這到底是怎么回事。我跟他說,問題很可能出在一個很多人都會忽視的地方——字符集。
字符集這個話題,說起來可能有點枯燥,但它確實是網站本地化最底層、也最關鍵的技術基礎。今天我就用最直白的話,跟大家聊聊網站本地化服務對多語言字符集到底有什么要求。
先做個簡單的比喻。計算機其實只認識0和1這兩個數字,不管是我們看到的文字、表情符號還是特殊符號,最終都要轉換成二進制才能存儲和傳輸。字符集就是一套規則,告訴計算機「某串二進制數字代表什么字符」。
早期的計算機設計主要是給美國人用的,所以他們只考慮了英文大小寫字母、數字和一些常用符號,這就是我們說的ASCII字符集。這套編碼只用了1個字節(8位),可以表示128個字符,對英語來說綽綽有余,但放到全世界的語言環境里,就完全不夠看了。
歐洲人覺得ASCII不夠用,他們搞了個ISO-8859系列,用1個字節的后128位來添加法語、德語、西班牙語等語言的特殊字符。但問題來了,不同國家用的后128位不一樣,一份文檔換臺電腦可能就亂套了。更麻煩的是,亞洲國家的文字更多——漢字、日文假名、韓文音節加起來有幾萬個字符,1個字節根本裝不下。
這就是為什么后來出現了Unicode。Unicode的目標很宏大:收錄全世界所有語言的字符,讓任何語言都能在同一套系統里共存。目前Unicode已經收錄了超過14萬個字符,涵蓋了人類歷史上幾乎所有主要文字系統。對做網站本地化的團隊來說,理解和正確使用Unicode,是最基本也是最重要的一課。

光有字符集定義還不夠,還需要具體的編碼方式來實現存儲和傳輸。Unicode有幾種常見的編碼方案,其中UTF-8已經成為了互聯網的絕對標準。根據w3techs的統計數據,目前全球超過97%的網站都使用UTF-8編碼。為什么UTF-8這么流行?
第一個原因是兼容性太好了。UTF-8完全兼容ASCII,這意味著那些老舊的系統和服務仍然可以正常處理純英文內容,不會出現任何問題。你在代碼里寫注釋用英文,變量名用英文,在UTF-8環境下絕對不會亂碼。
第二個原因是可變長度設計很聰明。UTF-8用1到4個字節來表示一個字符。英文用1個字節,和ASCII一樣高效;歐洲語言用2個字節;中文、日文、韓文這些CJK字符用3個字節;而一些特殊符號比如emoji會用4個字節。這種設計在保證兼容性的同時,也兼顧了存儲效率。
第三個原因是Unicode生態系統的支持。現在幾乎所有的編程語言、數據庫、操作系統、瀏覽器都原生支持UTF-8。你不用費勁巴力地去做各種轉碼適配,天然就能處理多語言內容。
我見過不少本地化項目,因為早期圖省事用了其他編碼(比如GBK用于中文,或者Shift-JIS用于日文),結果后來要擴展支持其他語言時,發現整個技術棧都要推翻重做。所以如果你的網站未來有可能面向多語言用戶,從一開始就選UTF-8絕對是明智的選擇。
說了這么多背景知識,現在我們來具體聊聊,網站本地化服務在實際操作中,對字符集到底有哪些要求。下面這張表總結了幾個最關鍵的維度:
| 要求維度 | 具體要求 | 常見問題 |
| 聲明一致性 | HTML meta標簽、HTTP響應頭、數據庫charset設置必須完全一致 | HTML聲明UTF-8但數據庫用Latin1,導致亂碼 |
| 輸入處理 | 表單提交、URL參數、API接口都要正確識別和傳遞Unicode字符 | 用戶輸入俄文姓名后變成問號 |
| 存儲規范 | 數據庫表、字段的字符集設置要和應用程序邏輯匹配 | 中文內容存入數據庫后部分字符丟失 |
| 輸出顯示 | 瀏覽器渲染時必須正確識別頁面編碼 | 頁面頭部缺失charset聲明導致瀏覽器誤判 |
| 特殊符號支持 | 要能處理連字符、貨幣符號、數學符號等特殊字符 | 歐元符號€顯示成亂碼 |
這是最容易出問題的地方。很多程序員會覺得,「我在HTML里寫了charset=UTF-8,就應該沒問題了吧」。其實不是這樣的。一個網頁的編碼信息可能來自好幾個地方:HTTP響應頭里的Content-Type字段、HTML文件開頭的meta標簽、可能還有XML聲明或者BOM(字節順序標記)。
這些信息必須完全一致,瀏覽器才能正確渲染頁面。我見過最奇葩的情況是,HTTP頭聲明是ISO-8859-1,HTML meta標簽聲明是UTF-8,數據庫存的是GBK,三方數據源返回的是Big5。一個頁面上同時存在四種編碼,不亂套才怪。
康茂峰在服務客戶的時候,第一件事就是幫他們梳理整個技術鏈路,確保從數據庫到后端代碼到前端頁面,每個環節的字符集設置都統一成UTF-8。這看似簡單,但實際操作中經常能發現一些隱藏很深的坑。
網站本地化后,用戶可能用各種語言輸入內容。姓名、地址、評論、搜索關鍵詞……這些都需要正確處理。
這里有個常見的陷阱。很多網站的表單沒有指定accept-charset,或者服務器沒有正確配置,在某些瀏覽器環境下,用戶輸入的非英文內容提交到服務器時可能就被損壞了。解決這個問題,需要在HTML表單里明確設置accept-charset="UTF-8",同時確保服務器端的接收邏輯沒有問題。
URL參數的處理也很關鍵。如果你允許用戶使用多語言關鍵詞進行搜索,那么URL里的中文、日文、阿拉伯文必須經過正確的URL編碼(percent-encoding)。比如「北京」在URL里會變成%E5%8C%97%E4%BA%AC,后端服務器要能正確解碼還原。如果這一步沒做好,用戶的搜索請求可能就變成了一堆亂碼,后端根本理解不了用戶想要什么。
數據庫是存儲用戶內容和網站數據的核心,字符集配置往往是最復雜也最容易出錯的一環。
以MySQL為例,你需要同時考慮三個層面的設置:服務器級別(character_set_server)、數據庫級別(character_set_database)、表級別(character_set_table)、字段級別(character_set_column)。更要命的是,排序規則(collation)也跟著湊熱鬧,不同的排序規則會影響排序和比較的結果。
我建議的做法是,從一開始就統一把everything設成utf8mb4。為什么要用utf8mb4而不是utf8?因為早期的UTF-8編碼只支持最多3個字節,這意味著它能表示的字符范圍有限,一些特殊符號比如emoji就不支持。utf8mb4是真正的UTF-8,支持4個字節,能涵蓋Unicode里的所有字符。
康茂峰在幫客戶做本地化項目審計時,經常發現數據庫層面的字符集不一致——主庫用utf8mb4但從庫用utf8,或者某個歷史遺留的表還是latin1_swedish_ci。這些隱藏的不一致可能在日常運營中不會暴露,但一旦遇到特定字符或者特定操作,就會引發各種詭異的問題。
說完通用要求,我們來聊聊幾種常見語言在字符處理上的特殊挑戰。
中文、日文、韓文,也就是我們常說的CJK語言,是字符集處理的重點和難點。這三種語言加在一起的字符量非常大,而且有很多相似的字形,但在Unicode里被當作不同的字符處理。
這里要提一下「統一漢字」的概念。Unicode收錄了很多在不同語言里寫法相同或相似的漢字,但為了保持兼容性,原則上每個字符只有一個碼點。比如「日本」的「日」,在中文、日文、韓文里字形幾乎一樣,Unicode只分配了一個碼點。但也有一些漢字在不同的語言傳統里有不同的寫法,這些就會分配不同的碼點。
這意味著,如果你要做中日韓多語言網站,不能簡單地用同一個字庫去顯示兩種語言的文字。有時光憑字符本身無法判斷它屬于哪種語言,需要結合上下文或者語言標記來處理。
阿拉伯語和希伯來語的書寫方向是從右往左,這對網頁布局和字符處理都是挑戰。
Unicode有一個很聰明的設計叫做Bidi算法(雙向文本算法)。它能自動處理一段文本里同時存在從左往右(LTR)和從右往左(RTL)文字的情況。比如一個阿拉伯語網頁里嵌入一段英文,瀏覽器會自動判斷各部分的顯示方向。
但Bidi算法不是萬能的。在某些復雜場景下,比如混合了數字、標點、阿拉伯語、英文的句子,算法可能會判斷錯誤。這時候就需要HTML的dir屬性和bdo元素來手動指定文本方向。
還有一點值得注意的是,阿拉伯語和希伯來語的字符會根據上下文變形。比如阿拉伯語的字母「?」,單獨寫是一個樣子,在詞首、詞中、詞尾的寫法都不同。這種變形是由字體渲染引擎處理的,前提是字符集和字體都配置正確。如果你的系統不支持OpenType的高級排版特性,顯示效果可能會比較粗糙。
泰語看起來圈圈很多,但它其實是簡單的線性文字,沒有字符變形的問題。真正的挑戰在于,一些南亞和東南亞語言使用復雜的腳本系統,字符的位置關系非常復雜。
比如印地語使用的天城文,一個輔音字母可能自帶一個繼承的a元音,而在這個元音上面還會疊加其他元音符號。一個完整的音節可能由三四個字符疊加組成,這對字體渲染引擎的要求很高。如果你的服務器和瀏覽器環境不支持復雜的腳本排版,這些語言可能顯示得支離破碎。
所以在做這些語言的本地化時,除了字符集要正確配置,字體選擇和CSS設置也很關鍵。你需要確保目標語言的字符在你的服務器上有對應的支持字體,否則用戶看到的就是一個個空框或者豆腐塊。
現在emoji已經是互聯網交流的標配了,但在字符處理上還是有一些坑。
首先,emoji的字符集歸屬經過了幾次變化。早期的emoji是日本運營商的私有標準,后來才被納入Unicode。如果你的系統比較老,可能無法正確識別新一代的emoji字符。
其次,emoji有「皮膚變體」和「性別變體」。比如