- Một API JavaScript built-in giúp tách grapheme, word, sentence theo đúng quy tắc Unicode của từng locale — kể cả CJK, Thái, Khmer nơi dấu cách không phân tách từ.
- Đã chính thức Baseline từ 16/04/2024.
TL;DR
Intl.Segmenter là API native trong JavaScript dùng để tách văn bản thành grapheme, word, hoặc sentence theo locale. Nó giải quyết một bug âm thầm tồn tại trong code i18n suốt nhiều năm: split(' ') hoàn toàn vô dụng với tiếng Nhật, Trung, Thái, Khmer — những ngôn ngữ không dùng dấu cách để phân tách từ. Từ 16/04/2024 API này đã đạt Baseline Newly available, chạy được trên cả Chrome, Edge, Safari và Firefox, không cần thư viện ngoài.
What's new
Ngày 16/04/2024, web.dev xác nhận Intl.Segmenter đã chính thức là một phần của Baseline sau khi Firefox 125 hoàn tất triển khai. Trước đó Chrome 87 (11/2020) và Safari 14.1 (04/2021) đã hỗ trợ, còn Firefox là engine cuối cùng cập bến. MDN Web Docs vừa nhắc lại API này trên X để remind dev rằng đã đến lúc xóa các hack str.split(" ") hay regex tự chế.
Why it matters
Hãy nhìn ví dụ kinh điển này từ MDN:
const jaStr = "吾輩は猫である。名前はたぬき。";
jaStr.split(" ").length; // 1 — sai
jaStr.length; // 15 — code units, không phải số từ
Câu trên có 8 từ theo phân tích ngôn ngữ. Bất kỳ word counter, search index, hay autocomplete nào dựng trên split đều fail im lặng cho hàng tỷ người dùng CJK. Tương tự với Hindi "किंतु": str.length trả về 5 nhưng người dùng chỉ thấy 2 ký tự — vì các dấu kết hợp (combining marks) bị tính riêng.
Intl.Segmenter dùng quy tắc Unicode UAX #29 + dữ liệu ICU của browser để tách đúng cho từng locale, không cần dev biết quy tắc ngôn ngữ.
Technical facts
API gồm constructor + một method chính:
const seg = new Intl.Segmenter('ja-JP', { granularity: 'word' });
const out = seg.segment("吾輩は猫である。名前はたぬき。");
const words = Array.from(out)
.filter(s => s.isWordLike)
.map(s => s.segment);
console.log(words);
// ["吾輩","は","猫","で","ある","名前","は","たぬき"]
console.log(words.length); // 8
Mỗi segment object có 4 field:
| Field | Mô tả |
|---|---|
segment | Chuỗi con đã tách |
index | Vị trí bắt đầu trong input |
input | Chuỗi gốc |
isWordLike | Boolean — phân biệt từ thật vs dấu câu (chỉ có khi granularity: 'word') |
3 mức granularity:
| Granularity | Dùng cho | Ví dụ |
|---|---|---|
grapheme (mặc định) | Đếm ký tự đúng theo người đọc | Hindi किंतु → 2, không phải 5 |
word | Đếm từ, tokenize cho search | CJK, Thái — split chuẩn locale |
sentence | Tách câu, ước lượng read time, chunk cho TTS | Hindi dùng ।, English dùng . |
Comparison
| Cách làm | English | Japanese 「吾輩は猫である」 | Bundle cost |
|---|---|---|---|
str.split(' ') | Tạm được | ❌ Trả về cả câu, 1 token | 0 |
str.length | Char count | ❌ Đếm code units | 0 |
grapheme-splitter + tokenizer JS | OK | OK nhưng tùy lib | 50–200KB |
Intl.Segmenter | OK | ✅ 8 từ chuẩn ngôn ngữ học | 0 (native) |
Use cases
- Word counter trong CMS, editor, Notion clone — đếm đúng cho người dùng Nhật/Trung/Thái.
- Tokenize CJK cho search index client-side, autocomplete, fuzzy matching.
- Truncate an toàn: cắt chuỗi mà không xé đôi grapheme cluster (emoji ghép, dấu kết hợp Indic).
- Sentence split để chia chunk cho TTS, AI summarization, hoặc tính read time.
- Selection word count: lắng nghe
mouseup, đo từ trong vùng đang select.
Recipe — đếm từ trong selection
function countSelection() {
const text = window.getSelection().toString();
const seg = new Intl.Segmenter(navigator.language, { granularity: 'word' });
return Array.from(seg.segment(text)).filter(s => s.isWordLike).length;
}
document.addEventListener('mouseup', () => {
document.getElementById('count').textContent = countSelection();
});
Dùng navigator.language để segmenter follow theo locale của user. Muốn check trước locale nào được hỗ trợ: Intl.Segmenter.supportedLocalesOf(['hi','ja-JP','th']).
Limitations & pricing
Miễn phí, native, không bundle. Vài lưu ý:
- Từ ghép có gạch nối tiếng Anh bị tách thành nhiều từ — Raymond Camden test "The properties defined in the format specifies the location-path and the alt-text" ra 14 thay vì 12 vì "location-path" và "alt-text" mỗi cái thành 2 từ.
- Chất lượng word boundary phụ thuộc vào ICU data của browser — kết quả có thể lệch nhẹ giữa Chrome/Firefox/Safari.
- Không customize được tokenizer (không train dictionary riêng) — chỉ dùng Unicode UAX #29.
- Cần polyfill cho browser cũ trước 2024 (Firefox 124 trở xuống). Sau Baseline, ~95%+ user đã có sẵn.
What's next
API đã ổn định, không có kế hoạch deprecate. Improvements tương lai chủ yếu đến từ ICU data updates trong browser engine — quy tắc tách từ cho ngôn ngữ ít phổ biến sẽ chính xác hơn theo thời gian. Nếu bạn còn đang ship grapheme-splitter, sentence-splitter, hay tự viết regex tách emoji ZWJ trong bundle, đây là lúc xóa và giảm 30–200KB tùy lib.
Action items cho team frontend tuần này: (1) grep codebase tìm .split(' ') trên user-generated text, (2) replace bằng Intl.Segmenter với locale phù hợp, (3) audit word counter / read-time / character limit components — phần lớn đều đang sai cho user CJK và Indic. Một dòng API native, fix bug i18n đã âm ỉ cả thập kỷ.
Nguồn: MDN Blog, web.dev — Baseline announcement, MDN Reference, Raymond Camden — word counting.
