翻譯
「國際化」(internationalization,常被簡寫為i18n),是指將字串和其他一些具有區域特徵的片段從你的程式中提取(abstract)出來並基於使用者所在區域(如語言、國家)而將其置於一個能被翻譯和轉化的層的過程。對於文字來說(text),這意味著用一個能夠把它(或「資訊」)翻譯成使用者所需語言的函數,來剝離文字的每一部分:
// 文本始终以英语输出echo 'Hello World'; // 文本将以用户指定语言或默认英语输出echo $translator->trans('Hello World');
locale的意思,單純來說就是使用者的語言和國家。在程式中它可以是任何一個字串,用來管理翻譯(translation)和其他格式資訊(如幣種)。推薦使用以ISO 639-1語言程式碼,加上一個底線(_
),再跟一個ISO 3166-1 alpha-2國家代碼(例如fr_FR
這個locale是指「法國法文」French/France)。
在本章,你將學習如何使用Symfony框架中的translation元件。你可以閱讀翻譯元件來了解更多。整體上,翻譯的過程有以下幾步:
開啟與設定Symfony的翻譯服務;
將字串抽象化(如“xxxx”),這是透過呼叫Translator去剝離它們來實現的;(參考翻譯基礎)
針對每個被支援的locale,建立翻譯資源/檔案,用於翻譯程式中每一個待譯字串;
針對request(請求)和可選的基於使用者整個session過程,來確定、設定和管理使用者的locale資訊。
設定 ¶
翻譯的過程是透過translator
服務來處理的,該服務使用使用者指定的locale來尋找並傳回翻譯過的資訊。使用translator之前,在設定檔中開啟它:
PHP:// app/config/config.php$container->loadFromExtension('framework', array('translator' => array('fallbacks' => array('en')),));
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:translator> <framework:fallback>en</framework:fallback> </framework:translator> </framework:config></container>
YAML:# app/config/config.ymlframework:translator: { fallbacks: [en] }
關於 fallbacks關鍵字 以及Symfony在找不到翻譯語種時如何處理,請參考 翻譯時的Locales回滾 以了解細節。
翻譯時要用到的locale資訊會儲存在request物件中。一般在路由中被設為 _locale
屬性。請參考 The Locale and the URL。
翻譯基礎 ¶
translator
服務負責完成文字的翻譯。為了翻譯一個文字區塊(被稱為“message”,以下稱為“訊息”),使用trans()方法。例如,你要在controller中翻譯一個簡單的訊息:
// ...use Symfony\Component\HttpFoundation\Response; public function indexAction(){$translated = $this->get('translator')->trans('Symfony is great'); return new Response($translated);}
上述程式碼被執行後,Symfony就嘗試基於使用者的 locale
來翻譯 “Symfony is great”訊息。為了讓這個過程實現,你需要告訴Symfony如何透過一個「翻譯來源(translation resource)」來執行翻譯。一般來說翻譯來源是一個文件,包含有成組的翻譯訊息,對應某一指定的locale。它就像個翻譯時的“字典”,可被創建為多種格式,但推薦使用XLIFF(譯註:就是後綴不同的xml格式):
PHP:// messages.fr.phpreturn array('Symfony is great' => 'J\'aime Symfony',);
YAML:# messages.fr.ymlSymfony is great: J'aime Symfony
XML:<!-- messages.fr.xlf --><?xml version="1.0"?><xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="file.ext"> <body> <trans-unit id="symfony_is_great"> <source>Symfony is great</source> <target>J'aime Symfony</target> </trans-unit> </body> </file></xliff>
關於這類檔案的存放位置等信息,參考翻譯來源/檔案名稱與位置
現在,如果使用者的locale是法語(例如fr_FR
或fr_BE
),那麼前面的訊息將會翻譯為 J'aime Symfony
。你也可以在 模板(templates) 完成翻譯。
翻譯的處理過程 ¶
為了能翻譯一則訊息,Symfony執行下列簡明流程:
先確定request物件中所儲存的目前使用者的
locale
資訊;#某一目錄(一大組message)的已經翻譯好的訊息,將從由
locale
(例如fr_FR
)所決定的翻譯來源載入。如果locale不存在,則由 fallback locale 所決定的翻譯訊息,也會被載入並且合併到目錄之中。最終結果就是產生了一個「翻譯大字典」;如果能夠從目錄中找到待翻訊息,翻譯結果將會被回傳。否則, translator會傳回原始資訊。
當使用trans()方法時,Symfony從對應的資訊目錄中,尋找準確的字串,然後傳回它(如果存在的話)。
訊息佔位符 ¶
有時,一則包含變數的訊息,需要被翻譯:
use Symfony\Component\HttpFoundation\Response; public function indexAction($name){$translated = $this->get('translator')->trans('Hello '.$name); return new Response($translated);}
可是,對於這樣一個字串,創建相應的翻譯是不可能的,因為translator始終在嘗試尋找“確定訊息”,包括變數值本身(如「Hello Ryan」和「Hello Fabien」在translator看來是兩條不同的訊息)。
對於這種情形,請參考元件文件中的 資訊佔位符/Message Placeholders。同樣情況在範本中的處理方法,參考 Twig Templates。
複數處理 ¶
另一個複雜場面,則是你在翻譯中要面對基於某些變數的「複數狀況」:
1 2 | # There is one apple. There are 5 apples. |
##1 | #{% trans %}Percent: %percent%%%{% endtrans %} |
{% trans with {'%name%': 'Fabien'} from "app" %}Hello %name%{% endtrans %} {% trans with {'%name%': 'Fabien'} from "app" into "fr" %}Hello %name%{% endtrans %} {% transchoice count with {'%name%': 'Fabien'} from "app" %}{0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples{% endtranschoice %}
{{ message|trans }} {{ message|transchoice(5) }} {{ message|trans({'%name%': 'Fabien'}, "app") }} {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }}
trans和transchoice調節器,可用於翻譯變數文字和複雜表達式:
{# 标签中被翻译的文本从不被转义#}{% trans %}<h3>foo</h3>{% endtrans %} {% set message = '<h3>foo</h3>' %} {# 变量调节器翻译的字符串和变量,默认将被转义 #}{{ message|trans|raw }}{{ '<h3>bar</h3>'|trans|raw }}
在翻譯時無論使用標籤或調節器,效果是相同的,但有一個微小區別:自動輸出轉義功能僅對調節器有效。換言之,如果你需要翻譯出來的訊息“不被轉義”,則必須在translation調節器後面再跟一個raw調節器:
1
<?php echo $view['translator']->trans('Symfony is great') ?> <?php echo $view['translator']->transChoice('{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',10,array('%count%' => 10)) ?>################
注意這時僅會影響到當前模板,而不包括任何「被包容(included)」的模板(為的是減少副作用)。
PHP模板 ¶
translator也可以在PHP模板中透過translator helper來使用:
PHP:// app/config/config.php$container->loadFromExtension('framework', array('translator' => array('paths' => array('%kernel.root_dir%/../translations',),),));
翻譯來源/檔案的命名和位置 ¶
Symfony在下列位置尋找資訊檔案(即是translations/翻譯資訊):
app/Resources/translations
目錄;app/Resources/<bundle name>/translations
目錄;任何bundle下的
Resources/translations/
目錄
上面的位置是依照「高優先權在前」的順序排列的。這意味著,你可以用前面兩個目錄之一,來覆寫某個bundle中的翻譯訊息。
覆寫機制是基於鍵等級(key level)而執行:只有被覆寫的鍵需要被列在高優先級的資訊檔案中。當一個鍵沒有在資訊檔案中被找到時,translator會自動回滾到低優先順序的資訊檔案中。
資訊檔案的檔案名稱也很重要,每個資訊檔案必須按下列命名路徑來命名:domain.locale.loader
:
domain: 這是一個可選項,用於組織資訊檔案成為群組(例如admin, navigation 或default messages)。參閱使用翻譯訊息的網域Using Message Domains;
#locale: 這是翻譯訊息的locale (例如en_GB, en, 等等);
loader: 這是Symfony如何來載入和解析資訊檔案(也就是
xlf
,php
,yml
等檔案後綴).
載入器(loader)可以是任何已註冊載入器的名稱。 Symfony預設提供了許多載入器,包括:
xlf
: 載入XLIFF檔;##php
: 載入PHP檔;
yml
: 載入YAML檔;
載入資訊目錄 Loading Message Catalogs。
你可以在設定檔中透過paths
選項加入一個目錄:
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:framework="http://symfony.com/schema/dic/symfony" xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:translator> <framework:path>%kernel.root_dir%/../translations</framework:path> </framework:translator> </framework:config></container>
YAML:# app/config/config.ymlframework:translator:paths:- '%kernel.root_dir%/../translations'
1
每次你建立一個新的翻譯來源(translation resource)或安裝了一個包含翻譯來源的bundle時,一定要清除緩存,這樣Symfony才能發現這個新的翻譯來源。
$ php app/console cache:clear | #rrreee |
翻譯時Locale的回溯 ¶
#假設一個使用者的locale資訊是fr_FR
,而你正翻譯的鍵是Symfony is great
。為了找到法文信息,Symfony切實地檢查若干locale的翻譯源:
#首先,Symfony在一個
fr_FR
的翻譯源(例如messages.fr_FR.xlf)尋找翻譯資訊;如果沒找到,Symfony在一個
fr
翻譯來源(例如messages.fr.xlf)繼續尋找翻譯資訊;#如果仍然找不到,Symfony使用fallbacks這個設定參數, 它被預設設為
en
(參考FrameworkBundle設定資訊)。
2.6版Symfony引進了將缺少的翻譯訊息寫入日誌的能力。
當Symfony無法找到給定locale的翻譯資訊時,它會把缺少的翻譯資訊給加入到日誌檔案中。參閱 logging。
翻譯資料庫內容 ¶
翻譯資料庫內容時要用到Doctrine擴充功能中的Translatable Extension 或Translatable Behavior(PHP 5.4 )。更多資訊請參考對應文件。
翻譯約束資訊 ¶
參考 如何翻譯驗證約束訊息 以了解更多。
處理使用者的Locale ¶
翻譯的過程是取決於使用者的locale的。閱讀 如何操作使用者的Locale 以了解如何處理。
對翻譯進行偵錯 ¶
debug:translation 指令列語句從Symfony 2.5開始引入。 Symfony 2.6之前,此指令是translation:debug。
當你在不同語言的大量翻譯訊息中操作時,要追蹤到遺失了哪一個訊息以及哪一個訊息沒有被使用,是很困難的。閱讀 如何找到遺失或未使用的翻譯資訊 以了解如何發現這一類翻譯資訊。
總結 ¶
使用Symfony的翻譯元件創建一個國際化的應用程式將不再是一個“痛苦過程”,而是歸結於以下簡單步驟:
從程式中抽像出待翻譯的訊息,把每一則訊息用
trans()
或transChoice()
方法取代(透過使用Translator一文了解更多);透過建立訊息檔案(translation message file)將待譯訊息翻譯成多個locale語種。 Symfony能夠找到並處理每個文件,因為這些文件的名字遵循指定的命名約定;
#管理好用戶的locale,它可以存在request中,但是也可以存在用戶的session中。