本地化?

處理不同地域?

CodeIgniter 提供了一系列幫助你處理多語言環境下將應用本土化的工具。盡管一個應用完全地本土化是一個復雜的問題,在你的應用中將一些字符串根據不同的語言進行替換,是相當簡單的。

語言字符串存儲于 app/Language 目錄下,其下的每個子目錄都代表著一種所支持的語言:

/app
    /Language
        /en
            app.php
        /fr
            app.php

重要

地區的識別僅對于使用了 IncomingRequest 類的基于 web 的請求起效,命令行請求無法使用這些功能。

配置地區?

每個站點都擁有默認的語言/地區屬性,可以通過 Config/App.php 進行設置:

public $defaultLocale = 'en';

該變量的值,可以是任何字符串值,用于你的應用程序處理文本字符串和其他格式用。 我們推薦使用 BCP 47 類型的語言代號。該說明中,語言編碼是例如用于美國英語的 en-US 或者是用于法國法語的 fr-FR 的格式。 或者也可以參照 W3C’s site 以獲取可讀性更高的說明。

如果不能找到絕對匹配的語言代碼時,該系統將足夠靈活地使用更為泛化的語言代碼。 如果地區代碼被設為 en-US ,而只有 en 語言的語言文件,那么因為沒有更為精確地匹配 en-US 的語言,我們就會使用這些語言文件。 但是如果有一個語言文件目錄 app/Language/en-US 存在的話,該目錄里的語言文件就會被首先使用。

地區識別?

我們有兩種方式用于在請求中識別正確的地區。第一種方式是”設后即忘”的方式,并會自動執行 內容協商 以決定使用正確的地區。 第二種方式使得你可以在路由中給定一個特定的分段并用于設置地區。

內容協商?

你可以通過在 Config/App 中設置兩個額外的參數來自動開啟內容協商。 第一個參數用于告訴 Request 類我們需要開啟內容協商,因此只要將其設為 true 即可:

public $negotiateLocale = true;

當該參數啟用時,系統會自動根據你在 $suppoertLocales 中定義的語言數組來協商使用正確的語言。 如果在你提供的語言和所請求的語言中中匹配不到的話,該數組的第一個成員就會被使用。在下例中,在不匹配時, en 地區就會被使用:

public $supportedLocales = ['en', 'es', 'fr-FR'];

在路由中?

第二種方法是用一個自定義的通配符來檢測所需要的地區,并將其用于當前請求中。在你的路由中,通配符 {locale}} 可以被替換為一個路由分段。 如果該分段存在的話,所匹配到的路由分段就是你的地區:

$routes->get('{locale}/books', 'App\Books::index');

在本例中,如果用戶嘗試訪問 http://example.com/fr/books ,地區就會被設置為 fr ,并假設這是一個合理的地區參數。

注解

如果該路由分段值匹配不到 App 配置文件中合理的地區值的話,就會用默認的地區來代替。

獲取當前地區?

當前地區默認從 IncomingRequest 實例中獲取,通過 getLocale() 方法。 如果你的控制器繼承了 CodeIgniter\Controller ,以上操作也可以通過 $this->request 來實現:

<?php namespace App\Controllers;

class UserController extends \CodeIgniter\Controller
{
    public function index()
    {
        $locale = $this->request->getLocale();
    }
}

或者你也可以用 服務類 來獲取當前的請求:

$locale = service('request')->getLocale();

語言本土化?

創建語言文件?

Languages do not have any specific naming convention that are required. The file should be named logically to describe the type of content it holds. For example, let’s say you want to create a file containing error messages. You might name it simply: Errors.php.

Within the file, you would return an array, where each element in the array has a language key and the string to return:

'language_key' => 'The actual message to be shown.'

注解

It’s good practice to use a common prefix for all messages in a given file to avoid collisions with similarly named items in other files. For example, if you are creating error messages you might prefix them with error_

return [
    'errorEmailMissing'    => 'You must submit an email address',
    'errorURLMissing'      => 'You must submit a URL',
    'errorUsernameMissing' => 'You must submit a username',
];

基本用途?

你可以使用 lang() 輔助函數從所有語言文件中獲取文本值,通過將文件名和語言鍵作為第一個參數,以點號(.)分隔。 舉例來說,從 Errors 語言文件中加載 errorEmailMissing 字符串,你可以如下操作:

echo lang('Errors.errorEmailMissing');

如果所請求的語言鍵對于當前的地區來說不存在的話,就會不做修改的返回請求的參數。在本例中,如果 ‘Errors.errorEmailMissing’ 對應的翻譯不存在的話,就會直接被返回。

參數替換?

注解

以下函數需要加載并啟用 intl 擴展。如果該擴展未加載,則不會進行替換操作。 可參閱 Sitepoint.

你可以在語言字符串中,通過對 lang() 函數的第二個參數傳遞一個值數組來替代通配符中的內容。這一操作對于簡單的數字翻譯和格式化來說非常方便:

// 語言文件, Tests.php:
return [
    "apples"      => "I have {0, number} apples.",
    "men"         => "I have {1, number} men out-performed the remaining {0, number}",
    "namedApples" => "I have {number_apples, number, integer} apples.",
];

// 輸出 "I have 3 apples."
echo lang('Tests.apples', [ 3 ]);

通配符中的第一項對應著數組的索引下標(如果該下標是數字格式的話):

// 輸出 "The top 23 men out-performed the remaining 20"
echo lang('Tests.men', [20, 23]);

如果希望的話,你也可以使用命名數組來更為直接地傳遞參數:

// 顯示 "I have 3 apples."
echo lang("Tests.namedApples", ['number_apples' => 3]);

顯然你可以實現比起數字替換更為高級的功能。根據標準庫 official ICU docs 所示,以下類型的數據可被替換:

  • numbers - 整數,匯率,百分比
  • dates - 短,中,長,完整格式
  • time - 短,中,長,完整格式
  • spellout - 大寫數字 (例如 34 變成 thirty-four)
  • ordinal
  • duration

Here are a few examples:

// The language file, Tests.php
return [
    'shortTime'  => 'The time is now {0, time, short}.',
    'mediumTime' => 'The time is now {0, time, medium}.',
    'longTime'   => 'The time is now {0, time, long}.',
    'fullTime'   => 'The time is now {0, time, full}.',
    'shortDate'  => 'The date is now {0, date, short}.',
    'mediumDate' => 'The date is now {0, date, medium}.',
    'longDate'   => 'The date is now {0, date, long}.',
    'fullDate'   => 'The date is now {0, date, full}.',
    'spelledOut' => '34 is {0, spellout}',
    'ordinal'    => 'The ordinal is {0, ordinal}',
    'duration'   => 'It has been {0, duration}',
];

// Displays "The time is now 11:18 PM"
echo lang('Tests.shortTime', [time()]);
// Displays "The time is now 11:18:50 PM"
echo lang('Tests.mediumTime', [time()]);
// Displays "The time is now 11:19:09 PM CDT"
echo lang('Tests.longTime', [time()]);
// Displays "The time is now 11:19:26 PM Central Daylight Time"
echo lang('Tests.fullTime', [time()]);

// Displays "The date is now 8/14/16"
echo lang('Tests.shortDate', [time()]);
// Displays "The date is now Aug 14, 2016"
echo lang('Tests.mediumDate', [time()]);
// Displays "The date is now August 14, 2016"
echo lang('Tests.longDate', [time()]);
// Displays "The date is now Sunday, August 14, 2016"
echo lang('Tests.fullDate', [time()]);

// Displays "34 is thirty-four"
echo lang('Tests.spelledOut', [34]);

// Displays "It has been 408,676:24:35"
echo lang('Tests.ordinal', [time()]);

你需要閱讀 MessageFormatter 類以及 ICU 編碼格式以充分使用這一功能的特性,例如執行條件替換,多元素替換等。以上兩者的鏈接都在上文中有所提及,希望可以可以幫助你充分利用這一特性。

確定地區?

為了在替換參數時顯式調用一個不同的地區,你可以通過將地區作為 lang() 方法的第三個參數來實現:

// Displays "The time is now 23:21:28 GMT-5"
echo lang('Test.longTime', [time()], 'ru-RU');

// Displays "£7.41"
echo lang('{price, number, currency}', ['price' => 7.41], 'en-GB');
// Displays "$7.41"
echo lang('{price, number, currency}', ['price' => 7.41], 'en-US');

嵌套數組?

語言文件可以接受嵌套數組作為參數,以更為方便地處理列表類型的數據等:

// Language/en/Fruit.php

return [
    'list' => [
        'Apples',
        'Bananas',
        'Grapes',
        'Lemons',
        'Oranges',
        'Strawberries'
    ]
];

// Displays "Apples, Bananas, Grapes, Lemons, Oranges, Strawberries"
echo implode(', ', lang('Fruit.list'));

語言回滾?

如果對于一個給定的地區,你有多種語言文件類型,例如對于 Language/en.php ,你可以通過為這一地區增加一個語言變量,例如 Language/en-US/app.php

你唯一需要為這些信息提供的就是它們在不同地區里的值。如果對應的信息翻譯不存在的話,就會從主地區設置中獲取并賦值。

本土化功能可以將所有翻譯信息回滾為英語,以防止在新的信息增加到框架中時,你沒辦法為所在地區實現翻譯。

因此,如果你在使用地區 fr-CA ,那么翻譯信息會首先從 Language/fr/CA 中搜索,然后在 Language/fr ,最后在 Language/en 中。

信息翻譯?

在我們的 倉庫 .中,有一份”正式的”翻譯集

你可以下載該倉庫并復制其中的 Language 目錄到你的 app 中。因為 App 命名空間映射到了你的 app 目錄,對應的翻譯就會被自動使用。

不過更好的使用方式是在你的項目中使用 composer require codeigniter4/translations ,因為翻譯目錄自動映射之后,這樣被翻譯過的信息就會自動被使用。