Friday, November 04, 2005

ri18n

คุณยุทธถามมาเรื่อง ruby module ri18n
ที่ใช้ทำ localization
ได้เรื่องเลย เรื่องนี้เป็นเรื่องที่ผม postpone มานานแล้ว
เห็นทำ app ภาษาไทยได้ก็พอใจแล้ว ยังไม่ได้มองกรณีทำหลายภาษาเลย

หลังจากสั่ง

gem install ri18n


ก็ได้ source code มาจำนวนหนึ่ง
ตัวเอกสารไม่ต้องพูดถึง มีน้อยมาก
การใช้งานพึงแกะจาก source code เป็นหลัก

Note: ผมแกะมาพอให้เริ่มใช้งานได้นะครับ
ยังมีรายละเอียดอีกจำนวนหนึ่งที่ยังไม่ได้ดู
เช่น catalog, interpolation, phurals form


การใช้งาน ri18n กับ RoR
  • Environment Setup
    แก้ไข file config/environment.rb
    เพิ่มเนื้อหาส่วนนี้ลงไป
    # Include your app's configuration here:
    require 'i18nservice'

    I18nService.instance.po_dir="#{RAILS_ROOT}/i18n"

    ให้สังเกตว่าเราจะมี directory ที่ชื่อ i18n ที่จะไว้ใช้เก็บ
    po file (translation file)
    ข้อสังเกตอีกอัน ก็คือ I18nService ใช้ sigleton pattern
    ดังนั้นเราจึง access ผ่าน instance attribute

  • การแสดง message ใน views
    ในการ render output ใน rhtml
    แทนที่จะเขียนออกไปตรงๆ เราจะใช้สัญญลักษณ์ _('msgid') แทนที่ msg ที่เราต้องการ
    <html>
    <head>
    <title>hello</title>
    </head>
    <body>
    <p>
    <%= _('hello') %>
    </p>
    </body>
    </html>


  • เพิ่ม Task i18n ใน Rakefile
    โดย ri18n ได้เตรียม Rake Task ที่ชื่อ gettext ไว้แล้ว
    gettext นี้มีหน้าที่ scan file เพื่อหา msg ที่ต้องการทำ translation
    เพื่อทำการ genereate .po file ให้เรา
    ...
    require 'gettext'
    require 'i18nservice'

    ...
    RAILS_ROOT=File.dirname(__FILE__)
    ...

    desc "generate po (i18n) file"
    Rake::GettextTask.new { |gettext|
    gettext.source_files = FileList['**/*.rhtml']
    gettext.new_langs = ['th','en']
    I18nService.instance.po_dir="#{RAILS_ROOT}/i18n"
    }

    desc "create directory for i18n po file"
    task :mki18ndir do
    Dir.mkdir("i18n") unless File.exists?("i18n")
    end

    task :i18n => [:mki18ndir, :gettext]

    จะเห็นได้ว่าเรามีการ config task class ที่ชื่อ Rake::GettextTask
    โดยระบุว่าให้ scan message ที่ต้องการแปลงจาก file ที่มีนามสกุลเป็น rhtml
    และระบุด้วยว่า ให้สร้าง po file สำหรับ language 2 ภาษา ก็คือ thai (th) กับ english (en)
    Note: ผมทำแบบ struts ก็คือ ใน views ระบุเป็น message id
    เราสามารถทำอีกแบบได้ ก็คือ ใน views เป็น english แล้วทำเฉพาะส่วนแปลเป็น th


  • generate po file
    โดยใช้คำสั่ง rake i18n
    จะได้ file ออกมา 2 file ก็คือ th.po กับ en.po

  • ทำการแปล
    โดยการ add message ที่ต้องการลงไปใน po file
    ขั้นนี้ผมยังไม่ได้ลองเรื่อง encoding เลย
    ที่ทดลองทำก็คือแค่ใช้ editor ที่ save encoding เป็น utf-8 ได้
    ทำการ edit file เพิ่มเนื้อหา ภาษาไทย ลงใน file th.po
    ส่วน file en.po ก็เพิ่มส่วน ภาษาอังกฤษ ลงไป
    ตัวอย่าง file th.po
    Ôªø# SOME DESCRIPTIVE TITLE.
    # Copyright (C) YEAR Free Software Foundation, Inc.
    # This file is distributed under the same license as the PACKAGE package.
    # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
    #
    #, fuzzy
    msgid ""
    msgstr ""
    "Project-Id-Version: PACKAGE VERSION\n"
    "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    "POT-Creation-Date: 2002-12-10 22:11+0100\n"
    "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    "Language-Team: LANGUAGE <LL@li.org>\n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=ASCII\n"
    "Content-Transfer-Encoding: 8bit\n"
    "Plural-Forms: nplurals=2; plural=n == 0;\n"

    msgid "hello"
    msgstr "สวัสดีครับ"

    Note: ผมยังไม่ได้ดูเรื่อง header ใน po file เลย
    ใช้ค่า default ไปก่อน (ซึ่งมันก็ทำงานได้)


  • การเลือกใช้ language
    เราสามารถเลือกใช้ language ที่ต้องการได้โดย
    การสั่ง I18nService.instance.lang='th'
    โดยในตัวอย่างนี้ผมใส่ไว้ใน controller โดย check ว่ามี
    parameter ที่ชื่อ lang pass มาหรือไม่ ถ้าไม่มีก็ให้ default เป็น thai
    class MainController < ApplicationController
    def index
    lang = params[:lang]
    lang = "th" if lang == nil
    I18nService.instance.lang=lang
    end
    end



ทำเสร็จแล้ว ก็ start server ทดสอบได้เลย
ผลลัพท์ที่ได้ก็เป็นที่น่าพอใจ


ส่วนประเด็นที่เหลือ ก็อาจจะเป็นการเรื่องการจัดการ po file
กรณีที่มีการแก้ไขเพิ่มเติม views แล้วเราสามารถ regenerate เป็นแบบ partial
ได้ไหม (อันนี้ยังไม่ได้ลองแกะดู แต่เท่าที่ลองสั่งมันมี message แปลก display ขึ้นมา)

Related link from Roti

2 comments:

Anonymous said...

ตอบเท่าที่ตอบได้นะครับ

- เรื่อง partial gen นี่ไม่แน่ใจ ไม่ตอบดีกว่า
- header โดยทั่วไปแล้วใช้อ้างอิงเฉยๆ โดยเฉพาะในโครงการที่คนจำนวนมากๆ เข้ามาช่วยกันแปล อันที่จำเป็นและควรใส่ คือ revision date กับ last translator ส่วนเรื่อง plural นี่ภาษาไทยจะไม่ใช้ครับ (เช่น item, items)
- เรื่อง encoding เข้าใจว่าต้องเป็น utf-8 นะ (ไม่ชัวร์เหมือนกัน)​แต่ editor หลายตัวสามารถแปลง tis-620 <-> utf-8 ได้ในตัวอยู่แล้ว หรือใช้ iconv ช่วยก็ไม่มีปัญหา
- แนะนำ editor สำหรับ po ใช้ kbabel บน kde เข้าท่าที่สุด โดยเฉพาะการแปลข้อความจำนวนมากๆ การเลื่อนคำจะง่ายกว่า gtranslator ไม่ควรอย่างยิ่งที่จะใช้ text editor ธรรมดาแปล เพราะจะมีปัญหาเรื่อง syntax error ได้ง่ายมากๆ

ส่วนเรื่องการแปลโดยตรง แนะนำ l10n.opentle.org ครับ

PPhetra said...

ขอบคุณ mk ที่ให้คำแนะนำเพิ่มเติมครับ