Two families of bugs that English-only testing never surfaces: line breaking (CJK text wraps differently) and IME composition (characters are composed across multiple keystrokes, so key events lie).
This skill is East-Asian glyph rendering + input. For the broader multi-language
copy layer — text expansion breaking layout, CLDR plural categories, Intl
number/date/currency formatting, RTL and lang/dir markup — use
i18n-copy-and-layout.
Line breaking
- East Asian text lines can break between almost any two characters by default
(UAX #14 ideographic line-breaking class; UAX #14 itself notes Korean is commonly
tailored to space-based breaking) — in CSS, English-style word integrity for
Korean is opt-in via
word-break: keep-all, not the default. - Korean:
word-break: keep-allprevents intra-word breaks for CJK text so space-separated Korean words wrap as units (MDN: "Word breaks should not be used for Chinese/Japanese/Korean (CJK) text"). Pair withoverflow-wrap: break-wordas the escape hatch for long unbreakable tokens (URLs). word-break: break-allsolves the opposite problem — inserting breaks between any two characters of non-CJK text to prevent overflow (CJK behavior is unchanged by it, per MDN); don't apply it to body text in mixed-language UIs.line-break: strict|loosetunes Japanese/Chinese punctuation-adjacent breaking (kinsoku); it does not control Korean word integrity — that'skeep-all.- Headlines/buttons: test with real Korean copy — label widths differ wildly from English and mid-word breaks read as typos to native speakers. W3C klreq (Korean Layout Requirements) is the canonical reference for Korean typography rules.
IME composition (the part that breaks controlled inputs)
-
IMEs compose text across keystrokes; the DOM fires
compositionstart→compositionupdate* →compositionend(MDN CompositionEvent). -
During composition, key events are unreliable for shortcuts:
keydownfires withKeyboardEvent.isComposing === true, and legacy browsers/engines reportkeyCode 229(UI Events spec documents 229 as the IME-processing code). The classic bug: pressing Enter to commit the IME buffer ALSO triggers the form's Enter handler — submitting half-composed text. Guard:input.addEventListener('keydown', (e) => { if (e.isComposing || e.keyCode === 229) return; // IME is composing — not a command if (e.key === 'Enter') submit(); }); -
Controlled inputs (React etc.): transforming/filtering
valueon every change whileisComposingbreaks the composition buffer (characters duplicate or split, e.g. Hangul jamo separation) — community-documented across React issue threads rather than any spec, but consistently reproducible. Apply sanitization/uppercase/masking oncompositionendor blur, not mid-composition. -
Search-as-you-type: each composition update fires
inputevents with partially composed Korean syllables, for example ㅅ → 스 → 슼 → 스키 → 스킬 (successive input states while typing the word skill). Debouncing alone still queries invalid intermediate states; gate dispatch oncompositionend(plus a normal path for non-IME input). -
Browser order differences exist between engines for
compositionendvs the finalinputevent — never assume one fixed sequence; branch onevent.isComposingrather than event order.
Counting and slicing
String.lengthcounts UTF-16 code units, not user-perceived characters. Hangul syllables in NFC are 1 unit, but decomposed jamo are 2-3 (NFD; also file names from macOS HFS+-era tooling — HFS+ stored names in a frozen canonical decomposition similar to, but not identical to, standard NFD); emoji are 2+. For user-facing length limits and cursor-safe slicing useIntl.Segmenter(grapheme granularity) and normalize (.normalize('NFC')) before comparing strings from mixed sources. Feature-detectIntl.Segmenter; for older WebViews or browsers, fall back to a tested grapheme-splitter library or a conservative "validate after input, do not slice mid-string" policy rather than pretendingString.lengthis a character count.
PR-worthiness gate
Require a real CJK string or IME sequence and a user-visible failure: a broken composition buffer, premature shortcut/submit, incorrect grapheme limit, or layout that makes actual copy unreadable. Add the smallest native-input or rendered-text regression.
Reject weak findings: keyCode === 229 used only as a compatibility guard,
String.length on protocol bytes or internal identifiers, a generic narrow
container with no CJK reproduction, or translation expansion that belongs to
i18n-copy-and-layout.
Output shape
Report the language/input method, composition or layout sequence, event and rendered evidence, smallest guard/style/segmentation fix, browser or WebView matrix, and the regression that uses real CJK text.
Sources
- MDN
word-break: https://developer.mozilla.org/en-US/docs/Web/CSS/word-break - MDN
line-break: https://developer.mozilla.org/en-US/docs/Web/CSS/line-break - MDN
overflow-wrap: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap - MDN
CompositionEvent: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent - MDN
KeyboardEvent.isComposing: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/isComposing - Unicode Standard Annex #14, Unicode Line Breaking Algorithm: https://www.unicode.org/reports/tr14/
- W3C Korean Layout Requirements: https://www.w3.org/TR/klreq/
- UI Events spec, keyCode 229 / IME composition processing: https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode