Thursday, June 21, 2007

Tapestry5 กับ ภาษาไทย

veer เปิดประเด็นปัญหานี้ขึ้นมา ผมก็เลยไปสรุปเพิ่มเติมมา แบ่งเป็น 3 ประเด็นคือ

update: คุณ pa แจ้งว่า มีการแก้ไข case นี้แล้ว
โดยกำหนดให้ set ค่า encoding ผ่านทาง META tag ใน Html template แทน
ซึ่งจะครอบคลุม case 1.2 และ 2

update(27/06/2007): ทดสอบการแก้ไขที่คุณ pa แจ้งแล้ว พบ bug Tapestry-1605


Note: เนื่องจาก T5 ยังอยู่ในช่วยพัฒนา
วิธีการที่ว่ามานี้ ในอนาคตคาดว่าจะมีการ implement กลไกที่ง่ายกว่านี้

1. ประเด็นแรกก็คือ ถ้าเราใส่ภาษาไทยลงไปใน template file ตรงๆ (แทนที่จะใช้กลไก localize ของ T5)
จะแสดงผลให้ถูกต้องได้อย่างไร

ประเด็นนี้ แยกเป็นประเด็นย่อยได้ 2 เรื่องคือ
1.1 ประเด็นการ parse template file ด้วย encoding ที่ถูกต้อง
ถ้าเราไปแกะ code ของ tapestry ดู จะเห็นว่า คนที่รับผิดชอบในการ
parse template ก็คือ TemplateParserImpl
ซึ่งภายในจะใช้กลไก org.xml.sax.helpers.XMLReader ในการ parse
ดังนั้น ปัญหาในจุดนี้ น่าจะแก้ได้โดยการระบุ encoding บนหัว template file

<?xml version="1.0" encoding="UTF-8"?>

Note: ผมไม่เจอปัญหานี้ เนื่องจาก enviroment ผม มี default locale เป็น UTF-8 อยู่แล้ว

1.2 ประเด็นการของ print output ออกไปยัง response stream
ด้วย encoding ที่ถูกต้อง
จากข้อ 1.1 หลังจากได้ character ที่ถูกต้องแล้ว
ก็ต้อง print ออกไปให้ถูก encoding ด้วย
ตรงนี้ เห็นมีพูดถึงใน mailing-list, เท่าที่ดูหลายๆอันแล้ว ผมชอบวิธีนี้มากสุด
โดยการ replace PageResponseRenderer ด้วยการใช้กลไก decorator
    public static PageResponseRenderer decoratePageResponseRenderer(
@InjectService("PageMarkupRenderer")
final PageMarkupRenderer markupRenderer,
@InjectService("MarkupWriterFactory")
final MarkupWriterFactory markupWriterFactory, final Object delegate)
{

return new PageResponseRenderer()
{
public void renderPageResponse(Page page, Response response) throws IOException
{
MarkupWriter writer = markupWriterFactory.newMarkupWriter();
markupRenderer.renderPageMarkup(page, writer);
PrintWriter pw = response.getPrintWriter("text/html; charset=UTF-8");
writer.toMarkup(pw);
pw.flush();
}
};
}
}


2. ประเด็นของ form input element ที่ submit เป็นภาษาไทย เข้ามา
ตรงนี้ เราต้องแก้ไขด้วยการใส่ filter เข้าไป set ค่า encoding ให้กับ servlet request ก่อน
โดยปกติเราจะใช้ servlet filter ช่วย set ให้ก็ได้
แต่วิธีที่ลงให้ดูนี้ จะใช้กลไกของ IOC container ของ tapestry มาจัดการให้แทน
    public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
@InjectService("TimingFilter")
RequestFilter filter,
@InjectService("Utf8Filter")
RequestFilter utf8Filter)
{
// Each contribution to an ordered configuration has a name, When necessary, you may
// set constraints to precisely control the invocation order of the contributed filter
// within the pipeline.

configuration.add("Timing", filter);
configuration.add("encoding", utf8Filter);
}

public RequestFilter buildUtf8Filter(
@InjectService("RequestGlobals")
final RequestGlobals requestGlobals ) {
return new RequestFilter() {
public boolean service( Request request, Response response, RequestHandler handler ) throws IOException {
requestGlobals.getHTTPServletRequest().setCharacterEncoding( "UTF-8" );
return handler.service( request, response );
}
};
}


3. ประเด็นกรณี uri ที่ submit เข้ามา, มีภาษาไทย encode เข้ามาด้วย
กรณีนี้ รู้สึกจะเป็นกับ Tomcat เจ้าเดียว
ให้ configure file server.xml เพิ่ม
URIEncoding="UTF-8"

Related link from Roti