本地化?

處理不同地域?

CodeIgniter 提供了一系列幫助你處理多語言環(huán)境下將應(yīng)用本土化的工具。盡管一個(gè)應(yīng)用完全地本土化是一個(gè)復(fù)雜的問題,在你的應(yīng)用中將一些字符串根據(jù)不同的語言進(jìn)行替換,是相當(dāng)簡單的。

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

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

重要

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

配置地區(qū)?

每個(gè)站點(diǎn)都擁有默認(rèn)的語言/地區(qū)屬性,可以通過 Config/App.php 進(jìn)行設(shè)置:

public $defaultLocale = 'en';

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

如果不能找到絕對匹配的語言代碼時(shí),該系統(tǒng)將足夠靈活地使用更為泛化的語言代碼。 如果地區(qū)代碼被設(shè)為 en-US ,而只有 en 語言的語言文件,那么因?yàn)闆]有更為精確地匹配 en-US 的語言,我們就會使用這些語言文件。 但是如果有一個(gè)語言文件目錄 app/Language/en-US 存在的話,該目錄里的語言文件就會被首先使用。

地區(qū)識別?

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

內(nèi)容協(xié)商?

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

public $negotiateLocale = true;

當(dāng)該參數(shù)啟用時(shí),系統(tǒng)會自動根據(jù)你在 $suppoertLocales 中定義的語言數(shù)組來協(xié)商使用正確的語言。 如果在你提供的語言和所請求的語言中中匹配不到的話,該數(shù)組的第一個(gè)成員就會被使用。在下例中,在不匹配時(shí), en 地區(qū)就會被使用:

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

在路由中?

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

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

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

注解

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

獲取當(dāng)前地區(qū)?

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

<?php namespace App\Controllers;

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

或者你也可以用 服務(wù)類 來獲取當(dāng)前的請求:

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

語言本土化?

創(chuàng)建語言文件?

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() 輔助函數(shù)從所有語言文件中獲取文本值,通過將文件名和語言鍵作為第一個(gè)參數(shù),以點(diǎn)號(.)分隔。 舉例來說,從 Errors 語言文件中加載 errorEmailMissing 字符串,你可以如下操作:

echo lang('Errors.errorEmailMissing');

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

參數(shù)替換?

注解

以下函數(shù)需要加載并啟用 intl 擴(kuò)展。如果該擴(kuò)展未加載,則不會進(jìn)行替換操作。 可參閱 Sitepoint.

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

// 語言文件, 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 ]);

通配符中的第一項(xiàng)對應(yīng)著數(shù)組的索引下標(biāo)(如果該下標(biāo)是數(shù)字格式的話):

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

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

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

顯然你可以實(shí)現(xiàn)比起數(shù)字替換更為高級的功能。根據(jù)標(biāo)準(zhǔn)庫 official ICU docs 所示,以下類型的數(shù)據(jù)可被替換:

  • numbers - 整數(shù),匯率,百分比
  • dates - 短,中,長,完整格式
  • time - 短,中,長,完整格式
  • spellout - 大寫數(shù)字 (例如 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 編碼格式以充分使用這一功能的特性,例如執(zhí)行條件替換,多元素替換等。以上兩者的鏈接都在上文中有所提及,希望可以可以幫助你充分利用這一特性。

確定地區(qū)?

為了在替換參數(shù)時(shí)顯式調(diào)用一個(gè)不同的地區(qū),你可以通過將地區(qū)作為 lang() 方法的第三個(gè)參數(shù)來實(shí)現(xiàn):

// 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');

嵌套數(shù)組?

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

// Language/en/Fruit.php

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

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

語言回滾?

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

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

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

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

信息翻譯?

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

你可以下載該倉庫并復(fù)制其中的 Language 目錄到你的 app 中。因?yàn)?App 命名空間映射到了你的 app 目錄,對應(yīng)的翻譯就會被自動使用。

不過更好的使用方式是在你的項(xiàng)目中使用 composer require codeigniter4/translations ,因?yàn)榉g目錄自動映射之后,這樣被翻譯過的信息就會自動被使用。