Unity I18N 小探
前略
軟體要分發給使用不同語言和處於不同地區的人員,需要從多個方面處理從而使這些人能以自己文化背景下熟知的方式來使用。這包括了最基本的文字的翻譯,也有一些通用習俗和約定的問題(如不同國家和地區的日期時間的表示等)。而本地化(L10N)和國際化(I18N)就是專門來解決這些問題的。
要很好的解決這一問題,往往需要多方面的人員參與,既有軟體設計者,有程式開發人員,也需要翻譯人員甚至翻譯測試人員,而要把這些人組織起來完成工作,需要的不是隻面向一兩方的API和程式碼,良好的本地化團隊往往需要面面俱到。
傳統的解決方案
最知名的解決這些問題的一套系統,是GNU Gettext。這是一套上世紀九十年提出的用以解決自由軟體的國際化和本地化問題的工具套件。在gettext裡,制定了翻譯文字的組織方式(po/pot),提供了從程式原始碼提取字串並生成翻譯模板的工具(xgettext)。在這些之外,gettext和其社群也發展出了面向翻譯人員的翻譯內容管理系統(Weblate),以及有用於完成翻譯工作的專門軟體(Poedit)。Gettext也提供DIff/Merge工具來解決翻譯內容的漸進式更新的需求。
遊戲具有其特殊性
遊戲做為最特殊的軟體之一,對於本地化和國際化也有著屬於它的苛刻要求:
•遊戲往往會引入其他型別的需要多語言處理的資源而非只是文字,比如圖片,語音,乃至口型檔案。
•因為上一條的緣故,導致遊戲本地化團隊往往會引入更多不同工種的人員,從而增加了人員管理和協調的負擔。
傳統方案在遊戲開發上的擱淺
GNU Gettext天生不適合遊戲開發中的多語言問題,它只處理文字資源,原版的gettext甚至只能從原始碼中收集多語言資料,這顯然只適合普通軟體的需求,卻很難滿足遊戲開發的需要。
對於遊戲而言,只有處於UI介面裡的資料,會比較切近Gettext的需求場景,然而在很多成熟引擎下,這些資料也往往位於特定的UI宣告檔案內(在Unity裡這些資料處於scene/prefab檔案中)而不是原始碼中。至於佔遊戲內比重更大的其他需要多語言的資料,更是會分散在各種特定格式的資料之中。在Unity下,關卡資料可能存放在prefab中,角色/物品這類的設定性的文件資料可能處於ScriptableObject,甚至這些資料都儲存在別的序列化檔案內(諸如最常見的csv/xls或者xml/json,再或者自定義的二進位制資料檔案)。
一點牢騷
誠然,遊戲的多語言問題在如今看起來似乎並不算多麼麻煩的事情,即使是考慮多語言輸入的問題等,在遊戲程式設計精粹系列中,很早就有論證這些問題的文章[1]。而如今面臨的問題似乎同前輩們沒什麼兩樣。相比遊戲開發中的其他領域(尤其是圖形學),這個話題好像沒什麼可談的。即使我在文章開頭的前幾段論述了這個問題多麼棘手,但貌似現成的解決方案存在著一大堆。而在之前,我在一個遊戲開發的群裡提到這個話題時,被人指出是在浪費時間,對方建議我應該多研究研究影象學之類的如何讓遊戲畫面看起來漂亮的技術或者去鑽研鑽研遊戲敘事來讓遊戲內容變得好玩。:-)
但事實真的如此了嗎?
從key-value表到I2 Localization
實現一個簡單的多語言系統是十分容易。一個最小可用的多語言系統大體需要如下的部分:
•字典表,記錄了所有需要多語言處理的字串,key往往是對應內容的英文,而value則是對應語言的翻譯內容。這些內容在檔案系統裡通常以json/csv/xml這樣的形式存在。
•一個形似Localization.Get(key,lang)的函式,用來在Runtime返回指定key和語言所對應的翻譯內容
•一套載入機制,用來在對應語言環境下載入對應的多語言檔案
•一套通知機制,使得系統能得知語言被切換過。這個系統往往是通過訂閱對應的事件來完成通知。
實現這些東西的程式碼花費不了幾個小時就能完成。然而仔細思考就會發現要拿這套系統來用的舒爽,就十分的困難了。
一些待解決的問題:
•字型
•缺乏對其他多媒體資源的支援
•少數語言對文字排版的需求
•翻譯人員對接
就字型問題而言,對於歐美不少語言,一個字型檔案往往很容易就能解決大部分語言的字型問題,但是一旦遇到中日韓,就會變的十分頭疼。所幸很多系統和引擎都提供字型的fallback機制,只要合理利用這套機制,使得字型問題也能輕鬆得以解決。再或者乾脆再弄個字型檔案的字典表,替換文字的同時也替換對應的字型。
其他媒體資源得支援問題,在別的本地化系統裡出場率遠遠低於遊戲。這是因為遊戲裡很容易產生那些需要多語言處理得多媒體資源。一個藝術字標題或者一個圖片按鈕,亦或是人物對話的配音。不過我們照貓畫虎,按著如何處理文字,也同樣來處理多媒體資源就行。這裡比較棘手得問題是,相比文字這種往往一個語言的所有多語言資源都塞到一個檔案內就能解決,多媒體資源在檔案系統方面就會變得十分複雜。而一旦這些問題和引擎的資源打包系統耦合在一起,要設計出一套好用又節省記憶體的方案並不簡單。
少數語言文字排版需求通常是指阿拉伯文字這種特有的從右往左排版的情況。相關資料十分豐富,我也不再贅述。不過要注意的時如果涉及到多語言混排,基本很難做對。如果需要相關資訊,W3C i18n小組下的內容對此會十分有幫助[2]。
和翻譯人員對接部分,目前來看,業界主要以通過翻譯外包的形式將本地化任務託管給其他公司。這些公司往往要求提供key-value形式的多語言資料檔案(如xml/csv/xls)給他們。如果你在第一步的時候就已經決定用這樣的方式來組織你的多語言文字,正好可以一勞永逸。但第一步選擇了別的方案可能就需要提供匯入匯出功能。
Unity下有現成的處理好這些問題的框架嗎?當然,小節標題後邊的部分說的就是它——I2 Localization,Unity資源商店裡頂頂有名的國際化外掛。
關於它的介紹我不打算多花筆墨,讀者可以直接閱讀它的文件來一窺究竟。I2 Localization基本上很好的解決了上邊提到的問題。而且他還有不少十分有用的特性。
如果你覺得你的問題已經解決了,那麼你可以停止閱讀趕緊去購買外掛/自己實現類似系統了。但是,假如你任然保佑困惑,或者好奇多語言系統還有那些問題和改進的地方,那麼我將在下邊兩個小節內盡力給你一個滿意的答案。
Rosetta-關於本地化問題的反思和實現
雖然上一節提到的I2 Localization十分優秀,但是依然有不少問題有待解決:
1.key-value形式的多語言系統天然違反遊戲開發的流程和直覺
2.繁雜而未經設計的翻譯文字組織檔案進一步導致了功能上的缺失和解決這一問題的困難
3.對於漸進式開發和需更新維護的遊戲缺乏合適的Merge機制
4.和翻譯人員對接的部分過於的粗暴反而埋下多語言問題的禍根
在key-value模式下,你需要為每一個多語言文字分配一個key,這個key往往是一個英文單詞或者短語。這對於通常的軟體來說十分夠用。本來大部分需要多語言處理的文字都是按鈕上的文字或者窗體的標題。但是對於遊戲來說,這類的資料佔比十分低,而更多的多語言文字是遊戲內各種物品技能裝備等的說明和關卡里的人物對白。這些文字全都篇幅不短。如果你的遊戲內有書籍系統,那麼更是意味著對應的是一篇短文。這時候為這些數量眾多的大段文字去分配一個個不會衝突的key是十分頭痛而又麻煩的。可能最後這些key變成了一個個奇怪的縮寫標記(比如一個l10e2s2這樣的神祕字串代表著關卡十中第二個事件上的第二段對話)。而開發人員在瀏覽遊戲工程檔案時,看著充滿神祕字串的介面,還要去回頭查詢對應的是什麼文字。
你可能會說,既然如此我乾脆用需要翻譯的文字本身來作為key好了。噔噔噔!恭喜你,想出了幾十年前那些自由軟體黑客們就已經構思並實現了的點子——GNU Gettext。GNU Gettext的一大特點就是用文字本身做為key。整個軟體選取一個開發語言,開發時所有的文字均按該語言撰寫。然後這些文字資料通過特定的標記(往往是通過Gettext的獲取多語言文字的函式),在軟體編寫完成後,通過特定的程式掃描整個軟體的程式碼,從中將這些被標記的靜態文字挨個收集起來。
除此之外,GNU Gettext所定義的用於組織多語言文字的特定檔案格式po/pot(po檔案用來組織翻譯文字,而pot檔案則是翻譯文字模板檔案),也解決了上邊的問題二。相比大部分情況下使用某種現成的序列化文字格式來組織多語言文字,po/pot檔案考慮了更多多語言工作中需要解決的問題,比如pot檔案裡支援爭對單個文字片段的多種形式的註釋:
1).#後緊接著空格符的註釋內容,是翻譯者新增的註釋;
2).#後緊接著.的註釋內容,是xgettext從原始碼中提取出的註釋內容(通過xgettext--add-comments選項);
3).#後緊接著:的註釋是待翻譯語句在原始碼中的位置資訊;
4).#後緊接著,的註釋是msgfmt程式專用的flag;
5).#後緊接著|的註釋是這條待翻譯語句之前的相關翻譯資訊;
而大部分使用通用序列化文字格式的本地化系統,基本不會去考慮這些情況。更何況這些部分還需要各種軟體和工具的支援,而繁雜的翻譯文字組織方式更是讓想支援這些也變得困難,除此之外,對於問題三,大部分使用通用的序列化文字格式的系統也幾乎不曾考慮,而po/pot檔案很早就開始考慮這些問題並提供支援。GNU Gettext作為一個存在幾十年的系統,期間積攢了數量眾多的處理這些問題的工具和軟體。
問題四要指出的是,目前的外包形式的解決辦法,遊戲開發商難以較為積極的參與到多語言工作中,其次對於遊戲來說往往存在著大量需要多語言處理的多媒體資源,這些資源通常還涉及到美術和配音人員。最後的結果就是要麼把這些也一同丟給本地化外包公司,要麼就組織兩套系統然後花費額外精力來維護兩個系統間的週轉。其次完全交給外包公司也讓遊戲本地化工作問題的反饋難以及時更進(實際上大部分遊戲根本不會提供多語言測試版或者僱傭多語言測試人員)。這一結果使得遊戲開發和多語言工作過分割裂,最後很容易產生各種翻譯問題並很難解決(比如被遊戲視訊UP主神奇陸夫人常常唸叨的Artifex公司作品的翻譯迫擊炮問題)。
我個人認為解決這一問題的辦法是遊戲開發公司應該把多語言工作的主動性掌握在自己手裡,通過自己部署翻譯管理系統,將多語言工作涉及到的人員全部聚集在一起。同時遊戲應該提供多語言測試工具並提供多語言測試版本,然後讓多語言測試人員(可以是公司招聘的人員,也可以是通過釋出測試版來讓對應語言的玩家充當)將多語言問題通過這個系統提交到翻譯內容管理系統上。在這一體制下,翻譯人員不再是外包給翻譯公司,由公司內部分配然後運作在其內部系統上,而是翻譯公司外派人員在遊戲開發公司的系統上來工作。這樣一來無論是監管還是進度追蹤抑或是翻譯問題的追蹤和處理都會變得簡單而透明。同時也可以將配音和美術人員也統一在這一框架下。而且如果是大公司的話,這裡邊的翻譯工作人員也可以是公司自己內部的員工。當然,這些構思只是出於一個技術人員在軟體開發角度的考量。可能涉及到商業和管理方面的問題並不一定會達成(比如翻譯公司拒絕外派人員而堅持使用自己的內部系統)。另外不少獨立遊戲公司的本地化工作都是通過Mod系統然後讓對應語言的玩家社群來完成。如果有這樣的工具來提供給玩家社群也是很不錯的。
從上邊討論可知,GNU Gettext的解決方法優於key-value方案,並且gettext還有配套的開源的翻譯內容管理系統(比如Weblate),這意味著問題四也可以方便的解決。
然而原本的GNU Gettext並不十分符合遊戲開發。比如遊戲的多語言資料基本上不會存在於原始碼中。其次遊戲存在眾多的需要多語言處理的多媒體資源,而那些配合gettext的TMS往往只支援文字內容的處理,前略裡已經完整的講過這個問題了。如此一來,直接把GNU Gettext搬過來只會水土不服。我們需要一個適用於Unity和遊戲開發的使用和GNU Gettext類似理念的全新的本地化框架。
簡單來說,我們需要:
1.設計良好的組織多語言資料的檔案格式
2.通過簡單標記就可以自動化收集多語言資源的工具
3.一個執行時的載入這些多語言資料檔案,並在恰當的時機講資源替換成對應語言的內容的系統
4.對翻譯人員提供符合他們工作習慣的工具來完成翻譯工作
這一想法最後誕生的結果就是Rosetta。
Rosetta採取了和GNU Gettext相同的組織翻譯文字的方式。這意味著我們可以在文字資源上充分利用GNU Gettext現成的工具鏈。除此之外還處理了多媒體資源的多語言問題。
而在問題二上Rosetta充分利用了C#的attribute和reference這兩個語言特性,通過i18nAttribute來標記需要多語言處理的資源,然後通過反射來蒐集這些資料並生成對應的多語言模板檔案。
對於問題四來說,Rosetta暫時只能依託於GNU Gettext現有的工具來解決。目前我正計劃在Rosetta 1.0.0版本的時候,提供一個和Rosetta搭配的適用於遊戲開發的翻譯內容管理系統:RosettaServer。
只有這些了嗎
在遊戲本地化問題上,是否還有其他前瞻性的東西,我想很多人和我一樣好奇。而思考這些問題則花去了我十分多的時間(實際上這些問題在一年多以前就以各種片段的形式存在於我腦海而直到最近Rosetta的第一個可執行版本完成開發,我才開始著筆寫下這篇文章)。有幸於我對小眾遊戲的關注和經常看到好玩的但沒有中文的遊戲/軟體會動漢化他的念頭的緣故。我得以見到很多遊戲提供的翻譯系統,也接觸到了一些處於這個領域十分前沿的問題。
AVG.js和huozi.js
AVG.js是我的一位朋友Icemic在HTML5平臺上實現的galgame開發套件。這套工具裡最讓我印象深刻的是專為遊戲內中日韓文字排版而實現的排版引擎huozi.js。
實際上游戲開發對文字顯示的要求僅僅處於能看這一階段。而去思考並實現對應語言的規範排版需求的系統的情況基本不存在。雖然W3C的i18n小組一直在做這方面的工作(huozi.js的開發也有參考他們的中文排版需求),但是遊戲行業對這些毫無興趣。這一方面是因為這個問題本身十分複雜且和多個系統耦合(排版系統和I18N),另一方面遊戲內往往並不會出現大篇幅的密集文字。然而隨著遊戲的發展,我相信以後不少遊戲都會遇到類似的需求(如上古卷軸5裡的書籍系統)。
Wayward:從語義到語法
大部分情況下,我們漢化一段文字的時候,只是在理解它的語義,然後用另一種語言來表達它。但在一些特殊情況下,翻譯工作會涉及到把一門語言的語法翻譯到另一門語言上的問題。
最簡單的情況是單複數的處理。在中文中單複數很少會導致一個名詞發生變化,然而在英語等語言裡,單複數往往會導致名詞發生變動,而在一些語言裡甚至一個兩個三個和複數個都對應著不同的形式(在I2 Localization裡有處理這個情況,並且支援最多為七的情況)。
然而名詞的單複數只是這個問題下最簡單的一種情況。在Wayward中,翻譯需要處理更為複雜的語法問題,除了名詞單複數外,還有代詞和介詞等。整個遊戲翻譯模板裡有一個很複雜的表格來表述這種轉換:
之所以會存在這種情況是因為,Wayward是一個RougeLike類遊戲,而遊戲內有不少文字來自程式生成。在這時候就自然而然的涉及到語法的問題。翻譯工作也不僅僅是翻譯語義,而要考慮對應語言的語法。
而可以遇見的未來是,由於過程生成技術在遊戲裡越來越廣泛的使用,遊戲文字的過程生成也必然會越來越盛行。以後涉及到語法的本地化工作的情況也會越來越常見。
配音和口型生成
幾乎所有的3A遊戲都提供配音,而這時候又涉及到3D遊戲使用對應語言的配音時人物模型口型的問題。很早以前遊戲可能會考慮採取捕捉或者人工調整來生成口型資料,而如今更多的是使用演算法通過文字來生成對應的口型資料。這樣一來又引入了一種全新的需要多語言處理的資料。
結語
以上提到的諸多問題,很多都處於多個領域交叉的情況,甚至你很難界定某些具體問題屬於那個領域。但是你要完成一個遊戲的多語言工作,往往必然會遇到他們。
另一方面,從最早的普通軟體到如今的3A遊戲,隨著遊戲技術不停的發展,也會引入越來越多的問題。最早我們可能只需要翻譯文字,之後引入了影象檔案,再後來遊戲配音又引入新的問題(配音和口型)。而可以預見的未來是,隨著過程生成技術流行,很多傳統的對多語言資源的處理也會面臨著翻譯內容變為翻譯規則。而只要遊戲技術不停的發展,遊戲本地化工作也會出現新的待解決的問題,這是本地化和國際化做為一個同遊戲內多個方面耦合的系統的必然結局。
參考
1.^遊戲程式設計精粹3-1.12 1.13
2.^《阿拉伯文字文字佈局需求》-w3c國際化興趣組阿拉伯文佈局特別任務組
作者:shitake
來源:indienova
原地址:https://indienova.com/indie-game-development/unity-i18n/
相關文章
- with as探討時小插曲
- Unity UI優化小結UnityUI優化
- unity小恐龍模型控制Unity模型
- Unity Network 使用小結Unity
- 【小貼士】探一探javascript中的replaceJavaScript
- Unity之掛載小問題Unity
- unity ui的建立方式小記UnityUI
- Unity3D的LightProbe動態光探頭用法介紹Unity3D
- MasaFramework -- i18n (國際化)Framework
- Django 多語言教程 (i18n)Django
- python i18n不能使用Python
- jquery i18n(前端國際化)jQuery前端
- .NET程式設計5月小結 - Blazor, Unity, Dependency Injection程式設計BlazorUnity
- 窺探小程式的祕密 個人小程式申請
- Unity開發實戰探討-資源的載入釋放最佳策略簡要心得Unity
- 雲棲探館!雲小寶首秀遇上老司機小龍,猜猜誰贏了?
- Spring Security 整合 微信小程式登入的思路探討Spring微信小程式
- Unity——Js和Unity互相呼叫UnityJS
- linux i18n修改系統語言Linux
- nuxt2 國際化i18n使用UX
- 小閃對話:微信長連線設計的探討(三)
- 小閃對話:微信長連線設計的探討(二)
- Unity使用小劇場—建立的按鈕On Click()只有MonoScript怎麼辦UnityMono
- environmentmap in unityUnity
- Unity元件Unity元件
- unity xchartsUnity
- unity backbufferUnity
- cinemachine unityMacUnity
- mipmap of unityUnity
- 全民探店小魔推系統會從市場上消失嗎?
- [Android] EditText設定只能輸入2位小數的探討Android
- [Unity3d]unity中http通訊Unity3DHTTP
- vue中如何使用i18n實現國際化Vue
- Spartacus i18n 配置相關程式碼的工作原理
- Internationalization(i18n) support in SAP CRM,UI5 and HybrisUI
- Rails 訊息資源的國際化(I18n)AI
- 萬字+28張圖帶你探祕小而美的規則引擎框架LiteFlow框架
- Mozilla和Unity合作將Unity引擎帶到WebUnityWeb