Docs
Status: pre-release. The API is the planned v0.1 surface and may change.
Install
npm install thaiwrap # or bun add thaiwrap
Prebuilt native binaries are installed for supported platforms. Node.js 22+ and Bun.
Usage
import { wrapThai } from "thaiwrap";
const lines = wrapThai("กระบวนการสื่อสาร ส่งมอบ และสร้างคุณค่า", 10);
// ["กระบวนการ", "สื่อสาร", "ส่งมอบ และ", "สร้าง", "คุณค่า"]
API
wrapThai(text: string, maxLength: number, options?: WrapOptions): string[]
| Parameter | Type | Description |
|---|---|---|
| text | string | Thai, Latin or mixed text. |
| maxLength | number | Maximum line length. A positive integer. |
| options | WrapOptions | Optional settings. |
Returns an array of lines. Break spaces (spaces and ZWSP) at line edges are trimmed. No-break spaces (U+00A0, U+2007, U+202F) are content.
Wrong types throw TypeError. Wrong values throw RangeError: maxLength not an integer from 1 to Number.MAX_SAFE_INTEGER, or an unknown measure or overflow.
Options
| Option | Default | Description |
|---|---|---|
| measure | "codeUnits" | "codeUnits" or "graphemes". See Measuring length. |
| overflow | "split" | "split" or "keep". What to do with a word longer than maxLength. |
Line breaking rules
Spaces
Spaces are break opportunities. They are trimmed at line edges and kept inside a line. A zero width space (U+200B) breaks like a space. A no-break space never breaks.
Line breaks
LF, CRLF, CR, VT, FF, NEL, U+2028 and U+2029 always end a line.
Blank lines
A blank input line becomes "". Blank lines before the first and after the last visible line are dropped. Blank lines between visible lines are kept.
wrapThai("", 10) // []
wrapThai("ก\n\nข", 10) // ["ก", "", "ข"]
wrapThai("\n\nก\n\n", 10) // ["ก"]
Long words
overflow: "split" cuts a long word at grapheme boundaries that keep vowels and tone marks with their consonant. "keep" puts the whole word on its own line. A unit that cannot be cut sits alone on its line, even if longer than maxLength. For Thai text, use a maxLength of 5 or more.
Measuring length
By default, length is counted like String.prototype.length (UTF-16 code units). Vowel and tone marks above or below a consonant count as one unit each. The same input at 10:
// codeUnits (default) ["กระบวนการ", "สื่อสาร", "ส่งมอบ และ", "สร้าง", "คุณค่า"] // graphemes ["กระบวนการ", "สื่อสาร ส่ง", "มอบ และ", "สร้างคุณค่า"]
Use code units to match a character limit (database columns, SMS). Use measure: "graphemes" to fit text on screen.
How it works
- Break opportunities come from the ICU4X dictionary line segmenter (UAX #14), compiled into the addon.
- Thai text rules remove breaks that would start a line with a combining mark or end it with a leading vowel.
- Units fill the current line until the next one would exceed maxLength.
- Spaces at line edges are dropped.
The dictionary ships inside the package, so output does not depend on the ICU version of your Node or Bun.
Platforms
| OS | Architectures |
|---|---|
| Linux | x64 (glibc), arm64 (glibc), x64 (musl) |
| macOS | x64, arm64 |
| Windows | x64 |
Not supported yet: browsers and edge runtimes such as Cloudflare Workers.
Known limitations
- Lone surrogates become U+FFFD.
- Invisible format characters (soft hyphen, LRM/RLM) are not looked through by the Thai rules.
- Output can differ from Intl.Segmenter, for example it keeps
?and)with the word before them. - A split inside a long word can leave a single final consonant on a line, for example
สร้า/ง.