Chromium本地化的方法

http://blog.csdn.net/only_youch/article/details/5892706

Chromium的本地化方法

 

 

一、軟件環境

      chromium版本:6.0.482.0

      os:windows xp sp2

 

二、chromium的本地化

             chromium中需要本地化的部分有三部分:

             1>chromium項目本地化,2>webkit本地化,3>setup本地化.

             由於對第2種的本地化需求較少,setup部分的本地化較簡單,本文只講述1部分的本地化方法。

 

 

三、chromium項目本地化

 

        1.通過編譯chrome項目後,可以發現,在生成的locals中,共有51個.dll文件,分別是不同語言的本地化庫。在sln文件中分別對應有這51個項目,而在每個項目中都包含名爲generated_resources_xx.rc的資源文件(xx爲語言代號,例如zh_CN爲簡體中文),在其下的stringtable中則是以此種語言的本地化字符串集。細心的朋友可以發現,整個chrome編譯之前,這些rc文件是不存在的,它的生成是在編譯時動態生成的。生成它的項目正是chrome_strings項目。

 

 

        2.打開chrome_strings項目可以看到,其下包含了三個grd文件:chromium_strings.grd, generated_resources.grd, google_chrome_strings.grd。另外還有一批xtb文件。

    google_chrome_strings.grd文件是用於編譯chrome時才使用的,而我們是要編譯chromium項目,因此不涉及此文件.

    chromium_string.grd文件涉及到chromium項目的本地化和setup的本地化,其中包含安裝卸載軟件介紹等字符串的本地化。

    generated_resources.grd主要涉及chromium項目的本地化,幾乎包含所有chrome.exe中所要使用到的字符串的本地化。

 

    綜上所述,generated_resource.grd是我們需要關注的對象。打開該文件可以看到,該文件由3部分組成:

 

<output>部分包含了兩種生成文件,

<output filename="generated_resources_zh-CN.rc" type="rc_all" lang="zh-CN" />

<output filename="generated_resources_zh-CN.pak" type="data_package" lang="zh-CN" />

 

<translations>部分包含一種生成(翻譯)文件:

<file path="resources/generated_resources_zh-CN.xtb" lang="zh-CN" />

 

<release>部分包含的是message對,每一個<message></message>中包含了一個所要使用的字符串。

 

    3.本地化原理

             顧名思義,需要本地化,那麼首先需要有一個“標準”字符串文件,然後在不同種語言下替換掉該語言。由此我們需要揭開上面所述的四種文件之間(grd, rc, xtb, dll)的生成關係:

    

                             <xtb>

                 grd  --------------->  rc     --------->  dll

 

grd:grd文件就是“標準”字符串文件,在其中定義好項目中所需使用的字符串id與對應的字符串值(一般爲英文版本,因爲比較通用)。

xtb:針對grd文件的不同語言翻譯版本文件,例如,中文環境下的字符串翻譯都需要寫在generated_resources_zh-CN.xtb中,至於存放格式,這是後面討論的重點。

rc:本文件對應在不同的語言項目中,並且被該項目所包含以便編譯時生成到dll文件中。

dll:由rc文件生成出來的不同語言翻譯版本的字符串“庫”。

 

    4.如何本地化

           上面提到,對“標準”文件本地化時,要把對應語言的翻譯版本寫到對應的xtb文件中。因此翻譯簡體中文版本時,只要將對應“標準”文件中的字符串本地化到generated_resources.xtb文件中,打開此文件可以看到。

      在generated_resources.grd中字符串值

     <message name="IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION" desc="Description for a keyword match.">
        (Keyword: <ph name="KEYWORD">$1</ph>) 

     </message>

   (注:$1是chromium中定義的字符替換標識符,此部分的原理不在本文討論範圍內。)

  在generated_resources_zh_CN.xtb中對應的翻譯版本如下:

     <translation id="406259880812417922">(關鍵字:<ph name="KEYWORD"/>)</translation>

  在generated_resources.zh_TW.xtb中對應的翻譯版本如下:

    <translation id="406259880812417922">(關鍵字: <ph name="KEYWORD"/>)</translation>

 

      由此,我們可以預測程序中使用字符串id爲IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION的地方,在中文環境下對應使用的字符串是翻譯字符串中id=406259880812417922所對應的“(關鍵字:$1)”, 在繁體環境下對應的是(關鍵字:$1) 但是這是如何對應起來的?我們只能看到在下面的翻譯版本和上面message中都含有同一個鍵“id”,但這兩個鍵的值之間看不出有明顯的聯繫。因此如果找到了這兩者之間的聯繫,那麼我們就找到了把“標準”字符串翻譯成各種語言版本的根本方法。

      到此,想從項目中去找到這個聯繫的頭緒已經斷了,此時我想到了官網。果然,官網上給出了答案:

 

"If you are adding a string to Chromium that you want for a different version of Chromium and you want to have translation in other languages, you need to add translations to the .xtb files in /src/chrome/app/resources.  These files have a transalation id that is based on a hash of the en-US string (see /src/tools/grit/external/FP.py for details on the hash)"


   答案就在/src/tools/grit/external/FP.py文件中,打開該文件看到如下代碼:

def UnsignedFingerPrint(str, encoding='utf-8'):
  """Generate a 64-bit fingerprint by taking the first half of the md5
  of the string."""
  hex128 = _new_md5(str).hexdigest()
  int64 = long(hex128[:16], 16)
  return int64

def FingerPrint(str, encoding='utf-8'):
  fp = UnsignedFingerPrint(str, encoding=encoding)
  # interpret fingerprint as signed longs
  if fp & 0x8000000000000000L:
    fp = - ((~fp & 0xFFFFFFFFFFFFFFFFL) + 1)
  return fp

      從這兩個python寫的函數中可以看到基本的思路:FingerPrint函數接收一個字符串輸入,然後調用函數UnsignedFingerPrint對字符串計算md5值hex128,並對hex128按照16進製取前一半,再經常一系列位運算之後返回其值,由此大致可以判斷算出來的值是一列數字串,因此,我們再只需要確定輸入的字符串的規則即可算出最後的數字串的值。

 

      經過我對輸入字符串進行各種可能的測試之後,發現有時算出的值與xtb中的值相同,有時不同,由此斷定計算方法中還有未知的部分,因此我們需要找到調用FingerPrint函數的部分是否做了其它處理。因此我在源代碼中搜索,結果如願找到了文件/src/tools/grit/external/tclib.py中有如下代碼:

 

def GenerateMessageId(message, meaning=''):
  fp = FP.FingerPrint(message)
  if meaning:
    # combine the fingerprints of message and meaning
    fp2 = FP.FingerPrint(meaning)
    if fp < 0:
      fp = fp2 + (fp << 1) + 1
    else:
      fp = fp2 + (fp << 1)
  # To avoid negative ids we strip the high-order bit
  fpid = str(fp & 0x7fffffffffffffffL)
  return fpid

 

      這個函數中,我們看到了另一個輸入輸入字符串meaning,根據名字猜測應該是在有被替換的字符串標識符的時候會被使用到,函數的邏輯比較簡單,據此,我們得到了計算id值406259880812417922的大致方法。但被輸入的兩個字符串該選取<message>的哪個部分還是不確定。個人以爲,與其一個個這樣去輸入兩個字符串參數,然後用腳本計算id值,何不讓編譯程序“自動”爲我生成,這樣也免去我確定輸入參數時的各種錯誤情況。“自動”生成的方法很簡單,在這個函數中,計算出id值之後,把它保存到一個文件中就OK了。本函數修改後的代碼如下:

 

def GenerateMessageId(message, meaning=''):
  fp = FP.FingerPrint(message)
  if meaning:
    # combine the fingerprints of message and meaning
    fp2 = FP.FingerPrint(meaning)
    if fp < 0:
      fp = fp2 + (fp << 1) + 1
    else:
      fp = fp2 + (fp << 1)
  # To avoid negative ids we strip the high-order bit
  fpid = str(fp & 0x7fffffffffffffffL)

########
  fp2 = file('c:/message_log.txt', 'a')
 fp2.write('message=' + message + ', id=' + fpid + "/r/n")

  fp2.close()

#######
  return fpid

  

      上面#所包含的四行代碼將計算出的id值以xtb中的格式保存到c:/message_log.txt文件中,剩下需要做的就是編譯chrome_strings項目,再去找到message_log.txt文件中對應所需要計算的<message>值並將id拷貝到xtb文件中,附加上翻譯後的字符串即可。

例如,上面所列舉的IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION字符串在message_log.txt中對應的一列爲:

message=(Keyword: KEYWORD), id=406259880812417922

 

      當然,如果想要深究GenerateMessageId的兩個輸入參數也是很容易的,稍加改動write語句,把兩個輸入參數的值輸出即可知道輸入字符串的規則,但目的還是計算得知id,既然我們已經達到此目標,我就不去深究這個值。

 

      至此,我們的問題全部解決了,再來理一下整體思路,如果要添加一個新的字符串到chromium項目中,並且需要翻譯成本地版本,我們需要做的步驟如下:

1.添加字符串到generated_resources.grd文件中。

<message name="IDS_OK" desc="Used for Ok on buttons">
        OK
      </message>

 

2.only編譯chrome_strings項目,在message_log中找到下面一行

 message=OK, id=6965382102122355670

 

3.在generated_resrouces_zh_CN.xtb中添加如下一行翻譯值

 <translation id="6965382102122355670">確定</translation>

 

4.再編譯chrome_string項目,就可以在zh_CN項目的generated_resources.rc的stringtable中找到下面這一行

 IDS_OK        11028    確定

 

  當編譯出的chromium程序運行在簡體中文環境下時,調用IDS_OK的地方則會顯示出“確定”字符串。

 

 

 

後記:雖然本地化的問題已經解決,但我在完成此任務時,出現不少問題,在此列出來主要的幾個,讓大家少走一些彎路:

1.generated_resources.xtb中的id值不能重複,出現重複時,編譯chrome_string項目時會給出提示,但它的提示未明顯指出是重複問題。如果出現重複,刪掉一條即可,因爲id值如果重複,那麼說明這兩個字符串的值本身就是相同的,那麼在生成rc文件時,兩個<message>字符串都會去對應到這條id值上,對結果無影響。

 

2.計算出id值後,填到xtb文件中時,如果字符串有有需要被替換的部分,即原字符串中包含諸如<ph name="LANGUAGE">$1<ex>English</ex></ph>的字段,則xtb文件中對應字符串一定要附加上<ph name="LANGUAGE"/>的串,否則項目chrome_strings編譯通不過,同樣,編譯錯誤俺看不懂。

 

3.有人說,可以直接在生成的rc文件中修改字符串的翻譯以達到本地化的目的。當然,我承認這是最直接的方式,但不可忽視的是,如果你所使用的所有字符串是一次性生成,一次性在rc文件中修改完成,那麼這種方法OK,沒問題。但當你在grd中新添加一條字符串值時,或者做了rebuild時,rc文件會被重新生成,你所做的努力將前功盡棄,必須全盤重來。所以這是一種治標不治本的方法,不推薦使用。

 

 

(第一次寫文章,邏輯,思維不甚嚴密,請見諒,謝謝)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章