วันพฤหัสบดีที่ 1 กันยายน พ.ศ. 2554

การใช้ภาษาไทย กับ java web application (servlet, jsp)

การใช้ภาษาไทย กับ java web application (servlet, jsp)
การใช้งาน java web app กับภาษาไทย มักจะมีปัญหา เรื่องไม่แสดงผลตามที่ต้องการ หรือใส่ข้อมูลหน้าเว็บ เป็นภาษาไทย แต่เมื่อ submit แล้ว กลับแสดงผลออกมาเป็นตัวอะไรก็ไม่รู้ ดังนั้นผมจึงได้รวบรวมข้อมูล และเสนอวิธีการจัดการเกี่ยวกับ การแสดงผลภาษาไทย และภาษาอื่น ๆ ของ application ที่ทำงานบน java web app container ดังนี้


รู้จัก Character set Encoding (ขอเรียกสั้น ๆ ว่า charset) ที่ใช้กับภาษาไทย
เนื่องจากปัจจุบันการเก็บข้อมูลของ character นั้นเป็นแบบ multibyte character หรือก็คือ 1 ตัวอักษร ใช้มากกว่า 1 byte ในการเก็บข้อมูล ซึ่งไม่เหมือนในสมัยก่อนที่ character 1 ตัวอักษรมีขนาดข้อมูลเท่ากับ 1 byte แต่การเก็บข้อมูลแบบ 1 character ต่อ 1 byte ก็ยังคงใช้อยู่ ดังนั้น ใน OS ที่รองรับการแสดงผลหลายภาษา จะมีการเข้ารหัสข้อมูลเพื่อให้รหัสตัวอักษรแต่ละตัว ตรงกับ ตารางตัวอักษรมาตรฐาน ของแต่ละภาษาก่อน ซึ่ง charset ที่ใช้กับภาษาไทยได้ มีดังนี้

TIS-620
WINDOWS-874


ส่วน charset ที่สามารถรองรับได้หลายภาษาพร้อมกันนั้น เรามักจะใช้ UTF-8 นั้นหมายความว่า ถ้าในเพจ หน้าหนึ่งของเรา ต้องแสดงผลหลาย ๆ ภาษา ควรจะต้องใช้ charset UTF-8

ในการเขียนโปรแกรมภาษาจาวา โครงสร้างข้อมูลพื้นฐานที่เก็บตัวอักษร (char) จะเก็บเป็น unicode ซึ่งจะกินเนื้อที่ 2 ไบต์ ข้อมูลตัวอักษรใด ๆ ที่ใช้ char หรือ class String อ้างอิงเพื่อประมวลผล จะถูกแปลงให้อยู่ในโีครงสร้างข้อมูลดังกล่าวก่อน ดังนั้น และใน class String จะมี method byte[] getBytes(charset) ซึ่งใช้สำหรับ encoding string ให้อยู่ในลำดับของ byte ด้วย charset ที่กำหนด ซึ่ง method นี้จะมีประโยชน์มาก เมื่อเราต้องการเก็บข้อมูลตัวอักษร ลงฐานข้อมูลที่ใช้ charset ต่างกัน หรือแปลงข้อมูลอักษรลงไฟล์ เพื่อไปใช้ในระบบอื่นที่ต้องการ charset ต่างออกไป

String s = new String(byte[],charsetDecoder)

โดย charset decoding คือ ชื่อของ decoding ที่ต้องการถอดรหัส ซึ่งควรจะเป็นชื่อเดียวกับ charset ที่ encoding ไว้ มิฉะนั้นอาจจะได้ String ที่อ่านไม่ออก

เมื่อเราพอจะรู้เรื่องของ encoding บ้างแล้ว ก็มาเริ่มทำขั้นตอนต่อไปเลยดีกว่า ซึ่งขั้นตอนต่อไปนั้น ทำให้เราสามารถประหยัดเวลาในการเขียนโปรแกรมขึ้นมาก

การใช้ html meta tag
เมื่อเราเขียน servlet หรือ jsp มาถึงขึ้นตอนการเขียนโปรแกรมแสดงผล ในบรรทัดแรกเลยที่จะใช้คำสั่ง out.println หรือถ้าใน jsp ก็หมายถึงบรรทัดแรก ๆ เราควรจะให้ output meta tag ก่อน เพราะใน meta tag มี attribute ที่ให้เราสามารถกำหนด character set encoding ได้ ดังนี้

servlet
out.println("");

JSP

และควรกำหนดที่ page directive tag ของ jsp ด้วยดังนี้
<%@ page contentType="text/html;charset=UTF-8"%>

ผลของการกำหนด meta tag จะทำให้ browser ที่แสดงผลปลายทาง ทำการ decoding ข้อมูลที่ได้รับจาก web server ตาม charset ที่กำหนด และ ทำการ encoding ข้อมูลที่ได้รับจาก form ในหน้าเว็บ ส่งให้ web server

ดังนั้น ถ้าเราไม่กำหนด meta tag ในส่วนนี้ browser ก็จะใช้ default charset ของ browser แต่ละยี่ห้อทำการ encoding และ decoding ( ซึ่งจะทำให้เราไม่สามารถควบคุมได้ เพราะเราก็รับประกันไม่ได้ว่าผู้ใช้จะใช้ browser ยี่ห้อใดบ้าง หรือ ผู้ใช้ตั้งค่า charset ของ browser ไว้เองแล้วหรือไม่

หมายเหตุ ส่วนใหญ่ browser จะใช้ default charset เป็น ISO-8859-1

การตั้งค่า compile Encoding
คือการกำหนดค่าให้กับ java compiler และ jsp compiler ว่า source code ที่จะ compile ต้องการใช้ encoding อะไร ซึ่งการกำหนด compile Encodeing จะมีผลโดยตรงกับ String ที่เราใช้แสดงผลที่ hard code ไว้ใน source code ถ้าเรากำหนด compile Encoding ที่ไม่รองรับภาษาที่เราใช้ ผลที่แสดงออกมาอาจจะผิดพลาดได้ เราสามารถ ตั้งค่า compile Encoding ได้ดังนี้

Servlet
ถ้าเราใช้ IDE ก็ให้ตั้งค่า Text file encoding ใน IDE นั้น ๆ ได้เลย แต่ถ้าเราใช้ command line java compile (javac) ให้ใส่ option เข้าไปดังนี้

javac -encoding TIS-620 myservlet.java

JSP
กำหนดได้ที่ attribute pageEncoding ของ Page directive เพื่อให้ JSP compile รู้ได้ดังนี้

<%@ page pageEncoding="TIS-620" %>

การตั้งค่า Encoding ให้กับ class HttpServletRequest และ HttpServletResponse
จากที่กล่าวมาแล้วในข้อที่ 2 ดังนั้น servlet และ jsp ที่มีการนำข้อมูลที่ส่งผ่าน form จากหน้าเว็บมาประมวลผล จะต้อง set charecterEncoding ให้กับ instance ของ HttpServletRequest (ใน jsp ก็คือ request) โดยเราจะต้อง set charset ให้ตรงกับที่ตั้งไว้ใน html meta tag หรือ page directive contentType ซึ่งถ้าเราไม่ได้ set charset ให้ ค่า charset ปกติจะเป็น ISO-8859-1 ซึ่งเมื่อ charset ไม่ตรงกัน string ที่ได้ก็จะผิด ส่วน instance ของ HttpServletResponse (ใน jsp ก็คือ response) นั้น จะถูกตั้งค่าไว้ตาม page directive contentType หรือตาม charset ที่ webcontainer ตรวจสอบได้ หรือเราตั้งค่าเองไว้ใน code

ตัวอย่างการใช้งาน
request.setCharacterEncoding("UTF-8");
String st = request.getParameter("param1");

response.setCharacterEncoding("TIS-620");

เพื่อความสะดวกมากขึ้น ผู้ออกแบบโีครงสร้าง web app ของจาวาได้ออกแบบ servlet filter class ซึ่ง servlet filter จะช่วยให้เราปรับแต่งค่าของ HttpServletRequest และ HttpServletResponse ซึ่งเราสามารถใช้ servlet filter ตั้งค่า charset สำหรับ jsp และ servlet ทุก ๆ หน้าได้ ทำให้ไม่ต้องโค้ดคำสั่ง request.setCharacterEncoding บ่อย ๆ ซึ่งวิธีการสร้าง class และ ใช้งานทำได้ดังนี้

4.1 สร้าง class servlet filter โดย มีตัวอย่างดังนี้


import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class UTF8Filter implements Filter {

FilterConfig filterConfig;

public UTF8Filter(){
super();
}

public void init(FilterConfig arg0)
throws ServletException {
filterConfig = arg0;

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");

if (chain != null)
chain.doFilter(request, response);
}

public void destroy() {
filterConfig = null;
}

}


จากโค้ดข้างบนนี้ เป็น filter ที่ตั้งค่า charset ให้กับ ServletRequest กับ ServletResponse เป็น UTF-8

4.2 เมื่อ คีย์โค้ดข้างบนแล้ว เราก็ต้องเขียน descriptor code เพิ่มไปใน web.xml โดย วางตำแหน่งไว้ใน tag ดังนี้


<filter
>
<filter-name>UTF8Filter</filter-name>
<filter-class>web.filter.UTF8Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>UTF8Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

หากเราต้องการให้ filter มีผลกับเฉพาะบางไฟล์ ก็ให้เราไปแก้ไข descriptor ที่ tag url-pattern

ข้อมูลอ้างอิงจาก :

http://www.narisa.com/forums/index.php?showtopic=1800
http://java.sun.com/products/jsp/syntax/1.2/syntaxref12.html
และประสบการณ์ที่ผ่านมา
อ้างอิงจาก blogger:

http://buewhite-javatip.blogspot.com/2007/12/java-web-application-servlet-jsp.html