The making of an IME

The making of an IME

Bộ gõ tiếng Việt - 1 kiểu app IME cực kỳ phổ biến ở Việt Nam và đã trải qua rất rất nhiều app khác nhau. Tuổi đời của chúng có lẽ dài gần bằng toàn bộ lịch sử tin học ở Việt Nam. Trải qua rất nhiều thay đổi về hệ điều hành và cấu trúc, số phận của chúng đúng là lên voi xuống chó. Tôi có thể kể đến 1 vài cái tên: Vietkey, Unikey, gotiengviet, EVkey, Openkey.

Vấn đề của chúng là gì? Tất cả đều ‘chết’ hoặc ngủ đông sau 1 thời gian vì tác giả không còn khả năng duy trì phát triển chúng song song với sự phát triển của hệ điều hành. Có lẽ sự duy trì chúng rất phức tạp.

Và rồi AI xuất hiện, tôi nghĩ đã tới lúc mình sẽ thử sức với bài toán mà rất nhiều người đã từng giải xem thế nào.

Gokey icon

Hiện trạng từ quá khứ

Thực tế tôi đã sử dụng EVkey trong 1 thời gian rất dài, trên cả Windows và Mac, song song. Tôi chọn nó vì nó có 1 tính năng rất hay đó là ‘trả về tiếng Anh’ khi nó thấy đấy không phải là từ tiếng Việt hợp lệ và không bị chữ w biến thành chữ ư.

WOW moment của tôi là lúc tôi có thể gõ chữ ‘windows’ 1 cách thoải mái mà không phải là ‘wwindows’. Và 1 thứ nữa, đó là nó có 1 bộ gõ tắt khá ổn, tôi có thể thêm các từ ngữ mình hay sai vào đó để sửa. VD như: phỉa (phải), voà (vào),.. Ngoài ra thì còn 1 số lỗi lặt vặt kiểu như: tự nhiên không chạy sau khi sleep hoặc.. chả biết tại sao. Thì thôi cũng được.

Tất cả nói chung ổn, cho tới khi nó… hết ổn. Đó là tác giả tuyên bố ngừng phát triển EVkey và macOS thì lên đời và EVkey ngừng chạy. Không sao, còn có Openkey. Bộ gõ này như kiểu song sinh với EVkey, chỉ là nhiều bug hơn. Điều đáng tiếc là tác giả cũng đã lâu không còn duy trì phát triển tích cực.

Bắt đầu

Với sự xuất hiện của AI. Tôi muốn thử 1 phen.

Mọi thứ bắt đầu từ 1 prompt vô thưởng vô phạt đó là bảo nó hãy sửa bug của OpenKey. Mọi việc thật đơn giản với Claude Opus 4.8 - 1 lúc là xong 2 con bug mà tôi khó chịu đó là ngưng hoạt động sau khi sleep và lỗ macro. Nếu tôi push ngược cái này lại, không biết bao giờ nó mới được merge & build nhỉ? Code AI viết có được tác giả chấp nhận không? Hay là tôi tự làm tự dùng thôi?

Chả biết nữa.

Vì bản chất tôi là UX/UI/Product designer nên tôi ngứa tay. Tôi bảo con AI: hãy làm 1 IME chạy được trên cả windows/mac và có thể cho linux về sau, nghĩa là chung engine. Ok, chỉ khoảng vài phút tôi đã có được 1 app chạy được, gõ được với bug tùm lum!

Đơn giản, tôi bắt đầu QC và bảo nó sửa, cũng sửa được, nhưng nó luôn mang lại 1 cảm giác không an toàn để sử dụng dù lúc đó tôi đã có 1 bộ kit QC cơ bản. Lúc này đã là ngày thứ 3, 4 gì đó tôi đầu tư vào cái app này mỗi buổi tối và lắt nhắt vào ban ngày khi rảnh ở công ty.

Cái khó ngay lúc đó là tôi tuy native nhưng tôi không hoàn toàn nắm quy luật tiếng Việt. Nếu để làm chuyện này 1 cách chuẩn chỉnh thì chúng ta sẽ phải research thay vì nói dăm ba câu từ cảm nhận bản thân cho vui.

Người đi trước

Tôi mở code Openkey ra xem & tìm đọc thêm các tài liệu về quy tắc tiếng Việt. Hoá ra chúng phức tạp hơn tôi nghĩ. Nhưng chúng không quá phức tạp như tiếng Nhật hay Trung.

Quả thật là, tôi học được rất nhiều thứ từ Openkey, không phải là cách họ làm cái app như thế nào mà là các quy luật về bỏ dấu trong tiếng Việt. Cảm ơn bạn TuyenVM đã làm ra sản phẩm này, tôi xin mượn các quy tắc của tiếng Việt trong code của bạn.

Engine được làm lại lần đầu.

Bây giờ bộ gõ đã có vẻ ‘đúng’ về dấu. Nhưng có 1 thứ tôi luôn gặp, đó là bug gõ chữ. Sửa chỗ này thì vỡ chỗ kia. Code lúc này If else quá nhiều, rất khó hiểu.

Tôi có đọc thêm engine của Unikey và gonhanh (chả biết ở đâu hiện ra) - 1 sản phẩm mà nhìn qua thì đặc quánh mùi AI tạo, tôi tải về xem nốt 1 thể cho gọn.

Tôi có thể nói thế này, bóc tách từng sản phẩm theo ý cá nhân.

Unikey

Đối với 1 sản phẩm lâu năm như Unikey, tôi đánh giá rất cao nỗ lực của tác giả, trong đó đặc biệt là sự tối ưu về khả năng thả dấu cho các máy tính đời cũ. Một bộ gõ giúp Việt Nam đổi mình trong cơn sóng của unicode thời 2000s

Nhận xét chung đây là 1 bộ gõ ổn định trên windows, chuẩn mực vàng. Kiến trúc của nó đi thẳng vào vấn đề xử lý tiếng Việt. Một số chỗ của nó còn hơi thô, nhưng bạn thạo gõ tiếng Việt là ok, mọi chuyện ổn. Tôi không nhầm thì iOS đang sử dụng/học engine của nó để làm bộ gõ mặc định.

Kiến trúc: Key -> Rule -> Output Rule là core.

Openkey

Tiến bộ hơn Unikey rất nhiều, đặc biệt là các tính năng cộng thêm của nó. Có lẽ tác giả đã học được khá nhiều từ EVkey. Điều kỳ lạ là có rất nhiều tính năng cổ lỗ sĩ vẫn được bê qua: VD như kiểu gõ VIQR, bảng mã VN-locale 1258, chuyển mã VNI… etc. Những tính năng hiện đại nhất ở thế kỷ trước!

Tôi rất thích bộ gõ này, nó mang lại gần như mọi thứ của EVkey mà tôi yêu thích trước đó. Chỉ là tôi không thể tuỳ biến được theo ý mình mà thôi.

Kiến trúc: Key -> Parser -> Validation -> Dictionary -> Output Output dựa trên cấu trúc tiếng Việt.

Gonhanh

App này rất bài bản khi mới tiếp xúc. Được thực hiện bởi 1 bạn có vẻ nổi tiếng trong giới AI và là coder lâu năm, cùng với cộng sự của bạn ấy. App có cả trang contributors riêng hẳn hoi. Tôi không biết bạn này.

Code của bộ gõ này rất phức tạp, đọc qua giống kiểu brute-force. Nó cho bạn gõ tất cả mọi thứ và dùng bộ từ điển siêu lớn để hoàn thiện ký tự sẽ được in ra.

Kiến trúc: Key -> Generate Candidates -> Dictionary -> Scoring -> Output Tập trung vào search. Tôi nhìn cái này và thấy hơi ngại làm, có cảm giác là có LLM ở đây. Cá nhân tôi không thích ý tưởng này lắm.

Nhưng mà thật ra cái tôi không thích nhất lại đi từ cái gốc designer của mình: icon của nó quá chói (haha).

FSM engine: module hoá, không cho phép sai

Sau 1-2 hôm vật vã với bugs, khó kiểm soát mọi thứ, tôi đi đến 1 quyết định lật ngược nữa.

Làm lại engine lần thứ 2.

Lần này, tôi sẽ kiểm soát chặt chẽ cách mà cái engine này hoạt động. Việc đầu tiên là viết 1 tài liệu mô tả rõ ràng, và cần nó phải hoạt đúng theo cách tôi chỉ định. Không để con AI tự nghĩ ra 1 cục gì đó mà chả hiểu là cục gì rồi tự chắp vá.

Concept

Tôi cho rằng concept này đi từ mindset của 1 Product designer của mình.

Một điều rất tự nhiên là, khi chúng ta viết 1 từ tiếng Việt hay bất kể ngôn ngữ nào đi nữa, chúng ta luôn biết chúng ta định viết cái gì, ký tự tiếp theo là gì.

VD, rõ ràng với 1 IME bình thường, disp = díp thì hợp lý rồi, nhưng mà gõ tới “displ” mà vẫn “dípl” thì quá quắt quá. Rõ ràng từ phương diện 1 product designer, bạn sẽ không cho phép người dùng đi vào ngõ cụt, nếu bạn có thể chặn việc đó ngay từ đầu.

Khi bạn bắt đầu viết 1 từ, VD bắt đầu bằng ‘n’

START

 └── 'n'

      ├── 'g' ──► NG
      │             │
      │             └── 'h' ──► NGH
      │                          │
      │                          ├── a e ê i o ô ơ u ư y
      │                          │
      │                          └── ...

      ├── 'h' ──► NH
      │             │
      │             ├── a e ê i o ô ơ u ư y
      │             └── ...

      ├── 'a' ──► NA
      │             │
      │             ├── c
      │             ├── m
      │             ├── n
      │             ├── ng
      │             ├── nh
      │             ├── i
      │             ├── o
      │             └── (END)

      ├── 'e' ──► NE
      │             ├── m
      │             ├── n
      │             ├── o
      │             └── (END)

      ├── 'i' ──► NI
      │             ├── h
      │             ├── n
      │             ├── t
      │             └── (END)

      ├── 'o' ──► NO
      │             ├── c
      │             ├── m
      │             ├── n
      │             ├── ng
      │             └── (END)

      ├── 'u' ──► NU
      │             ├── c
      │             ├── n
      │             ├── ng
      │             ├── ô
      │             ├── ơ
      │             └── (END)

      └── 'y' ──► NY
                    └── ...
 ...

Nghĩa là, ứng với mỗi ký tự được gõ ra, chúng ta luôn biết từ hợp lệ tiếp theo là từ gì. Đơn cử, nếu ký tự đầu tiên là ‘t’ thì nếu từ tiếp theo là ‘k’ thì chắc chắn đó không phải là tiếng Việt. Còn nếu đó là h, r, a, e, u, i, o thì là hợp lệ.

Đó là lúc engine FSM (Finite State Machine) của tôi ra đời.

FSM này sẽ làm 1 việc duy nhất, đó là sẽ luôn kiểm tra khả năng hoàn thành từ hiện tại ở dạng tiếng Việt hay không.

Cụ thể: Khi module FSM còn ‘lạc quan’ về khả năng cụm từ đang gõ là tiếng Việt thì nó sẽ cho phép bỏ dấu & giữ dấu đã có ở thời điểm đó. Cho tới khi FSM không tìm thấy từ tiếng Việt hợp lệ, nó sẽ ‘thả’ hết các từ đã gõ ra thành raw text.

Là lúc trả ‘display’ về lại thành ‘display’ vì ‘displ’ không hợp lệ.

Tiếp theo, để 1 IME hoạt động hợp lý, chúng ta sẽ cấu trúc nó như sau:

Buffer -> FSM -> TonePlacement -> Transform -> Commit

Chi tiết các thành phần module:

Kết quả & QC

Rất khả quan.

Engine hoạt động đúng như dự kiến. Cùng với việc tách luật gõ ra thành các file text lẻ bên ngoài, tôi lúc này có thể dễ dàng điều chỉnh các luật về tiếng Việt mà không bị vướng bận bởi code.

Điều khó nhất còn chưa tới

Thực ra việc làm 1 bộ gõ, khó nhất không phải là làm nó chạy được, mà là làm nó ổn định qua tất cả các app kỳ quái, chúng có thể xử lý input khác thường. Dự án này còn quá mới và tôi nghĩ sẽ còn rất nhiều khó khăn phía trước.

Nhưng dù gì đi nữa, việc tiếp cận vấn đề theo 1 cách khác hẳn, nó siêu nhẹ và siêu mượt - đó là thứ mà tôi thấy thú vị ở đây.

Tương lai

Một điều khá chắc chắn đó là ngoài 1 bộ engine tự trồng ra, Gokey sẽ là 1 phiên bản có UI chuẩn =))))

Định hướng của tôi là:

Việc chưa dám nghĩ tới: