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[]
ParameterTypeDescription
textstringThai, Latin or mixed text.
maxLengthnumberMaximum line length. A positive integer.
optionsWrapOptionsOptional 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

OptionDefaultDescription
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

  1. Break opportunities come from the ICU4X dictionary line segmenter (UAX #14), compiled into the addon.
  2. Thai text rules remove breaks that would start a line with a combining mark or end it with a leading vowel.
  3. Units fill the current line until the next one would exceed maxLength.
  4. 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

OSArchitectures
Linuxx64 (glibc), arm64 (glibc), x64 (musl)
macOSx64, arm64
Windowsx64

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 สร้า / ง.