2012年4月19日星期四

XML I18N

不少電腦語言都各自有自己專用的 I18N (Internationalization) 寫法,例如
PHP 通常會用 gettext 或 intl
Java, JSP, Servlet 通常會用 Locale + ResourceBundle 配合 Properties file
另外還有各種 Framework 都各自有一套 I18N 寫法

但是若果由一種 I18N 寫法至另一種 I18N 寫法顯然是需要一點點時間學習……

大部分高階電腦語言都擁有 XML Parser 而且寫法都大同小異,因此在下便覺得用 XML 製作語言檔是一種不錯的選擇
再用非常直觀的 XML 語法編製語言檔,例如
en.xml
<?xml version="1.0" encoding="UTF-8"?>
<i18n lang="en">
    <text id="how-are-you"><![CDATA[How are you ?]]></text>
</i18n>
zh-tw.xml
<?xml version="1.0" encoding="UTF-8"?>
<i18n lang="zh-tw">
    <text id="how-are-you"><![CDATA[你好嗎?]]></text>
</i18n>
zh-cn.xml
<?xml version="1.0" encoding="UTF-8"?>
<i18n lang="zh-cn">
    <text id="how-are-you"><![CDATA[你好吗?]]></text>
</i18n>
語法顯而易見
利用 i18n 的 lang 定義語言檔的語言種類,雖然檔案名已經根據語言命名,但於檔案中再宣告多次比較準確
再利用 text 的 id 作為唯一鍵,以 id 屬性給 DOM 的 getElementById 使用,加快 XML Parser 的速度

(在 XML 中 & " 之類特殊符號,必須以 &#nnnnn; 或 &entity; 的寫法才可以編制
但透過 <![CDATA[]]> 這種語法可以讓 XML 直接以 & " 編製在 CDATA[] 中)

PHP
<?php
class XMLI18N{

    private $document;

    public function __construct($file){
        $document = new DOMDocument();
        $document->load($file);
        if (is_null($document) === true){
            throw new Exception();
        } else {
            $this->document = $document;
        }
    }

    public function getText($id){
        //$element = $this->document->getElementById($id);
        $xpath = new DOMXPath($this->document);
        $element = $xpath->query('//*[@id="' . $id . '"]')->item(0);
        if (is_null($element) === true){
            return null;
        } else {
            return $element->nodeValue;
        }
    }
}

$xmli18ns = array(new XMLI18N('en.xml'), new XMLI18N('zh-tw.xml'), new XMLI18N('zh-cn.xml'));
foreach ($xmli18ns as $xmli18n){
    echo $xmli18n->getText('how-are-you') . "<b‎r/>\n";
}
?>

Java
package xmli18n;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

public class XMLI18N{

    private Document document;

    public XMLI18N(String filename) throws IOException, ParserConfigurationException, SAXException{
        try{
            document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(filename));
            this.document = document;
        } catch (IOException ex){
            throw new IOException(ex.getMessage());
        } catch (ParserConfigurationException ex){
            throw new ParserConfigurationException(ex.getMessage());
        } catch (SAXException ex){
            throw new SAXException(ex.getMessage());
        }
    }

    public String getText(String id){
        Element element = document.getElementById(id);
        if (element == null){
            return null;
        } else {
            return element.getTextContent();
        }
    }

    public static void main(String[] args){
        try{
            XMLI18N[] xmli18ns = {new XMLI18N("en.xml"), new XMLI18N("zh-tw.xml"), new XMLI18N("zh-cn.xml")};
            for (XMLI18N xmli18n : xmli18ns){
                System.out.println(xmli18n.getText("how-are-you"));
            }
        } catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

其他如 C, C++, C#, ActionScript 有具有由 W3C 所提供的 XML DOM Parser,因此不一一詳盡編寫
重點是先編制任意一類別或函式庫等前置程式,方便從 XML 解釋語言檔

另外在下還建議設立一個 DTD 檔,好讓 XML 檔有一套統一語法,可讓使用者知道 XML 的編製格式

i18n.dtd
<!-- declare DOCTYPE named "i18n" -->
<!DOCTYPE i18n [
    <!-- the root element of this XML must named "i18n" contains 1 or more than 1 "text" elements -->
    <!ELEMENT i18n (text+)>
        <!-- the "i18n" element must have "lang" attribute which accept Character Data -->
        <!ATTLIST i18n lang CDATA #REQUIRED>
    <!-- "text" element accepts any objects in its body -->
    <!ELEMENT text (#PCDATA)>
        <!-- the "text" element must have "id" attribute which accept an unique element id -->
        <!ATTLIST text id ID #REQUIRED>
]>

再利用 DOCTYPE 引入 dtd 來規範 XML 的結構
<!DOCTYPE i18n SYSTEM "i18n.dtd">

沒有留言 :

發佈留言