หลักการทำงาน
Internationalization (I18N) เป็นการออกแบบอย่างหนึ่งในกระบวนการของซอร์ฟแวร์นั่นคือการประยุกต์การใช้งานภาษาต่างในแต่ละภูมิภาค โดยไม่มีการเปลี่ยนแปลงด้านวิศวกรรม ใน Web Application หลักการนี้มีความสำคัญเพราะผู้ใช้งานมาจากทั่วทุกมุมโลก ซึ่ง Yii ได้รองรับการทำงานนี้ เช่น การแปลง ข้อความ ใน message การแปลงข้อความใน view หรือ การจัดรูปแบบวันที่และตัวเลข เป็นต้น
http://www.yiiframework.com/doc-2.0/guide-tutorial-i18n.html
โจทย์ปัญหาและขั้นตอนของระบบ
เราต้องการให้ผู้ใช้เลือกภาษาและให้ Browser จำค่าของการเลือกไว้โดยใช้ Cookie ในการจดจำว่าผู้ใช้เลือกภาษาอะไรไว้ ถึงแม้ว่า ปิดและเปิด Web Application Web Application ก็จะเลือกภาษาที่ผู้ใช้เลือกให้ล่าสุดโดยอัตโนมัติโดยที่ผู้ใช้งานไม่ต้องเลือกภาษาใหม่
การสร้างไฟล์ config i18n และการตั้งค่า
ก่อนอื่นต้องทำการสร้าง file config ก่อนเพื่อเก็บข้อมูลการตั้งค่าต่างๆ ในการเปลี่ยนภาษาโดยพิมพ์คำสั่ง
yii message/config @frontend/config/i18n.php
เป็นการสร้างไฟล์เก็บข้อมูลการตั้งค่า i18n.php เก็บไว้ที่ frontend/config/i18n.php และทำการตั้งค่าดังนี้
<?php
return [
// string, required, root directory of all source files
'sourcePath' => __DIR__ . DIRECTORY_SEPARATOR . '..',
// array, required, list of language codes that the extracted messages
// should be translated to. For example, ['zh-CN', 'de'].
'languages' => ['th'],
// string, the name of the function for translating messages.
// Defaults to 'Yii::t'. This is used as a mark to find the messages to be
// translated. You may use a string for single function name or an array for
// multiple function names.
'translator' => 'Yii::t',
// boolean, whether to sort messages by keys when merging new messages
// with the existing ones. Defaults to false, which means the new (untranslated)
// messages will be separated from the old (translated) ones.
'sort' => false,
// boolean, whether to remove messages that no longer appear in the source code.
// Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.
'removeUnused' => false,
// array, list of patterns that specify which files (not directories) should be processed.
// If empty or not set, all files will be processed.
// Please refer to "except" for details about the patterns.
'only' => ['*.php'],
// array, list of patterns that specify which files/directories should NOT be processed.
// If empty or not set, all files/directories will be processed.
// A path matches a pattern if it contains the pattern string at its end. For example,
// '/a/b' will match all files and directories ending with '/a/b';
// the '*.svn' will match all files and directories whose name ends with '.svn'.
// and the '.svn' will match all files and directories named exactly '.svn'.
// Note, the '/' characters in a pattern matches both '/' and '\'.
// See helpers/FileHelper::findFiles() description for more details on pattern matching rules.
// If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
'except' => [
'.svn',
'.git',
'.gitignore',
'.gitkeep',
'.hgignore',
'.hgkeep',
'/messages',
],
// 'php' output format is for saving messages to php files.
'format' => 'php',
// Root directory containing message translations.
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR . 'messages',
// boolean, whether the message file should be overwritten with the merged messages
'overwrite' => true,
];
จะเห็นว่ามีส่วนที่สำคัญคือ
languages เป็น array ซึ่งสามารถใส่ได้หลายภาษา
'translator' => 'Yii::t' คือตัวที่จะทำการ Translate จาก SourceCode ใน Model และ View
messagePath เป็น path ที่เก็บ messages ต่างๆ ในที่นี้จะอยู่ใน frontend/messages จึงต้องให้ระบบ path จากไฟล์ frontend/config/i18n.php ให้ไปที่ folder messages
กำหนดข้อความที่ต้องการทำหลายภาษาทั้งใน Model และ View
ตัวอย่าง Model ใน Method attributeLabels()
ใน Model นั้นตอน Generate model ด้วย Gii Generator สามารถเลือกได้ว่าใช้ i18n ในการกำหนด Attribute Label ในที่นี้จะใช้ตัว Translator คือ Yii::t() จากการตั้งค่าใน frontend/config/i18n.php
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'user_id' => Yii::t('app', 'User ID'),
'title' => Yii::t('app', 'Title'),
'price' => Yii::t('app', 'Price'),
'currency_id' => Yii::t('app', 'Currency'),
'description' => Yii::t('app', 'Description'),
'property_type_id' => Yii::t('app', 'Property Type ID'),
'bed' => Yii::t('app', 'Beds'),
'bath' => Yii::t('app', 'Baths'),
'garage' => Yii::t('app', 'Garages'),
'area' => Yii::t('app', 'Area'),
'address' => Yii::t('app', 'Address'),
'latitude' => Yii::t('app', 'Latitude'),
'longitude' => Yii::t('app', 'Longitude'),
'hit' => Yii::t('app', 'Hit'),
'is_rating' => Yii::t('app', 'Allow Rating'),
'property_status' => Yii::t('app', 'Property Status'),
'status' => Yii::t('app', 'Status'),
'created_at' => Yii::t('app', 'Created At'),
'updated_at' => Yii::t('app', 'Updated At'),
'imageFiles' => Yii::t('app', 'Photo File(s)'),
'images' => Yii::t('app', 'Photo File(s)'),
];
}
ตัวอย่าง View
ในตอนที่ Generate CRUD ก็สามารถเลือกได้ว่าต้องการใช้ i18n ในการกำหนดข้อความต่างๆ Translator คือ Yii::t() จากการตั้งค่าใน frontend/config/i18n.php
<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ListView;
/* @var $this yii\web\View */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Properties');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="row">
<!-- Results -->
<div class="col-md-9 col-sm-9">
<section id="results">
<header><h1><?=$this->title?></h1></header>
<section id="search-filter">
<figure>
<h3><i class="fa fa-search"></i><?=Yii::t('app', 'Search Results')?>:</h3>
<span class="search-count"><?=$count_item?></span>
<div class="sorting">
<div class="form-group">
<select name="sorting">
<option value=""><?=Yii::t('app', 'Sort By')?></option>
<option value="3"><?=Yii::t('app', 'Sell')?></option>
<option value="3"><?=Yii::t('app', 'Rent')?></option>
<option value="1"><?=Yii::t('app', 'Lowest price first')?></option>
<option value="2"><?=Yii::t('app', 'Highest price first')?></option>
<option value="3"><?=Yii::t('app', 'Date added')?></option>
</select>
</div><!-- /.form-group -->
</div>
</figure>
</section>
<section id="properties" class="display-lines">
<?= ListView::widget([
'dataProvider' => $dataProvider,
'layout' => "{items}<div class=\"center\">{pager}</div>",
'itemView' => '_property'
]); ?>
<section id="advertising">
<a href="<?=Url::to(['/property/create'])?>">
<div class="banner">
<div class="wrapper">
<span class="title"><?=Yii::t('app', 'Do you want your property to be listed here?')?></span>
<span class="submit"><?=Yii::t('app', 'Submit it now!')?> <i class="fa fa-plus-square"></i></span>
</div>
</div><!-- /.banner-->
</a>
</section><!-- /#adveritsing-->
</section><!-- /#properties-->
</section><!-- /#results -->
</div><!-- /.col-md-9 -->
<!-- end Results -->
<!-- sidebar -->
<div class="col-md-3 col-sm-3">
<section id="sidebar">
<?=$this->render('//layouts/search_box')?>
<?= $this->render(
'//layouts/feature_box'
) ?>
<?= $this->render(
'//layouts/guide_box'
) ?>
</section><!-- /#sidebar -->
</div><!-- /.col-md-3 -->
<!-- end Sidebar -->
</div><!-- /.row -->
Extract ภาษาที่ต้องการแปล
ทำการ Extract ภาษาไปเก็บไว้ที่ frontend/messages ด้วยคำสั่ง
yii message/extract @frontend/config/i18n.php
จะเห็นว่ามี ไฟล์เกิดขึ้นใน frontend/messages/th/app.php และให้ทำการแปล ฝั่งซ้ายคือ key index ฝั่งขวาคือข้อความที่ต้องการแปล ลักษณะดังนี้
<?php
/**
* Message translations.
*
* This file is automatically generated by 'yii message' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
* Each array element represents the translation (value) of a message (key).
* If the value is empty, the message is considered as not translated.
* Messages that no longer need translation will have their translations
* enclosed between a pair of '@@' marks.
*
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
'Real Estate' => 'อสังหาริมทรัพย์',
'SellRent.co is a bigest' => 'SellRent.co แหล่งรวม',
'Skype' => '',
'Sorry, we are unable to reset password for email provided.' => '',
'Update {modelClass}: ' => '',
'User with the same email as in {client} account already exists but isn\'t linked to it. Login using email first to link it.' => '',
' or this account is confirmed' => ' หรือบัญชีนี้ยืนยันแล้ว',
' success' => ' สำเร็จ',
'10 Jangsanit 17 alley, Jangsanit Road, Naimueng subdistrict, Muengubonratchathani district, Ubonratchathani 34000' => '10 ซอยแจ้งสนิท 17 ถนนแจ้งสนิท ตำบลในเมือง อำเภอเมืองอุบลราชธานี จังหวัดอุบลราชธานี 34000',
'About Me' => 'ข้อมูลเกี่ยวกับฉัน',
'About Us' => 'เกี่ยวกับเรา',
'Account' => 'ข้อมูลส่วนตัว',
'Add to bookmark' => 'เก็บไว้ดู',
'Add your Property' => 'เพิ่มอสังหาริมทรัพย์ของคุณ',
'Address' => 'ที่อยู่/ที่ตั้ง',
'Agencies' => 'บริษัทเอเจนซี่',
'Agency' => 'บริษัทเอเจนซี่',
'Agents' => 'ตัวแทน',
'Agents Listing' => 'รายการตัวแทน',
'All Property' => 'อสังหาริมทรัพย์ทั้งหมด',
'Allow Rating' => 'อนุญาตให้โหวต',
'Area' => 'พื้นที่',
'Auth key is wrong please contact contact@sellrent.co' => 'คีย์ Autn ไม่ถูกต้องกรุณาติดต่อ contact@sellrent.co',
'Baths' => 'ห้องน้ำ',
'Beds' => 'ห้องนอน',
'Body' => 'ข้อความ',
'Bookmarked Properties' => 'อสังหาริมทรัพย์ที่เก็บไว้',
'By clicking the “Create Agency” button you agree with our' => 'เมื่อกดปุ่ม “เพิ่มข้อมูล” แสดงว่าคุณได้ยอมรับ',
'Cannot confirm your account please contact contact@sellrent.co' => 'ไม่สามารถยืนยันบัญชีของคุณได้กรุณาติดต่อ contact@sellrent.co',
'Cannot find email: ' => 'ไม่พบอีเมล์:',
'Change Password' => 'เปลี่ยนรหัสผ่าน',
'Change Password Success' => 'เปลี่ยนรหัสผ่านเรียบร้อยแล้ว',
'Change Your Password' => 'เปลี่ยนรหัสผ่านของคุณ',
'Check your email for further instructions.' => 'ตรวจสอบอีเมลล์เพื่อดูรายละเอียด',
'Confirm Account' => 'ยืนยันบัญชี',
'Confirm New Password' => 'ยืนยันรหัสผ่านใหม่',
'Contact' => 'ติดต่อ',
'Contact Agent' => 'ติดต่อตัวแทน',
'Contact Info' => 'ข้อมูลการติดต่อ',
'Contact Information' => 'ข้อมูลการติดต่อ',
'Contact Us' => 'ติดต่อเรา',
'Create' => 'เพิ่มข้อมูล',
'Create Agency' => 'เพิ่มบริษัท',
'Create Agency Profile' => 'เพิ่มบริษัทเอเจนซี่',
'Create agency free' => 'เพิ่มบริษัทเอเจนซี่ฟรี',
'Create an Account' => 'สมัครใช้งาน',
'Create and manage your agency free' => 'คุณสามารถเพิ่มและจัดการบริษัทเอเจนซี่ของคุณฟรี',
'Created At' => 'เพิ่มเมื่อ',
'Currency' => 'สกุลเงิน',
'Current Password' => 'รหัสผ่านปัจจุบัน',
'Current Password not correct' => 'รหัสปัจจุบันไม่ถูกต้อง',
'Date added' => 'วันที่เพิ่ม',
'Delete' => 'ลบข้อมูล',
'Description' => 'รายละเอียด',
'Do you want your property to be listed here?' => 'ต้องการเพิ่มอสังหาริมทรัพย์ของคุณที่นี่ไหม?',
'Easy to contact your customer' => 'ง่ายต่อการติดต่อกับลูกค้า',
'Email' => 'อีเมล์',
'FAQ' => 'คำถามที่ถามบ่อย',
'Featured Properties' => 'อสังหาริมทรัพย์ที่น่าสนใจ',
'Find My Position' => 'ค้นหาตำแหน่งของฉัน',
'Forgot your password?' => 'ลืมรหัสผ่าน?',
'Frequency Ask Questions' => 'คำถามที่ถามบ่อย',
'Fullname' => 'ชื่อ-นามสกุล',
'Garages' => 'โรงจอดรถ',
'HanumanIT Co., Ltd.' => 'บริษัท หนุมานไอที จำกัด',
'Highest price first' => 'เริ่มจากราคาสูง',
'Hint' => 'บอกใบ้',
'Hit' => 'ดู',
'Home' => 'หน้าหลัก',
'How many to add properties in this platform?' => 'สามารถเพิ่มอสังหาริมทรัพย์ในแพลตฟอร์มนี้ได้เท่าไร',
'Last Update Properties' => 'อสังหาริมทรัพย์ล่าสุด',
'Login' => 'เข้าสู่ระบบ',
'Login or Signup' => 'เข้าสู่ระบบหรือสมัครใช้บริการ',
'Logo Slogan' => 'ซื้อ ขาย ให้เช่า อสังหาริมทรัพย์ฟรี',
'Logout' => 'ออกจากระบบ',
'Logout Success' => 'ออกจากระบบเรียบร้อยแล้ว',
'Lowest price first' => 'เริ่มจากราคาต่ำ',
'Map' => 'แผนที่',
'Mobile' => 'มือถือ',
'My Agency' => 'เอเจนซี่ของฉัน',
'My Properties' => 'อสังหาริมทรัพย์ของฉัน',
'My Social Network' => 'โซเชียลของฉัน',
'My Social Profiles' => 'ติดต่อผ่านโซเชียล',
'Name' => 'ชื่อ',
'New Password' => 'รหัสผ่านใหม่',
'New Password and Confirm Password not equal' => 'รหัสผ่านใหม่และยืนยันรหัสผ่านใหม่ไม่ตรงกัน',
'No' => 'ไม่',
'Our Agents' => 'ตัวแทนของเรา',
'Our Guides' => 'คำแนะนำ',
'Our Properties' => 'อสังหาริมทรัพย์ของเรา',
'Password' => 'รหัสผ่าน',
'Password reset' => 'รีเซตรหัสผ่าน',
'Phone' => 'โทร',
'Price' => 'ราคา',
'Privacy Policy' => 'นโยบายความเป็นส่วนตัว',
'Profile' => 'โพรไฟล์',
'Properties' => 'อสังหาริมทรัพย์',
'Property' => 'อสังหาริมทรัพย์',
'Property Description' => 'รายละเอียดอสังหาริมทรัพย์',
'Property Status' => 'สถานะ',
'Property Type' => 'ประเภท',
'Property Type ID' => 'ประเภท',
'Q' => 'ถาม',
'Quick Summary' => 'รายละเอียด',
'Random Properties' => 'สุ่มอสังหาริมทรัพย์',
'Rating' => 'โหวต',
'Read More' => 'เพิ่มเติม',
'Receive Newsletter' => 'รับข่าวสาร',
'Recent Properties' => 'อสังหาริมทรัพย์ล่าสุด',
'Rent' => 'เช่า',
'Reset' => 'รีเซต',
'Save Changes' => 'บันทึกการเปลี่ยนแปลง',
'Search' => 'ค้นหา',
'Search Properties' => 'ค้นหาอสังหาริมทรัพย์',
'Search Results' => 'ผลการค้นหา',
'Sell' => 'ขาย',
'Sellrent.co your signup from social network' => 'Sellrent.co คุณได้ลงทะเบียนจาก Social Network',
'Send Us a Message' => 'ส่งข้อความถึงเรา',
'Send a Message' => 'ส่งข้อความ',
'Send email to ' => 'ส่งอีเมล์ไปยัง',
'Services' => 'บริการของเรา',
'Shortly About Me' => 'เกี่ยวกับฉัน',
'Shortly About Us' => 'ข้อมูลเกี่ยวกับเรา',
'Signup' => 'สมัครใช้งาน',
'Signup and Login from social network success.' => 'สมัครใช้งานและเข้าระบบจาก Social Network เรียบร้อยแล้ว',
'Signup success please check your email to confirm your account' => 'สมัครใช้งานสำเร็จแล้วกรุณาตรวจสอบอีเมล์เพื่อยืนยันบัญชีของคุณ',
'Similar Properties' => 'อสังหาริมทรัพย์ที่คล้ายกัน',
'Social Profiles' => 'โซเชียลโพรไฟล์',
'Sort By' => 'เรียงลำดับโดย',
'Subject' => 'เรื่อง',
'Submit it now!' => 'เพิ่มตอนนี้เลย!',
'Tel' => 'โทร.',
'Terms and Conditions' => 'ข้อตกลงและเงื่อนไขการใช้งาน',
'Title' => 'อสังหาริมทรัพย์',
'Unlimit your properties' => 'เพิ่มอสังหาริมทรัพย์ได้ไม่จำกัด',
'Update' => 'ปรับปรุงข้อมูล',
'Updated At' => 'ปรับปรุงเมื่อ',
'Useful Links' => 'ลิงค์ที่น่าสนใจ',
'View All' => 'ดูทั้งหมด',
'Was this answer helpful?' => 'คำตอบนี้ช่วยได้ไหม?',
'We make platform for your customer to contact agent and agency' => 'เราสร้างแพลตฟอร์มที่ทำให้ลูกค้าของคุณติดต่อคุณได้อยางง่ายดาย',
'Website' => 'เว็บไซต์',
'Welcome back' => 'ยินดีต้อนรับอีกครั้ง',
'Where We Are' => 'ที่ตั้ง',
'Yes' => 'ใช่',
'You can add unlimit your properties' => 'คุณสามารถเพิ่มอสังหาริมทรัพย์ได้อย่างไม่จำกัด',
'You can add unlimit your properties in this platform and free service' => 'คุณสามารถเพิ่มอสังหาริมทรัพย์ของคุณในแพลตฟอร์มนี้ได้อยางไม่จำกัดและที่สำคัญเราให้บริการฟรี',
'You can change password in this section' => 'คุณสามารถเปลี่ยนรหัสผ่านในส่วนนี้',
'You can login at :' => 'คุณสามารถเข้าระบบได้ที่ :',
'Your Account' => 'ข้อมูลส่วนตัว',
'Your account : ' => 'บัญชีของคุณ : ',
'Your account confirmed' => 'บัญชีของคุณได้รับการยืนยันแล้ว',
'Your password : ' => 'รหัสผ่านของคุณ : ',
'we make easy to sell and rent properties. Of course it\'s free!' => 'เว็บไซต์สำหรับประกาศขาย-ให้เช่าอสังหาริมทรัพย์ ที่ช่วยให้อสังหาริมทรัพย์ของคุณเข้าถึงได้ง่ายดายมากยิ่งขึ้น ด้วยระบบจัดเก็บ แสดงผล และค้นหาที่มีประสิทธิภาพ และที่สำคัญเราให้บริการฟรี!',
];
หมายเหตุ หากมีการเพิ่มการแปลในภายหลัง สามารถเพิ่มได้ที่ source Model หรือ View ได้เลย แล้วเรียกใช้คำสั่ง extract อีกครั้ง Yii จะนำมาใส่ในไฟล์ให้อัตโนมัติ
แนะนำ อย่าเพิ่มในไฟล์ app.php เอง ให้กระทำผ่านคำสั่ง yii message/extract เท่านั้น
สร้างปุ่มเปลี่ยนภาษา
ทำการสร้างปุ่มเปลี่ยนภาษาใน layout หลัก ในที่นี้จะให้เป็นรูปภาพ และเมื่อเลือกภาษาไหน ให้ activate ภาษานั้น
<div class="language-bar">
<?php
echo Html::a(Html::img($directoryAsset.'/img/flags/th.png'), Url::current(['language' => 'th-TH']), ['class' => (Yii::$app->request->cookies['language']=='th-TH' ? 'active' : '')]);
echo Html::a(Html::img($directoryAsset.'/img/flags/us.png'), Url::current(['language' => 'en-US']), ['class' => (Yii::$app->request->cookies['language']=='en-US' ? 'active' : '')]);
?>
</div>
สร้างไฟล์ LanguageSelector.php
สร้างไฟล์ common/components/LanguageSelector.php โดยเขียนโปรแกรมดังนี้
<?php
namespace common\components;
use yii\base\BootstrapInterface;
use yii\web\Cookie;
use yii\base\Exception;
class LanguageSelector implements BootstrapInterface
{
public $supportedLanguages = [];
public function bootstrap($app)
{
$cookies = $app->response->cookies; //กำหนด cookie
$languageNew = $app->request->get('language'); //ตรวจสอบว่ามีการ request language หรือเปล่า
if($languageNew !== null)
{
if (!in_array($languageNew, $this->supportedLanguages)) { //ตรวจสอบว่า language ที่ส่งมาตรงกับที่ตั้งค่าไว้หรือเปล่า
throw new Exception('Invalid your selected language.'); //ถ้าไม่มี language ในรายการก็ exception
}
$cookies->add(new Cookie([
'name' => 'language',
'value' => $languageNew,
'expire' => time() + 60 * 60 * 24 * 30, // 30 days
])); //สร้าง cookie language ใหม่ให้มีระยะเวลา 30 วัน ตรงนี้ตั้งค่าได้ตามต้องการ
$app->language = $languageNew; //กำหนดค่าภาษาให้กับ app หลัก
//echo 'test1';
}
else
{
$preferedLanguage = isset($app->request->cookies['language']) ? (string) $app->request->cookies['language'] : 'th-TH'; //หากยังไม่ได้เลือกภาษาให้เป็นภาษาไทยก่อน
if(empty($preferedLanguage))
{
$preferedLanguage = $app->request->getPreferedLanguage($this->supportedLanguages); //หากยังไม่เลือกภาษา ให้ตรวจสอบว่าอยู่ในรายการหรือเปล่า
}
$app->language = $preferedLanguage; //กำหนดภาษาเริ่มต้นให้ app ในที่นี้คือ th-TH
}
}
}
ตั้งค่าให้ทำงานอัตโนมัติเมื่อมีการเลือกภาษา
ตั้งค่าให้ทำงานทันทีเมื่อมีการเลือกภาษา โดยโหลดจาก Class LanguageSelector ที่พึ่งสร้างขึ้น โดยกำหนดใน frontend/config/main.php ในส่วนของ Bootstrap
'bootstrap' => ['log',
[
'class' => 'common\components\LanguageSelector',
'supportedLanguages' => ['en-US', 'th-TH'], //กำหนดรายการภาษาที่ support หรือใช้ได้
]
],
ทดสอบการเปลี่ยนภาษา
ความคิดเห็น