ทดลอง Tesseract 4.0alpha กับภาษาไทย

Tesseract เป็นซอฟต์แวร์และไลบรารีแปลงภาพข้อความ (ที่คนอ่านเข้าใจ) ให้เป็นข้อความ (ที่คอมพิวเตอร์อ่านเข้าใจ) หรือที่เรียกกันว่า OCR

สาเหตุที่ Tesseract ได้รับความนิยม เพราะมันเป็นซอฟต์แวร์เสรี (free software ไม่ต้องจ่ายเงิน มีซอร์สโค้ดให้ดูและแก้ไขเผยแพร่ต่อได้ และประสิทธิภาพมันก็ดี จะเรียกใช้ตรงๆ ทาง command line ก็ได้ หรือจะเขียนโปรแกรมเชื่อมกับ API มันก็ได้ — ดู wrapper และ GUI อื่นๆ ได้ที่หน้า Add Ons ของโครงการ

ภาพที่จะส่งมา Tesseract ต้องเป็นภาพที่ปรับแต่งมาให้เหมาะกับการอ่านข้อความแล้ว คือหมุนมาค่อนข้างตรง และปรับแสงและสีให้อ่านง่าน พื้นหลังสีขาวหรือสีอ่อน ตัวอักษรสีดำ ใน StackOverflow มีคนอธิบายการใช้ OpenCV ปรับภาพเพื่อ OCR เอาไว้

Tesseract รองรับภาษาไทย (น่าจะตั้งแต่รุ่น 3) ตอนนี้รุ่น 4 กำลังจะออก เพิ่มเอนจินที่ใช้โมเดล Deep Learning แบบ LSTM เข้ามา เท่าที่ทีมพัฒนาทดสอบกันเอง มีข้อผิดพลาดน้อยกว่าเอนจินของรุ่นก่อน

โพสต์นี้จะพูดถึงการทดสอบรุ่น 4.0alpha บน macOS และทดลองเพิ่ม/ลดคำในรายคำศัพท์ที่ตัวเอนจิน LSTM จะเอาไปใช้

ส่วนใครจะใช้รุ่นที่ released แล้ว ติดตั้งด้วยวิธีปกติของแต่ละระบบปฏิบัติการได้นะครับ วิธีตามเอกสาร

ติดตั้งไลบรารีที่จำเป็น

brew install autoconf-archive leptonica icu4c pango

ไลบรารี icu4c (เอาไว้จัดการ Unicode) กับ pango (จัดการการวาดตัวอักษร) ติดตั้งเฉพาะถ้าเราต้องการฝึกโมเดลใหม่

อาจจะมีไลบรารีอื่นที่ต้องใช้เพิ่มเติม ลองอ่านที่ configure มันแจ้ง และติดตั้งตามที่มันบอกครับ

ICU ที่มากับ macOS ไม่มีไฟล์ header มาด้วย ดังนั้นใช้คอมไพล์ไม่ได้ ต้องลงใหม่ครับ

ตั้งค่า environment

ในไฟล์ ~/.bash_profile

export PATH="/usr/local/opt/icu4c/bin:$PATH"
export PATH="/usr/local/opt/icu4c/sbin:$PATH"
export LDFLAGS="-L/usr/local/opt/icu4c/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c/include"

อันนี้ผมใช้ brew ปกติของมันจะเอาไลบรารีและเฮดเดอร์ต่างๆ ไปไว้ที่ไดเรกทอรี /usr/local/opt/ ถ้าใครติดตั้งไว้ที่อื่นก็เปลี่ยนตามนั้นครับ

ดาวน์โหลดโค้ดและเตรียมคอมไพล์

โค้ด Tesseract ตัวล่าสุดอยู่ที่ https://github.com/tesseract-ocr/tesseract ก็ไปโคลนหรือฟอร์กมาได้เลย

จากนั้นในไดเรกทอรีของ tesseract เราก็สร้างไฟล์คอนฟิกเพื่อเตรียมคอมไพล์

./autogen.h

และ

./configure

จากนั้นก็คอมไพล์และติดตั้งตัว tesseract

make
make install

และคอมไพล์และติดตั้งตัวโปรแกรมสำหรับฝึกและแก้ไขรายการคำ

make training
make training-install

ถ้าคอนฟิกไม่ผ่านหรือคอมไพล์ไม่ผ่าน ส่วนใหญ่สาเหตุมาจากการที่ tesseract หาไลบรารีที่มันต้องการไม่เจอ ซึ่งอาจจะเกิดจากการที่เครื่องเรายังไม่มี (ก็ติดตั้งซะ) หรือมีแล้วแต่หาไม่เจอ (ก็ลองตั้งค่า environment ดู)

ใช้งาน Tesseract

การจะใช้งาน Tesseract ได้ ต้องมีไฟล์ข้อมูลภาษาให้มันด้วย ซึ่งดาวน์โหลดได้จาก https://github.com/tesseract-ocr/tessdata_best และ https://github.com/tesseract-ocr/tessdata_fast ตัวแรกจะแม่นกว่า ตัวหลังจะเร็วกว่า

โมเดลภาษาไทยชื่อ tha.traineddata
โมเดลภาษาอังกฤษชื่อ eng.traineddata

ตัวอย่างการเรียกใช้งานจาก command line:

tesseract input.png output --oem 1 -l tha -c preserve_interword_spaces=1 --tessdata-dir ./tessdata_best/

  • tesseract — เป็นชื่อโปรแกรมที่เราใช้จาก command line
  • input.png — ตรงนี้จะเป็นชื่อภาพอะไรก็ได้ ได้ทั้งฟอร์แมต TIFF, PNG, JPG
  • output — ชื่อไฟล์ text ใส่ไปแบบนี้ ไฟล์ที่ออกมาจะใช้ชื่อ output.txt (เติม .txt ให้อัตโนมัติ)
  • –oem 1 — เลือก OCR Engine mode เป็น LSTM
  • -l tha — เลือกภาษาไทย ถ้าเอกสารเรามีทั้งไทยและอังกฤษ ก็ใช้ -l tha+eng
  • -c preserve_interword_spaces=1 — บอกเอนจินว่าไม่ต้องแทรกช่องว่างระหว่างตัวอักษรให้ เนื่องจากภาษาไทยเขียนติดกันโดยไม่มีช่องว่าง ถ้าแทรกมาจะอ่านลำบาก
  • –tessdata-dir ./tessdata_best/ — บอกไดเรกทอรีที่เก็บข้อมูลโมเดล

เท่าที่ลองให้อ่านภาพตัวอักษรที่ใช้ฟอนต์ Tahoma กับฟอนต์ Sukhumvit Set ก็อ่านได้แม่นอยู่นะครับ ยกเว้นพวกวรรณยุกต์เล็กๆ บางๆ อย่างไม้เอก บางทีจะหายไป เอนจินมันอาจจะไม่เห็น ตรงนี้ถ้าจะแก้ไขทำได้ด้วยการประมวลผลภาพก่อนส่งเข้า Tesseract เช่นทำให้เส้นหนาขึ้น

แก้ไขไฟล์ wordlist

ไฟล์ tha.traineddata จริงๆ ข้างในมีข้อมูลอยู่หลายประเภท เราสามารถแตกมันออกมาเป็นไฟล์ย่อยๆ ได้

combine_tessdata -u ./tessdata_best/tha.traineddata ./tessdata_TEST/tha.

จะได้ไฟล์ unicharset และไฟล์ dawg (Directed Acyclic Word Graphs) ออกมา ซึ่งจากไฟล์เหล่านี้ เราใช้สร้างรายการคำศัพท์ได้

dawg2wordlist ./tessdata_TEST/tha.lstm-unicharset ./tessdata_TEST/tha.lstm-word-dawg ./tessdata_TEST/tha.lstm-word-list

พอได้รายการคำศัพท์มาแล้ว เราแก้มันได้ด้วย text editor ทั่วไปเลย — 1 บรรทัด 1 คำ

พอแก้ไขเสร็จแล้ว ก็ทำกลับกัน คือแปลงรายการคำศัพท์ให้เป็น unicharset และ dawg

wordlist2dawg ./tessdata_TEST/tha.lstm-word-list ./tessdata_TEST/tha.lstm-word-dawg ./tessdata_TEST/tha.lstm-unicharset

และรวมทั้งหมดเข้าด้วยกันเป็นไฟล์ traineddata เพื่อเอาไปใช้งานกับ tesseract

combine_tessdata ./tessdata_TEST/tha.

เราสามารถลองใช้โมเดลใหม่นี้ได้โดยบอก tesseract ผ่านพารามิเตอร์ –tessdata-dir ให้มาใช้ข้อมูลในไดเรกทอรีนี้

(วิธีการจัดการกับไฟล์คำศัพท์นี้ ขอบคุณ Shreeshrii ที่ช่วยอธิบายอย่างละเอียด)

ผลการทดสอบ

เท่าที่ทดสอบเร็วๆ การ preprocess ประมวลภาพก่อนจะส่งให้ Tesseract มีผลมาก แค่ปรับภาพจากสีเป็นขาวดำ ก็ทำให้อ่านข้อความได้เพิ่มขึ้น และยิ่งเราตัด (crop) ภาพมาเฉพาะส่วนที่มีข้อความ มันก็จะแม่นขึ้นอีก อย่างไรก็ตามการ postprocess อย่างการทำ spellcheck แก้คำผิด ก็ยังจำเป็นอยู่ ถ้าต้องการความแม่นยำที่เพิ่มขึ้นครับ

ด้านล่างเป็นผลทดสอบด้วยโมเดลจาก tessdata_best โดยไม่ได้ปรับแต่งอะไรเพิ่ม

Pantip on Facebook Color - read with Tesseract
ภาพจากสื่อสังคม (ไม่ได้ preprocess เลย) เมื่อทดสอบอ่านด้วย Tesseract
Pantip on Facebook Black and White - read with Tesseract
ภาพจากสื่อสังคม (ปรับสีเป็นขาวดำ) เมื่อทดสอบอ่านด้วย Tesseract
Pantip on Facebook Black and White, Cropped - read with Tesseract
ภาพจากสื่อสังคม (ปรับสีเป็นขาวดำและตัดมาเฉพาะส่วนที่เป็นข้อความ) เมื่อทดสอบอ่านด้วย Tesseract

ใครลองเล่นแล้วได้ผลอย่างไรบอกกันได้ครับ

Consumer information security tools – Thai localization 2017 summary

การแปลโปรแกรมรักษาความปลอดภัยในการใช้คอมใช้เน็ต สำหรับผู้ใช้ทั่วไป สรุปสิ้นปี 2017

Thanks COCONET: Southeast Asia Digital Rights Camp and Localization Lab for a localization sprint this year for Thai, Bahasa Indonesia, Burmese, and Khmer. Events like that help volunteer translators get connected to people who close to developers, and it helps the translation submission goes more smooth.

รอผู้พัฒนาดำเนินการต่อ

แปล 100% รีวิว 100% รอให้าทางผู้พัฒนาทดสอบการใช้งานในแอป

A chart showing that Signal on Android 100% translated to Thai

ยังแปลไม่เสร็จ

รอให้ทางโครงการเพิ่มภาษาไทย

  • Gpg4win ชุดโปรแกรมสำหรับเข้ารหัสลับแบบ PGP (Windows)
    https://www.gpg4win.de/localize-gpg4win.html

    • มีโปรแกรมย่อยอีก 4 ตัวคือ GnuPG (โปรแกรมเข้ารหัสลับ), Kleopatra (โปรแกรมจัดการกุญแจ), GpgOL (ปลั๊กอินสำหรับ Outlook – เข้ารหัสลับอีเมล), GpgEx (ปลั๊กอินสำหรับ Windows Explorer – เข้ารหัสลับแฟ้ม)
    • อาจจะมีทางให้ Localization Lab ช่วยจัดการให้แปลผ่าน Transifex ได้

     

  • GPG Suite ชุดโปรแกรมสำหรับเข้ารหัสลับแบบ PGP (macOS) — Update 2018-01-05: เปิดให้แปลภาษาไทยได้แล้ว
    https://www.transifex.com/gpgtools/public/

    • มีโปรแกรมย่อยอีก 4 ตัวคือ libmacgpg (โปรแกรมเข้ารหัสลับ), GPG Keychain (โปรแกรมจัดการกุญแจ), GPGMail (ปลั๊กอินสำหรับ Mail.app), GPGPerferences (ส่วนการตั้งค่า)

     

  • Orbot, Orfox แอปช่วยเข้าถึงเน็ตอย่างเป็นส่วนตัวมากขึ้น (Android)
    https://www.transifex.com/otf/orbot/ (เทียบได้กับ Tor) — Update 2018-01-10: เปิดให้แปลภาษาไทยได้แล้ว
    https://www.transifex.com/otf/orfox/ (เทียบได้กับ Tor Browser)

สำหรับโปรแกรมที่อยู่บน Transifex แล้ว ใครสนใจช่วยแปล สามารถสมัครสมาชิก Transfex (ฟรี) แล้วไปที่หน้าโครงการ กด Join Team ได้เลยครับ — ถ้าโปรแกรมเหล่านี้เป็นภาษาไทย ก็มีโอกาสที่คนไทยจะสามารถใช้โปรแกรมช่วยความปลอดภัยเหล่านี้ได้มากขึ้นครับ

สวัสดีปีใหม่ 2018 ทุกท่าน ขอให้เป็นปีที่ปลอดภัย คิด ทำ พูด ได้อย่างมั่นใจ 🙂

[27 ก.ค.] ตะลุยแปล #Tor เป็นไทย เสาร์นี้

แปล Tor เป็นไทย

ชวนชาวเน็ตมาช่วยแปลโปรแกรม Tor เป็นภาษาไทยกันครับ 🙂

เสาร์ 27 กรกฎา ประมาณ 10 โมงเช้าไปจนถึง 5 โมงเย็น
ที่ร้านกาแฟ Tom N Toms สยามเซ็นเตอร์ [Facebook event]

Tor (The Onioin Router) เป็นโปรแกรมที่ช่วยให้เราใช้งานอินเทอร์เน็ตได้ปลอดภัยขึ้น และลดโอกาสการถูกดักฟังขโมยข้อมูลจากผู้ไม่หวังดี มีวิธีการใช้ไม่ยุ่งยากนัก แต่อุปสรรคสำคัญอย่างหนึ่งสำหรับผู้ไม่คุ้นเคยคือ หน้าจอโปรแกรมยังเป็นภาษาอังกฤษอยู่ ซึ่งการแปลเป็นไทย ก็จะช่วยให้คนอีกมากเข้าอินเทอร์เน็ตได้อย่างปลอดภัยครับ

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

โดยรวมตอนนี้แปลไปแล้ว 46% และยังรอตรวจคำแปลอยู่อีกครับ

Knowledge WITH Borders

ความรู้มีเชื้อชาตินะ

Thai Digital Collection … เป็นโครงการที่มุ่งสนับสนุนการศึกษา … สำหรับประชาชนคนไทยเท่านั้น … คำตอบที่ถูกต้องสำหรับคำถามต่อไปนี้: ประเทศไทยรวมเลือดเนื้อชาติเชื้อไทย ประโยคต่อไปคือ … เพื่อป้องกันชาวต่างชาติใช้งาน … แต่จำเป็นต้องทำเพื่อปกป้องผลงานของคนไทยให้คนไทยใช้งานเท่านั้น

เอาน่ะ อย่างน้อยเขาก็มี ประชารัฐ ให้เลือก, ไม่ใช่ ราชอาณาจักร

technorati tags:
,
,

Thai-Style Sufficient Human Rights #amessinthailand

ขอยืนยันว่ารัฐบาลได้ให้ความสำคัญกับเรื่องสิทธิมนุษยชน เห็นได้จากนโยบายที่ให้มีการเรียนฟรีและเบี้ยยังชีพเป็นต้น

— ชวนนท์ อินทรโกมาลย์สุต เลขานุการรัฐมนตรีว่าการกระทรวงต่างประเทศ, 2553

เจ๋งเป้ง

Human Rights Watch ประเทศไทย: สิทธิมนุษยชนถดถอยอย่างหนักในปี 2552, บัวแก้วเตรียมแจงกรณีฮิวแมนไรท์วอทช์ ยันข้อมูลไม่ตรงความจริง

technorati tags:
,
,

[สรุป] High and Low Thai: Views from Within (A.V.N. Diller 1985)

บันทึกย่อเอกสารที่เรียน เอามาแปะไว้ในบล็อกเผื่อจะมีใครชวนคุย. อันนี้จากวิชาภาษาในสื่อสารมวลชน คณะศิลปศาสตร์ เป็นเอกสารชิ้นแรกที่อ่านในวิชา (ตอนนี้จะหมดเทอมแล้ว). ในบันทึกนี้ผมข้ามรายละเอียดไปเยอะ เพื่อยัดมันให้ลง 1 หน้า A4 (ตัวเอกสารเองผมก็อ่านไม่จบดีด้วย ข้าม ๆ บางส่วนไป). ขอบคุณ Rikker Dockum @thai101 ที่ส่งเอกสารหน้าที่ขาดหายไปให้.

ดาวน์โหลด PDF (81K), OpenDocument (18K)

หลังจากอ่านเอกสารนี้ เราอ่านอีกสองชิ้น ที่เกี่ยวกับภาษาในสื่อไทย. อันหนึ่งเกี่ยวกับการจำแนกระดับภาษาในหนังสือพิมพ์ไทย การใช้ภาษาในหัวข่าว คอข่าว ตัวข่าว โดยอิงตาม High Thai และ Low Thai ของ Diller (Khanittanan, Wilaiwan. 2007, Language of the news media in Thailand). อีกอันดูการสร้างและการใช้แสลงการเมือง (Srinarawat, Deeyu. 2007, Thai political slang: formation and attitudes towards usage).

—-

Anthony Diller เสนอว่า ‘ภาษา’ นั้นสามารถหมายถึง ‘ระดับภาษา’ หรือ ‘language subform’ (Edward Sapir) หรือ ‘style’ หรือ ‘register’ (Michael Halliday) โดยยกตัวอย่าง register ในภาษาไทย ได้แก่ ภาษาราชการ, ภาษากฎหมาย, ภาษาการศึกษา, ภาษาตลาด, ภาษาหนังสือ, ภาษาพูด, ภาษาปาก. นักการศึกษาไทยสังเกตว่าความแตกต่างระหว่าง ‘ระดับภาษา’ ดังกล่าว ขึ้นอยู่กับ บริบททาง ‘กาละเทศะ’ (โอกาส-ตำแหน่ง) เช่น ตำแหน่งทางสังคมโดยเปรียบเทียบและความสัมพันธ์ทางสังคมระหว่างคู่สนทนา. Diller ยกตัวอย่างที่อนุมานราชธนพูดถึงการใช้ภาษาที่ดัดจริตหรือใช้เต็มรูปแบบเกินไป เป็นตัวชี้ว่านักวิชาการไทยได้ตระหนักถึงการแบ่งระดับภาษามานานแล้ว. Diller ระบุว่า การที่เขาเลือกใช้คำว่า ‘ระดับภาษา’ หรือ ‘register’ นั้น เป็นความตั้งใจ เพื่อบอกว่า เมื่อนักวิชาการไทยอภิปรายกันเรื่องภาษาศาสตร์สังคม (sociolinguistics) นั้น พวกเขามักจะหมายถึงตัว ‘ระดับภาษา’ นี้ แม้พวกเขาจะใช้คำว่า ‘ภาษา’ เฉย ๆ ก็ตาม.

William J. Gedney ตั้งข้อสังเกตว่าชาวต่างชาติรุ่นก่อน ๆ ที่ศึกษาราชาศัพท์ เข้าใจผิดว่ามันเป็นภาษาอีกภาษาต่างหาก แต่เขาเห็นว่ามันเป็นเพียงระบบการแทนคำศัพท์หนึ่งด้วยอีกคำศัพท์หนึ่งเท่านั้น (a system of lexical substitutions) กล่าวคือเป็นเพียง ‘ระดับภาษา’ อีกระดับ. โดยบริบทในการเลือกระดับภาษานี้ มีทั้ง บริบทภายใน และ บริบทภายนอก. ตัวอย่างของบริบทภายในคือ เมื่อสามัญชนพูดถึงเจ้า ก็อาจใช้ราชาศัพท์, ส่วนบริบทภายนอก เช่น ตำแหน่งทางสังคมของคู่สนทนา ดังได้กล่าวไปแล้ว. อย่างไรก็ตาม Diller เสนอว่าการแบ่งระดับภาษาในภาษาไทยนั้น ไม่ได้เป็นการแบ่งในลักษณะ diglossia ที่มีระดับภาษาสองระดับ สูง-ต่ำ (High Thai และ Low Thai) เพื่อประสงค์การใช้งานที่แบ่งแยกกันชัดเจน. แต่ระดับภาษาในภาษาไทยนั้นมีหลายระดับ สูง-ต่ำโดยเปรียบเทียบ ตามมิติการใช้งาน สังคม และภูมิศาสตร์. Diller เรียกการแบ่งแบบนี้ว่า diglossic register differentiation หรือ diglossic subforms (น. 52-53).

ในเรื่องความแตกต่างระหว่างระดับภาษาสูงกับต่ำนี้ Diller ตั้งข้อสังเกตว่า ความแตกต่างจะพบได้มากที่สุดในชนบท ที่ซึ่งภาษาท้องถิ่นแตกต่างอย่างเห็นได้ชัดจากภาษากลาง (Central Thai ซึ่งให้ความหมายทั้งทางภูมิศาสตร์และทางการปกครอง) แต่ถึงอย่างไรก็ตาม ก็ยังเป็นภาษาเดียวกันอยู่. สิ่งหนึ่งที่ Diller ใช้ชี้ให้เห็นว่ามีความแตกต่างที่มากกว่าในชนบท ก็คือความรู้สึกสำนึก (conscious) ในการพัฒนาทักษะภาษา (language acquisition). เด็กในชนบทจะมีสองช่วง คือ เรียนรู้ระดับภาษาท้องถิ่นในช่วงปฐมวัย และเรียนรู้ระดับภาษากลางเมื่อเข้าศึกษาในโรงเรียน (formal education). ในขณะที่เด็กในเมือง ซึ่งคนรอบ ๆ ใช้ภาษากลางอยู่แล้ว จะเรียนรู้ภาษากลางตั้งแต่ในช่วงปฐมวัย ส่วนการศึกษาในโรงเรียนนั้นเพียงเพิ่มเติมคำศัพท์หรือภาษาพิธีกรรมเฉพาะเท่านั้น. กล่าวคือ โดยเปรียบเทียบแล้ว เด็กในชนบทจะเรียนรู้ภาษากลางอย่างมีสำนึกมากกว่าเด็กในเมือง (น. 53).

ไวยากรณ์ของภาษาไทยนั้น ได้รับอิทธิพลมาจากสองแหล่งภาษาใหญ่ ตามอิทธิพลทางความคิดในแต่ละช่วงเวลา ซึ่งใช้ภาษาเหล่านั้น คือ ไวยากรณ์บาลี-สันสกฤต และ ไวยากรณ์กรีก-ละติน (ผ่านภาษาอังกฤษ) โดยคำที่ใช้เรียกส่วนประกอบในไวยากรณ์กรีก-ละติน ถูกแปลเป็นไทยโดยใช้คำบาลี-สันสกฤต (น. 54).

สำหรับชุดคำศัพท์ สมัยสุโขทัยภาษายังไม่มีการแบ่งระดับมากเท่าทุกวันนี้ คำศัพท์ที่ใช้ก็ใช้คำเดียวกันกับผู้อยู่ในระดับสังคมต่าง ๆ กัน เช่น ใช้คำว่า ‘ตีน’ กับพระพุทธเจ้า ใช้คำว่า ‘กู’ กับเจ้า. ชุดศัพท์เขมร-อินเดียนั้นสำคัญมากในการแยกระดับภาษา (register differentiation) โดยคำในภาษาเหล่านี้เข้ามาก่อนทางศาสนาพุทธ และต่อมาใช้ในวัง และใช้ในกวีจากในวัง (น. 55). รัชกาลที่ 4 ให้ความสนใจการใช้ภาษาให้ถูกต้องตามแบบแผน มีการกำหนดลักษณนาม กำหนดการใช้คำต่าง ๆ และแสดงความไม่พอใจที่หนังสือพิมพ์ใช้คำ ‘สำเนียงไพร่เลว’ ไม่ใช้คำ ‘สำเนียงผู้ดี’. การใช้คำทับศัพท์ซึ่งก่อนหน้านี้ใช้กันแพร่หลาย ไม่ได้รับการยอมรับ มีการคิดคำศัพท์ไทยใหม่ ๆ เพื่อแทนคำทับศัพท์ โดยใช้คำบาลี-สันสฤกตมาสร้างคำใหม่เหล่านี้ (น. 56). เรื่องคำศัพท์นี้ ยังมีเรื่องการเลือกใช้ หรือ lexical variation ที่พูดถึงระดับคำที่ไม่ชัดเจนตายตัว ว่าคำไหนสูงคำไหนต่ำ แต่เป็นลักษณะ ‘สูงกว่า’ ‘ต่ำกว่า’ และคำต่ำก็ถูกใช้ทั่วไปได้ เช่น ‘ตีนแมว’ และการเลี่ยงคำบางคำที่มีความหมายไม่ดีหรือความหมายออกไปทางเรื่องเพศ-โดยเฉพาะในคำราชาศัพท์ (น. 59-62). นอกจากนี้ยังมีเรื่อง personal references (อะไร-อันใด; ใคร-ผู้ใด; ที่-ซึ่ง) ซึ่งเลือกใช้ตามเพศ ความใกล้ชิด ความเป็นทางการ (น. 63-64) และ deixis (นี่-นี้; นั่น-นั้น) ซึ่งเกี่ยวข้องกับภูมิศาสตร์ด้วย (น. 64). คำบุพบท (prepositions) บางอันปกติละได้ แต่เมื่อต้องการแก้ปัญหาความกำกวมก็จะถูกใช้แบบเต็ม การใช้คำบุพบทอย่างชัดแจ้ง บ่งถึงภาษาระดับสูง (น. 66-69).

การออกเสียงก็แยกระดับภาษาเช่นกัน เช่น ความแตกต่างระหว่าง /ร/ กับ /ล/ ซึ่งชาวไทยเชื้อสายจีนมีแนวโน้มจะออกเสียง /ร/ เป็น /ล/ (cluster loss) ความกลัวที่จะทำ /ร/ หาย นี้ นำไปสู่การแก้จนเกิน (overcorrection) ที่ใช้ /ร/ แทน /ล/ ในที่ที่ควรใช้ /ล/ และการกระดกลิ้นเพื่อออกเสียง /ร/ จนมากเกิน (over-rolled), การเติมเสียง -s ท้ายคำ หรือการขึ้นเสียงสูงท้ายประโยคเพื่อแสดงว่าเป็นคำถาม ซึ่งได้อิทธิพลมาจากภาษาอังกฤษ (น. 57-59).

ในตอนท้าย Diller เสนอว่า ความแตกต่างของระดับภาษามีเรื่องความขัดแย้งทางวัฒนธรรมอยู่ด้วย ซึ่งสะท้อนออกมาในความขัดแย้งทางระบบการศึกษา การศึกษาและสอนภาษาตามแนว prescriptive (มีภาษาแบบแผนในอุดมคติที่ถูกต้อง) และ descriptive (ภาษาอย่างที่มันเป็น).

เอกสารอ้างอิง

Diller, A. 1985, “High and low Thai: views from within”, in Papers in Southeast Asian Linguistics No.9, ed. D. Bradley, vol. 9, pp. 51-76. Pacific Linguistics, the Australian National University. Available from Southeast Asian Linguistics Archive: http://sealang.net/sala/htm/DILLERAnthonyVN.htm

technorati tags: , , , , ,

NLTK corpus readers for NECTEC BEST and ORCHID corpora

ความเดิมจากตอนที่แล้ว ทดลองสร้าง corpus reader ใน NLTK

ตอนนี้แก้การ encode ให้ใช้ได้กับ nltk.Text() แล้ว (แทนที่จะเก็บเป็น unicode ก็เก็บเป็น utf-8 encoded str แทน)

พร้อมกับเพิ่มตัวอ่านสำหรับคลังข้อความ BEST และ ORCHID ด้วย

ตัวอ่านคลัง BEST ในรุ่น 0.3 นี้ เรียกดูเป็นหมวดได้ (ข่าว วรรณกรรม สารานุกรม บทความ) เรียกดูข้อมูลกำกับขอบเขตคำ (word boundaries) ได้ แต่ยังไม่รองรับ <NE>named-entities</NE> กับ <AB>คำย่อ</AB> เนื่องจาก BEST ไม่มีข้อมูลขอบเขตประโยค ตัวอ่านคลังจะสร้างขึ้นเอง โดยสมมติ \n เป็นขอบเขตประโยค

ส่วนตัวอ่านคลัง ORCHID ในรุ่น 0.3 นี้ เรียกดูข้อมูลกำกับขอบเขตคำและชนิดคำ (Part-of-Speech) ได้ แต่ยังไม่รองรับขอบเขตย่อหน้า และยังเรียกดูเป็นรายเอกสารไม่ได้ (รุ่นนี้ทำงานกับคลัง ORCHID แบบที่ถูกเอา document-related metadata ออกไป)

ดาวน์โหลด & ติดตั้ง

แพ็คเกจ rotic รุ่น 0.3 ซอร์สโค้ดเผยแพร่ด้วยสัญญาอนุญาต GNU GPLv2 ตาม NLTK – ดาวน์โหลด rotic-0.3.tar.gz

วิธีติดตั้ง อ่าน README.TXT และ INSTALL.TXT – อย่าลืมดาวน์โหลดคลังข้อความมาติดตั้งด้วย รายละเอียดและสัญญาอนุญาตของข้อมูลแต่ละชุด อยู่ใน CORPORA.TXT

มีคำแนะนำอะไร เขียนมาบอกกันได้ครับ อยากจะลองทำให้มันเอาไปใช้ในการเรียนการสอนได้ – ไม่เฉพาะสำหรับนักเรียนคอมพิวเตอร์เท่านั้น แต่สำหรับนักเรียนภาษาศาสตร์ ฯลฯ ด้วย

ตอนนี้ความเร็วไม่ค่อยดีเท่าไหร่ โดยเฉพาะการโหลดตัว ORCHID ซึ่งใหญ่มาก ส่วนหนึ่งเป็นเพราะโค้ดยังซ้ำซ้อนอยู่หลายจุด เช่นตรงการแปลง utf-8 ที่น่าจะทำได้ตั้งแต่ระดับแรก ๆ ที่อ่านเข้ามาเลย ไม่ใช่มาแปลงเอาตอนหลัง-ต้องวนลูปอีกหนึ่งครั้งแบบขณะนี้ โค้ดยัง refactor ได้อีกเยอะ ใครคล่อง Python ก็ช่วยดูหน่อยนะครับ ผมแค่พอเขียนไถ ๆ ได้ ขอบคุณครับ 🙂

ตัวอย่างจาก example.py

1. พิมพ์ข้อความมั่ว ๆ ขึ้นมาจากตัวแบบ n-gram ที่สร้างจากคำในคลัง foosci :


foosci_text = nltk.Text(foosci.words())
foosci_text.generate()

ผลลัพธ์ :

… ซึ่ง ทฤษฎี สรุป ความรู้ ของ เรา เอา ไส้เดือน ไป ปล่อย ใน พื้นที่ ๆ มี ความ สงสัย ระหว่าง ความ เชื่อ เรื่อง มิติ ใหม่ นี้ …

2. พิมพ์ คำ/ชนิดคำ จาก 5 ประโยค แรกของคลัง ORCHID
โปรดสังเกตว่า เราใช้ชุดชนิดคำ (POS/tagset) แบบง่าย สามารถสลับชุดชนิดคำได้โดยสลับค่า simplify_tags :


for sent in orchid.tagged_sents(simplify_tags=True)[0:5]:
    print "[",
    for (word, tag) in sent:
        print word + "/" + tag,
    print "]"

ผลลัพธ์ :

[ การ/FIX ประชุม/V ทาง/N วิชาการ/N /PUNC ครั้ง/C ที่_1/DETN ]
[ โครงการวิจัยและพัฒนา/N อิเล็กทรอนิกส์/N และ/CONJ คอมพิวเตอร์/N ]
[ ปีงบประมาณ/N /PUNC 2531/N ]
[ เล่ม/C /PUNC 1/DETN ]
[ ศูนย์เทคโนโลยีอิเล็กทรอนิกส์และคอมพิวเตอร์แห่งชาติ/N ]

3. หาค่าการกระจายของสองคำ การ และ ความ ใน 4 หมวดของคลัง BEST
โปรดสังเกตว่า ตรงคำที่เราจะป้อนเข้าไปให้ฟังก์ชั่นต่าง ๆ ของ NLTK เราจะแปลงมันเป็น utf-8 encoded str ก่อน :


cfd = nltk.ConditionalFreqDist(
        (genre, word)
        for genre in best.categories()
        for word in best.words(categories=genre))

genres = ['news', 'encyclopedia', 'novel', 'article']
prefixs = [w.encode("utf-8") for w in [u'การ', u'ความ']]
cfd.tabulate(conditions=genres, samples=prefixs)

ผลลัพธ์ :

             การ ความ
        news 29567 11186
encyclopedia 25477 8541
       novel 4258 9097
     article 33200 16651

เล่นต่อเอง จากตัวอย่างในหนังสือ NLTK

เดี๋ยวอาจจะให้น้องฝึกงานที่โอเพ่นดรีมเอาไปทำต่อ เช่นทำให้มันใช้ AB, NE หรือขอบเขตประโยค/ย่อหน้าได้ .. เห็นนั่งเล่นเกมมาหลายวันละ :p

technorati tags:,,,

playing around Thai blog corpus with NLTK

อยากจะลองเล่น NLTK กับข้อมูลภาษาไทยดู คิดไปคิดมา เอาข้อมูลจาก foosci.com มาลองดูละกัน เขาเปิดให้ใช้ เป็น ครีเอทีฟคอมมอนส์ แสดงที่มา-อนุญาตแบบเดียวกัน (CC by-sa)

แต่ไม่อยากไปดึงมาเอง ขี้เกียจ เห็นว่าโครงการโรตี (อัลฟ่า) โดย Opendream ดูดบล็อกไทยจำนวนหนึ่งมาเก็บไว้ได้ระยะหนึ่งแล้ว เพื่อใช้ในการแนะนำลิงก์ (ดูตัวอย่างที่ keng.ws ที่ท้ายแต่ละโพสต์) ก็เลยเอาจากตรงนั้นมาใช้ละกัน

ข้อมูลที่มีเป็น XML ที่ dump มาจาก MySQL เราก็เขียนสคริปต์ก๊อก ๆ แก๊ก ๆ ดึงเฉพาะที่อยากได้ออกมา ด้วย xml.etree.cElementTree (ตอนแรกใช้ ElementTree แตน ๆ แต่อืดเกิน เนื่องจากแฟ้มมันใหญ่)
เอา HTML tags ออกด้วย Beautiful Soup แล้วตัดคำด้วย python-libthai ตัดประโยคแบบถึก ๆ ด้วย .split(‘\n’) จะได้ข้อมูลออกมาหน้าตาประมาณนี้ (จะเห็นว่าข้อมูลมันไม่ได้สมบูรณ์มาก มีแท็ก HTML โผล่มาด้วย-อันนี้เป็นที่ข้อมูลป้อนเข้าที่ dump มา) :


<?xml version="1.0" encoding="utf-8"?>
<roti>
  <entry id="4947" url="http://www.foosci.com/node/401" ...>
    <tags> <tag>LHC</tag> <tag>quantum physics</tag> ... </tags>
    <title> <w>บิดา</w> <w>ของ</w> <w>อนุภาค</w> ... </title>
    <content>
      <s> <w>p</w> <w>นัก</w> <w>วิทยาศาสตร์</w> ... </s>
      <s> <w>pcenter</w> <w space="1"> </w> <w>ภาพ</w> ... </s>
      ...
    </content>
  </entry>
  <entry>
    ...
</roti>

ใน w คือ คำ, ใน s คือ ประโยค

ดาวน์โหลดข้อมูล : foosci-20090424.tar.bz2 (สัญญาอนุญาต CC by-sa เช่นเดียวกับเนื้อหาใน foosci.com)
ข้างในจะมีสองแฟ้ม foosci00.xml และ foosci01.xml ให้ก๊อปปี้ไปใส่ในไดเรกทอรีข้อมูลของ NLTK (NLTK_DATA) $NLTK_DATA/corpora/rotibc ตัวโมดูลที่จะพูดถึงต่อจากนี้จะวิ่งมาหาที่ตำแหน่งนี้

ได้ข้อมูลมาแล้ว จะเอาเข้าไปใช้ใน NLTK ยังไง ? ก็ต้องเขียนตัว corpus reader ขึ้นมาก่อน ซึ่งกรณนี้ เราจะทำต่อมาจาก XMLCorpusReader (เรียกว่า inherit ไหม?) โดยไอเดียไม่มีอะไรมาก ก็ implement ตัวฟังก์ชั่น .words() เพื่อส่งกลับรายการคำ และฟังก์ชั่น .sents() เพื่อส่งกลับรายการประโยค โดยดูตัวอย่างจาก BNCCorpusReader

ที่ต้องทำเพิ่มเติมก็คือ สร้างแฟ้ม __init__.py ใส่ไว้ใน package เพื่อที่ว่าตอนโหลด มันจะได้โหลดเอาตัวข้อมูลขึ้นมาให้เราอัตโนมัติเลย (ซึ่งไม่ต้องกลัวอึด เพราะว่าโหลดแบบ lazy คือยังไม่ได้โหลดข้อมูลจริง ๆ จนกว่าจะใช้)

ตอนทำ __init__.py นี้ ทำให้รู้ว่า ทุกไดเรกทอรีที่เราจะใส่โมดูลอะไรลงไป จะต้องมีแฟ้มนี้ ไม่งั้นตอน build มันจะไม่นับไดเรกทอรีนั้นเป็น package จะข้ามไป เพราะงั้นถึงไม่ได้จะโหลดจะทำอะไร ก็ต้องใส่แฟ้มว่าง ๆ ไว้ (ดูเอกสาร Python Tutorial – Modules)

ใน __init__.py ไม่มีอะไรมาก แค่โหลดข้อมูลเฉย ๆ :
foosci = LazyCorpusLoader('rotibc', RotiCorpusReader, r'foosci\d+\.xml')

ดาวน์โหลดแพคเกจ roti.corpus : rotibc-0.1.tar.gz
แตกออกมาแล้ว ก็ลงด้วยคำสั่ง :
sudo python setup.py install
(ดูวิธีสร้าง setup.py มาจากเอกสาร Distutils – Creating a Source Distribution)

โอเค ครบละ ข้อมูล โปรแกรมอ่าน คราวนี้มาเล่นกัน ลองใน interpreter shell ของ Python ก็ได้


>>> from roti.corpus import foosci
>>> foosci.fileids() #แสดงรายชื่อแฟ้มในคลังข้อความ
['foosci00.xml', 'foosci01.xml']
>>> foosci.words() #แสดงรายการคำ
['p', u'\u0e19\u0e31\u0e01', ...]
>>> for w in foosci.words()[0:5]: #พิมพ์คำจากรายการ ตำแหน่ง 0-5
...     print w,
...
p นัก วิทยาศาสตร์ อังกฤษ ที่
>>>
>>> foosci.sents() #แสดงรายการประโยค
[['p', u'\u0e19\u0e31\u0e01', ...],
['pcenterimg', ' ', 'src=http://', ...], ...]
>>>

จะเห็นว่า เราพอจะเล่นอะไรกับมันได้ละ ถ้าจะเล่นมากกว่านี้ ลองดูตัวอย่างที่ Getting Started (NLTK)

ตัวอย่างหนึ่งจาก NLTK Book บทที่ 2 Accessing Text Corpora and Lexical Resources เขาลองเล่นกับ conditional frequency distribution เอามาสร้างประโยคมั่ว ๆ เล่น จากโมเดลไบแกรม ด้วยโค้ดด้านล่างนี้ :


def generate_model(cfdist, word, num=15):
    for i in range(num):
        print word,
        word = cfdist[word].max()

words = foosci.words()
bigrams = nltk.bigrams(words)
cfd = nltk.ConditionalFreqDist(bigrams)

ลองใส่คำอะไรสักคำให้มันดู มันจะสร้างประโยคมาให้


>>> generate_model(cfd, u'คอมพิวเตอร์')
คอมพิวเตอร์ ที่ มี ความ เสี่ยง มะเร็ง เต้า นม   href=http:// www. physorg. com/ ~r/ foosci/

การสร้างประโยคนั้น generate_model() ใช้วิธีเลือกเอาคำที่น่าจะเกิดต่อจากคำข้างหน้ามากที่สุด มาเรียงต่อกัน

ลองเล่นต่ออีกนิดหน่อยกับติวอันนี้ Working with corpora: Character Ngrams

ถ้ามีคลังข้อความที่น่ารัก ๆ กว่านี้ ก็น่าจะใช้ NLTK นี้ไปใช้เรียนสอน NLP หรือภาษาศาสตร์คลังข้อมูลง่าย ๆ ได้

ปัญหาอย่างนึงที่เจอตอนนี้คือ nltk.text.Text() ใช้กับ unicode ไม่ได้ คือมันจะพยายามแปลงข้อความไปเป็น ascii ซึ่งแปลงไม่ได้ แล้วก็จะตาย nltk.text.Text() นี่มีฟังก์ชั่นน่าใช้สำหรับการเรียนรู้เรื่องภาษาศาสตร์เยอะพอดู เช่น .concordance() .collocations() .similar()

<อัปเดต 2009.04.25> ใช้กับ nltk.Text() ได้แล้ว (แก้ตามคำแนะนำจากเมลกลุ่ม nltk-users) โดยต้องให้คำใน list เป็น str (“”) ที่ encode ด้วย utf-8 แทนที่จะใส่เป็นสตริงแบบ unicode (u””) ทำได้โดยแก้สองฟังก์ชั่น _elt_to_words() และ _elt_to_sents() ในแฟ้ม roti/corpus/rotibc.py ตรง .append(w.text) ให้เป็น.append(w.text.encode("utf-8", "replace")) เดี๋ยวจะปรับตัวแพคเกจใหม่ </อัปเดต>

ลองเล่นดูครับ เอาไปโมต่อตามสบาย โค้ดทั้งหมดเป็น public domain

ใช้ NLTK แล้วพบปัญหา คุยกับผู้ใช้รายอื่น ๆ ได้ที่เมลกลุ่ม nltk-users หรือถ้าอยากคุยกับคนไทย ลองกลุ่ม THLTA


แถม : Open License และคลังข้อมูลภาษา

ในงาน NAC 2009 โดยสวทช.ที่ผ่านมา ได้มีโอกาสแลกเปลี่ยนประเด็น open content, open license และ คลังข้อมูลภาษา กับคนในวงการ NLP จำนวนหนึ่ง ซึ่งก็มีความคิดเห็นหลาย ๆ อย่าง หลาย ๆ มุมก้นไป

เกือบทุกคนเห็นด้วยว่า เป็นเรื่องสำคัญที่ควรจะมีอะไรที่มันแชร์กันได้ ที่มัน open แต่ความหมายของคำว่า open สำหรับแต่ละคนก็ดูจะไม่เท่ากัน บางคนบอกว่า คลังอันนั้นอันนี้ฟรี ตัวนั้นตัวนี้โอเพ่นซอร์ส แต่พอไปดูเอาจริง ๆ ในรายละเอียด ก็พบว่า จำเป็นต้องลงทะเบียนก่อนบ้างหรือไม่ได้อัปเดตนานแล้วบ้าง (พจนานุกรม Lexitron) หรือลิงก์ดาวน์โหลดหายไปบ้าง (ORCHID Corpus – ดาวน์โหลดได้ที่ backup site) หรือก่อนหน้านี้เรื่องของฟอนต์หลาย ๆ ตัว ที่เอามาใช้ได้ฟรี แต่ไม่รู้ว่าจะโมได้ไหม redistribute ได้ไหม

ความเห็นของผมก็คือ จะเปิดหรือจะปิด อย่างไรก็ได้ เป็นสิทธิของเจ้าของข้อมูลที่เขาลงแรงลงเวลาไป
แต่ถ้าจะบอกว่าเปิด ก็ขอให้บอกให้ชัดเจนหน่อย ว่าในเงื่อนไขอะไร แล้วจะเอามาใช้จริง ๆ ได้ยังไง การบอกว่า เปิด เฉย ๆ โดยไม่ได้ให้รายละเอียดอะไรเลย ในทางปฏิบัติก็แทบจะเหมือนการไม่เปิด หน้า การแลกเปลี่ยนทรัพยากรและเครื่องมือ ที่ THLTA ก็อาจจะเป็นความพยายามหนึ่งที่จะทำให้เรื่องพวกนี้เคลียร์

สิ่งที่ผมคิดว่าน่าสนใจ และเป็นคุณสมบัติสำคัญของ open licenses ทั้งหลาย ไม่ว่าจะเป็น copyleft, GNU หรือ Creative Commons ก็คือ การไม่ต้องขออนุญาต ผมคิดว่าการไม่ต้องขออนุญาตนี้ทำให้ ข้อมูล โค้ด ไอเดีย ต่าง ๆ มันไหลเวียนได้อย่างอิสระ-ทันที ใครอยากจะเล่นอะไรก็เอา เต็มที่ ตามเงื่อนไขที่ประกาศไว้ชัดเจนล่วงหน้า ไม่ต้องรอไปรอมา ไม่ต้องตกอยู่ในภาวะไม่แน่ใจ

ซึ่งจริง ๆ แล้วเรื่องของความชัดเจนนี้ แม้จะเป็น closed content, closed source หรืออะไรก็ตาม ก็สามารถจะชัดเจนเรื่องนี้ได้ เพียงประกาศให้ชัดเจน — ไม่ใช่แค่บอกเฉย ๆ ว่า เปิด แล้วก็ทิ้งให้งง ให้เดาใจกันเล่น ๆ ว่า ตกลงจะเปิดแบบไหน เปิดยังไง

technorati tags:,,,

encode("UTF-8", "ignore") ข้าม ๆ เรื่องที่ทำไม่ได้ใน Python

หลังจากเอา python-libthai ของวีร์มาใช้กับข้อมูลที่ได้มาจากเว็บ ก็พบปัญหาเรื่อง character encoding นิดหน่อย

libthai นั้นปัจจุบันทำงานกับข้อมูลที่เป็นภาษาไทย 8 บิตอยู่ (น่าจะเป็น TIS-620) ตัว python-libthai เลยมีขั้นตอนการแปลงจากยูนิโค้ดไปเป็น 8 บิตก่อน
ทีนี้ ปรากฏว่า encoder “CP874”, “TIS_620” และ “ISO8859_11” ของ Python มันดันแปลงตัวอักษรบางตัวไม่ได้ (เนื่องจากใน charset พวกนั้น มันไม่มีตัวอักษรดังกล่าว) โปรแกรมก็เลยจะตาย ถ้าไปเจออักษรพวกนั้น

ก่อนตายมันจะโวยทำนองว่า :

UnicodeEncodeError: 'charmap' codec can't encode character
u'\u200b' in position 3560: character maps to <undefined>

วิธีแก้แบบถึก ๆ คือ เอาหูไปนาเอาตาไปไร่ซะ ignore มัน ด้วยการไปแก้ แฟ้มชื่อ libthai.c ของ python-libthai (แฟ้มนี้เป็น wrapper ที่ไปเรียก libthai ให้)

หาบรรทัดที่เรียกฟังก์ชั่น PyUnicode_Encode/Decode แล้วแก้พารามิเตอร์ตัวที่สี่เป็น “ignore” ซะ

เช่น จาก


PyObject *txt_cp874 =
    PyUnicode_Encode(s1, s1_len, "CP874", NULL);
tok =
    PyUnicode_Decode(buffer, tok_len, "CP874", NULL);

เป็น


PyObject *txt_cp874 =
    PyUnicode_Encode(s1, s1_len, "CP874", "ignore");
tok =
    PyUnicode_Decode(buffer, tok_len, "CP874", "ignore");

แล้ว sudo python setup.py install ใหม่อีกรอบ (อย่าลืมล้าง build เก่าทิ้งก่อน) ก็น่าจะใช้ได้แล้วครับ

ลิงก์ : Python Unicode How-to

technorati tags: 

WordPress 2.7 Thai localization update

สัปดาห์ที่ผ่านมา ปรับปรุงคำแปลภาษาไทยบางส่วนของ WordPress 2.7
มีทั้งแก้ตัวสะกด เปลี่ยนคำ และปรับสำนวนแปล โดยเฉพาะในส่วนของ Dashboard

ได้ส่งไปให้ผู้รับผิดชอบการแปลคือคุณ kazama แล้ว ดูรายละเอียดได้ที่เว็บบอร์ด WordThai

(ทำในอัตรางานของ Opendream เพื่อใช้กับเว็บไซต์ครีเอทีฟคอมมอนส์ประเทศไทย http://cc.in.th/)

อัปเดต: คุณ kazama แจ้งว่า WordPress 2.7.1 กำลังจะออกแล้ว ตัวคำแปลที่ผมเสนอไปนี้ อาจจะยังไม่ได้เข้าไปใน 2.7.1 เพราะต้องรอพิจารณาร่วมกันก่อน ว่าจะเอาอันไหนไม่เอาอันไหน

technorati tags:
,
,