2013-12-10 45 views
11

考慮編寫一個PHP庫,該庫將通過Packagist或Pear發佈。它針對的是在任意設置中使用它的對等開發人員。如何國際化PHP第三方庫

該庫將包含爲客戶端確定的一些狀態消息。我該如何國際化此代碼,以便使用該庫的開發人員可以自由地插入自己的本地化方法?我不想承擔任何事情,特別是不要強迫開發者使用gettext。

要在一個例子工作,讓我們這個類:

class Example { 

    protected $message = "I'd like to be translated in your client's language."; 

    public function callMe() { 
     return $this->message; 
    } 

    public function callMeToo($user) { 
     return sprintf('Hi %s, nice to meet you!', $user); 
    } 

} 

這裏有兩個問題:我要怎樣私人$message翻譯,以及如何允許開發本地化字符串裏面callMeToo()

一(非常不方便),選擇是,要請一些國際化的方法在構造函數中,像這樣:

public function __construct($i18n) { 
    $this->i18n = $i18n; 
    $this->message = $this->i18n($this->message); 
} 

public function callMeToo($user) { 
    return sprintf($this->i18n('Hi %s, nice to meet you!'), $user); 
} 

,但我特別希望有一個更好的解決方案。

編輯1:除了簡單的字符串替換外,i18n的字段是一個寬字符。前提是,我不想在我的庫中包含任何i18n解決方案,或者強制用戶選擇一個專門用於我的代碼的解決方案。

那麼,如何構建我的代碼,以便爲不同方面提供最佳和最靈活的本地化:字符串翻譯,數字和貨幣格式,日期和時間......?假設其中一個或另一個顯示爲我的庫的輸出。消費開發人員可以在哪個位置或界面插入本地化解決方案?

回答

7

最常用的解決方案是一個字符串文件。例如。像下面:

# library 
class Foo { 
    public function __construct($lang = 'en') { 
    $this->strings = require('path/to/langfile.' . $lang . '.php'); 
    $this->message = $this->strings['callMeToo']; 
    } 

    public function callMeToo($user) { 
    return sprintf($this->strings['callMeToo'], $user); 
    } 
} 

# strings file 
return Array(
    'callMeToo' => 'Hi %s, nice to meet you!' 
); 

可以,避免$this->message分配,還與魔術干將工作:

# library again 
class Foo { 
    # … code from above 

    function __get($name) { 
    if(!empty($this->strings[$name])) { 
     return $this->strings[$name]; 
    } 

    return null; 
    } 
} 

您甚至可以添加一個loadStrings方法,這需要一個字符串數組從用戶和合並它與你的內部字符串表。

編輯1:爲了獲得更大的靈活性,我會稍微改變上述方法。我會添加一個翻譯函數作爲對象屬性,並且當我想要本地化一個字符串時總是調用它。默認函數只是在字符串表中查找字符串,如果它找不到本地化的字符串,就像gettext一樣返回值本身。然後,使用您的庫的開發人員可以將其功能更改爲他自己提供的功能,以執行完全不同的本地化方法。

日期本地化不是問題。設置語言環境是與你的類庫使用的軟件有關的。格式本身是一個本地化的字符串,例如, $this->translate('%Y-%m-%d')將返回日期格式字符串的本地化版本。

通過設置正確的語言環境和使用sprintf()等功能來完成數字本地化。

但是,貨幣本地化是個問題。我認爲最好的辦法是增加一個貨幣轉換函數(也許爲了更好的靈活性,另一個數字格式化函數),如果開發人員想改變貨幣格式,他可以覆蓋它。或者,您也可以爲貨幣實施格式字符串。例如%CUR %.02f - 在此示例中,您將用貨幣符號替換%CUR。貨幣符號本身也是本地化的字符串。

編輯2:如果你不想使用setlocale你必須做很多工作...基本上你必須重寫strftime()sprintf()來實現本地化日期和數字。當然可能,但很多工作。

+0

謝謝,有趣。基本上,建議的'loadStrings'似乎與我的需求很匹配。你知道野外的任何項目,他們如何處理它? (雖然Zend被豁免,但他們有自己的i18n庫) – Boldewyn

+0

@Boldewyn我知道一些處理它的閉源項目,但我必須認真思考一個開源項目。大多數都沒有本地化,如果他們是,他們使用gettext ... – ckruse

+0

是的,這正是我的問題,不幸的是... – Boldewyn

2

基本方法是爲消費者提供一些方法來定義映射。它可以採取任何形式,只要用戶可以定義雙射映射。

例如,螳螂Bug跟蹤使用一個簡單的globals file

<?php 
    require_once "strings_$language.txt"; 
    echo $s_actiongroup_menu_move; 

他們的方法是基本的,但工作得很好。它包裝在一個類,如果你喜歡:

<?php 
    $translator = new Translator(Translator::ENGLISH); // or make it a singleton 
    echo $translator->translate('actiongroup_menu_move'); 

使用XML文件,而不是,或INI文件或CSV文件......你喜歡的任何格式,其實。


回答您稍後編輯/評論

是,上面並沒有不同於其他解決方案了。但我相信沒有什麼別的可說:

  • 翻譯只能通過字符串替換來實現(映射可能採取的形式無限多)
  • 格式數量和日期是關你的事。它是表示層的責任,你應該只返回原始數據(或DateTime S或時間戳),(除非你的圖書館的根本目的是本地化;)
+0

感謝您的回答,但我特別感興趣的是我的圖書館用戶沿途的後果。這些解決方案可能產生的缺陷是什麼?除此之外,我沒有看到你的概念與我在問題中提供的概念或已經提到的@ckruse有什麼不同。我還會更新這個問題,以擴展到更多國際化問題。 – Boldewyn

2

這裏有一個主要的問題。您不想在現在的國際化問題中製作代碼。

讓我解釋一下。主翻譯可能是程序員。第二個和第三個可能是,但是然後你想把它翻譯成任何語言,即使對於非程序員。對於非程序員來說,這應該很容易。通過非程序員的類,函數等來狩獵絕對不是好事。

所以我提出這樣的:保持你的源句子(英語)在不可知格式,這很容易理解大家。這可能是一個XML文件,數據庫或任何其他形式,你看它適合。然後在你需要的地方使用你的翻譯。你可以不喜歡它:

class Example { 
    // Fetch them as you prefer and store them in $messages. 
    protected $messages = array(
    'en' => array(
     "message" => "I'd like to be translated in your client's language.", 
     "greeting" => "Hi %s, nice to meet you!" 
    ) 
    ); 

    public function __construct($lang = 'en') { 
    $this->lang = $lang; 
    } 

    protected function get($key, $args = null) { 
    // Store the string 
    $message = $this->messages[$this->lang][$key]; 
    if ($args == null) 
     return $this->translator($message); 
    else { 
     $string = $this->translator($message); 
     // Merge the two arrays so they can be passed as values 
     $sprintf_args = array_merge(array($string), $args); 
     return call_user_func_array('sprintf', $sprintf_args); 
     } 
    } 

    public function callMe() { 
    return $this->get("message"); 
    } 

    public function callMeToo($user) { 
    return $this->get("greeting", $user); 
    } 
} 

此外,如果你想使用一個small translation script I did,你可以進一步簡化它。它使用一個數據庫,所以它可能沒有那麼多的靈活性,因爲你正在尋找。您需要注入它,並在初始化中設置語言。請注意,如果文本不存在,文本會自動添加到數據庫中。

class Example { 
    protected $translator; 

    // Translator already knows the language to translate the text to 
    public function __construct($Translator) { 
    $this->translator = $Translator; 
    } 

    public function callMe() { 
    return $this->translator("I'd like to be translated in your client's language."); 
    } 

    public function callMeToo($user) { 
    return sprintf($this->translator("Hi %s, nice to meet you!"), $user)); 
    } 
} 

它可以很容易地修改爲使用xml文件或任何其他來源的翻譯字符串。

注意的第二個方法

  • 這比你提出的解決方案不同,因爲它正在做的工作在輸出,而不是在初始化,所以沒有必要跟蹤的每一個字符串。

  • 你只需要用英文寫一次你的句子。我寫的類將把它放在數據庫中,前提是它已正確初始化,使您的代碼非常乾淨。這正是我開始使用它的原因,而不是僅僅使用gettext(以及我的簡單需求的gettext的荒謬大小)。答:這是一個古老的類。當時我並不瞭解很多。現在我會改變一些東西:製作一個language字段,而不是en,es等,在這裏和那裏拋出一些例外,並上傳我做的一些測試。

+0

感謝您的回答。我也查看了Github上的代碼。不幸的是,這個解決方案不適合我。我想盡可能地限制自己的需求,同時仍然允許消費用戶完全本地化我的代碼所發出的內容。我還更新了這個問題,以包含日期格式等內容。我擔心這比翻譯輸出或初始化時更復雜。 – Boldewyn

+0

隨着你的更新,我認爲它實際上太寬了。要麼代碼需要數百行代碼,要麼是[程序員](http://programmers.stackexchange.com/)中更好的一個概念性問題。然而,如果你願意,可以隨意擴展(並推回給github)我指出要處理更好本地化的課程。 –

+0

另一個有趣的問題是,你如何處理貨幣?由於這是隨時間而變化的,所以您需要使用外部API,因此執行所有重新定義操作變得越來越不方便,而且它不會變得簡單*。 –