Phalcon Multi-lingual Support

การทำ Multi-lingual ใน Phalcon สามารถทำได้หลายวิธี ด้วย Framework ที่ยืดหยุ่น และไม่จำเป็นต้องมี Structure ตายตัว ทำให้เราสามารถออกแบบ File structure ได้เองตามใจชอบ ซึ่งในคู่มือ จะใช้ structure แบบนี้

app/messages/en.php  
app/messages/es.php  
app/messages/fr.php  
app/messages/zh.php  

แล้วเนื้อหาข้างในก็จะเป็น Array ประมาณนี้

<?php

//app/messages/en.php
$messages = array(
    "hi"      => "Hello",
    "bye"     => "Good Bye",
    "hi-name" => "Hello %name%",
    "song"    => "This song is %song%"
);

<?php

//app/messages/th.php
$messages = array(
    "hi"      => "สวัสดี",
    "bye"     => "ลาก่อน",
    "hi-name" => "สวัสดี %name%",
    "song"    => "เพลงนี้ชื่อ %song%"
);

ออกแบบ File Structure ใหม่ให้จัดการง่ายขึ้น

ปัญหาของวิธีข้างบนคือไฟล์การแปลภาษาจะใหญ่มาก และจัดการลำบาก เพราะทุกอย่าง มากองอยู่ที่เดียวกัน เราเลยจะจัดการ File structure ใหม่ให้ออกมาหน้าตาแบบนี้ app/messages/en/{Controller Name}.php

app/messages/en/main.php  
app/messages/en/index.php  
app/messages/en/signup.php

app/messages/th/main.php  
app/messages/th/index.php  
app/messages/th/signup.php  

Config

สร้าง config เพื่อระบุ path ที่อยู่ของไฟล์ภาษา ใน app/config/config.ini ที่เพิ่มเข้ามาคือ messageDir

[application]
controllersDir = ../app/controllers/  
modelsDir      = ../app/models/  
viewsDir       = ../app/views/  
pluginsDir     = ../app/plugins/  
libraryDir     = ../app/library/  
cacheDir       = ../app/cache/  
messageDir     = ../app/messages/  
baseUri        = /  

Controller

จากนั้นก็เขียนโค้ดที่ Controller เพื่ออ่านไฟล์แปลภาษา และส่งต่อให้ View โดยแก้ใขไฟล์ app/controllers/ControllerBase.php ให้ออกมาหน้าตาแบบนี้

<?php  
namespace Modules\Settings\Controllers;

use Phalcon\Mvc\Controller,  
    Phalcon\Translate\Adapter\NativeArray,
    Phalcon\Tag,
    Phalcon\Mvc\Url;

class ControllerBase extends Controller  
{

    protected function _getTransPath()
    {
        global $config;

        $translationPath = $config->application->messageDir;
        $language = $this->session->get("language");
        if (!$language) {
            $this->session->set("language", "en");
        }
        if ($language === 'th' || $language === 'en') {
            return $translationPath.$language;
        } else {
            return $translationPath.'en';
        }
    }

    /**
     * Loads a translation for the whole site
     */
    public function loadMainTrans()
    {
        $translationPath = $this->_getTransPath();
        require $translationPath."/main.php";

        //Return a translation object
        $mainTranslate = new NativeArray(array(
            "content" => $messages
        ));

        //Set $mt as main translation object
        $this->view->setVar("mt", $mainTranslate);
      }

      /**
       * Loads a translation for the active controller
       */
    public function loadCustomTrans($transFile)
    {
        $translationPath = $this->_getTransPath();
        require $translationPath.'/'.$transFile.'.php';

        //Return a translation object
        $controllerTranslate = new NativeArray(array(
            "content" => $messages
        ));

        //Set $t as controller's translation object
        $this->view->setVar("t", $controllerTranslate);
    }

    public function initialize()
    {
        Tag::prependTitle('Thaizoft: ');
        $this->loadMainTrans();
    }

}

จากโค้ดจะสังเกตได้ว่าขณะ initialize จะมีการเรียก loadMainTrans() เพื่ออ่านไฟล์แปลภาษาชื่อ main.php และกำหนดค่าให้ตัวแปรชื่อ mt ก่อนจะส่งต่อให้ View ซึ่งใน main.php เราวางแผนว่าจะเอาไว้เก็บการแปลภาษาที่เป็นคำพื้นฐานทั่วไป เช่น Edit, Delete, Create, Save

ส่วนเมธอด loadCustomTrans() จะทำงานคล้ายกัน แต่จะสามารถระบุไฟล์ที่ใช้อ่านเพื่อแปลภาษาได้ และกำหนดค่าให้ตัวแปรชื่อ t ก่อนจะส่งต่อให้ View เช่นกัน

ตัวอย่างการใช้งานเช่น

class PersontypeController extends ControllerBase  
{

    public function initialize()
    {
        Tag::setTitle('Person Type');
        $this->loadCustomTrans('persontype');
        parent::initialize();
    }

จะเห็นว่า $this->loadCustomTrans('persontype'); จะบอกให้ไปอ่าน NativeArray จาก app/messages/{xx}/persontype.php เพื่อกำหนดค่าให้ตัวแปร t

View

ตัวอย่างการใช้งานใน View แบบ phtml

<p><?php echo $t->_("hi"); ?></p>

<p><?php echo $t->_("hi-name", array("name" => "Pangpond")); ?></p>

<p><?php echo $mt->_("edit"); ?></p>  

จะได้ output ออกมาแบบนี้

Hello  
Hello Pangpond  
Edit  

เปลี่ยนภาษา

ต่อไปเราจะสร้าง Method ที่ใช้ในการเปลี่ยนภาษาไปมา ไว้ใน app/controller/IndexController.php

class IndexController extends ControllerBase  
{

    public function initialize()
    {
        $this->loadCustomTrans('index');
        parent::initialize();
    }

    public function indexAction()
    {
        $language = $this->session->get('language');
    }

    public function setLanguageAction($language='')
    {
        //Change the language, reload translations if needed
        if ($language == 'en' || $language == 'th') {
            $this->session->set('language', $language);
            $this->loadMainTrans();
            $this->loadCustomTrans('index');
        }

        //Go to the last place
        $referer = $this->request->getHTTPReferer();
        if (strpos($referer, $this->request->getHttpHost()."/")!==false) {
            return $this->response->setHeader("Location", $referer);
        } else {
            return $this->dispatcher->forward(array('controller' => 'index', 'action' => 'index'));
        }
    }

}

Action Method ที่เราใช้ในการเปลี่ยนภาษาชื่อ setLanguageAction() ซึ่งอยู่ใน IndexController การทำงานคือรับพารามิเตอร์เข้ามาและกำหนดค่าภาษาให้ Session ชื่อ language จากนั้นก็จะ redirect กลับไปที่ URL เดิมก่อนที่จะเรียกใช้ setLanguageAction

Router

เพื่อความสวยงามเราจะกำหนด Router ให้ setLanguageAction โดยสร้างไฟล์ app/config/routes.php ตามนี้

$router = new Phalcon\Mvc\Router();

$router->add("/set-language/{language:[a-z]+}", array(
    'controller' => 'index',
    'action' => 'setLanguage'
));

หากเราเรียก URL /set-language/en Phalcon Router ก็จะ mapping ให้ไปเรียกใช้ action ชื่อ setLanguage ใน controller ชื่อ index โดยส่งพารามิเตอร์เป็น en เข้าไป

Language Switcher

สร้างตัวเปลี่ยนภาษาไว้ใน View เพื่อเรียกใช้งาน setLanguageAction ผ่าน Router

<ul class="dropdown-menu pull-right">  
    <li>
        <?= $this->tag->linkTo("set-language/th", Phalcon\Tag::image("img/demo/flags/th.gif") . "<span>ภาษาไทย</span>") ?>
    </li>
    <li>
        <?= $this->tag->linkTo("set-language/en", Phalcon\Tag::image("img/demo/flags/us.gif") . "<span>English</span>") ?>
    </li>
</ul>  

จากด้านบนจะได้ HTML ออกมาประมาณนี้

<ul class="dropdown-menu pull-right">  
    <li>
        <a href="/set-language/th"><img src="/img/demo/flags/th.gif"><span>ภาษาไทย</span></a>                            
    </li>
    <li>
        <a href="/set-language/en"><img src="/img/demo/flags/us.gif"><span>English</span></a>                            
    </li>
</ul>