UTF-16 vs Locale Sort: Code Units vs localeCompare
The live sorter's default Sort is Array.prototype.sort with no compare function: ECMA-262 UTF-16 code units, not localeCompare. A (65) precedes a (97). Try it withbanana, Apple, apple, banana, Cherry. Default Sort is Apple, Cherry, apple, banana, banana. Ignore case is Apple, apple, banana, banana, Cherry. Unique on that same sample is banana, Apple, apple, Cherry (exact keys) and banana, Apple, Cherry (Ignore case). Reverse is Cherry, banana, apple, Apple, banana. 10 precedes 2 because 1 is 49 and 2 is 50. Ignore case keys with toLowerCase(), still UTF-16. Not Unix sort, not Intl.Collator, not numeric. Sort does not call String.prototype.normalize. Compose e + U+0301 to é (U+00E9) on /unicode-normalize/. Nothing is uploaded.
A dictionary in German or Swedish is not the same order as a JavaScript array. The live line sorter splits on LF (after CRLF/CR normalize), then Sort calls Array.prototype.sort() with no compare function. This page names the gaps that change the first line — ASCII case and digits first, then letters a locale would regroup. NFC normalization é (e + U+0301) → é (U+00E9) is /unicode-normalize/. Sort does not call String.prototype.normalize.
Default sort is UTF-16 code units
A before a, Cherry before apple
MDN and ECMA-262: if the compare function is omitted, elements become strings and are compared as sequences of UTF-16 code units — the same test as < on two strings. Uppercase Latin letters occupy 65–90; lowercase occupy 97–122. Try it withbanana, Apple, apple, banana, Cherry. Default Sort yields Apple, Cherry, apple, banana, banana. An en localeCompare typically groups the two apples first.
10 before 2, file10 before file2
Digits are not numbers here. The first differing unit decides: 1 is 49, 2 is 50, so 10 precedes 2 and 80 precedes 9 (MDN's example). Prefixes keep file1, file10, file2. A numeric collator (new Intl.Collator("en", { numeric: true })) would emit file1, file2, file10. Signs are units too: + is 43 and - is 45, so +1 precedes -1.
Surrogates and letters a locale would regroup
Each surrogate unit is compared alone
Code points above U+FFFF are two UTF-16 units (U+D800–U+DFFF). MDN's pair 𥙑 (U+25651) sorts before fullwidth Z (\uFF3A, U+FF3A) because unit 55381 is less than 65338 — even though the first code point is larger. An emoji such as 😀 (U+1F600, units D83D DE00) therefore sorts after z. This is not a code-point sort and not a grapheme cluster sort.
ö after z here; after o in German; after z in Swedish
ö is U+00F6 (246), so UTF-16 order is O, o, z, ö. localeCompare with de groups ö with o (o, O, ö, z); sv treats it as a later letter (o, O, z, ö). Å is 197, so it sorts after a and Z here. Those locale results come from ICU / CLDR in the engine; this page never asks for them.
Ignore case is still UTF-16
toLowerCase keys, then the same < test
The Ignore case checkbox (Sort and Unique only) does not call localeCompare. It compares toLowerCase() strings. The sample becomes Apple, apple, banana, banana, Cherry. Equal keys keep their input order because ES2019 made sort stable. Unique on that same sample is banana, Apple, apple, Cherry (exact keys) and banana, Apple, Cherry (Ignore case). Reverse is reverse() on the split array and is Cherry, banana, apple, Apple, banana — it does not sort. Output is join('\\n') with no extra trailing newline. Lines are not trimmed; one trailing empty field from a final newline is dropped.
Not Unix sort, not a column sorter
GNU sort uses strcoll under LC_COLLATE. The C locale is byte order. Neither path runs in this browser tab. Sort, Unique, and Reverse are buttons — not as-you-type. Try it with the five fruit lines and leaves the result empty. Copy writes the displayed string. This is not a CSV column sorter, not localeCompare, not Intl.Collator, and not a natural/numeric sort.
UTF-16 order is not NFC
e + U+0301 → é (U+00E9) on /unicode-normalize/
Sort compares UTF-16 code units and never calls String.prototype.normalize. That is not the encoded text in/unicode-normalize/. The sample there fills é (e + U+0301); NFC normalization produces é (U+00E9) as normalized.txt. Those two strings look the same when rendered. The first units are 101 and 233, so NFD é sorts before NFC é. Unique keeps both lines. Ignore case toLowerCase() still leaves them distinct. The sample on /line-sorter/ stays the five fruit lines. The NFC vs NFD vs NFKC guide is /blog/nfc-vs-nfd-vs-nfkc/.
Sort lines in UTF-16 order
Default Sort is code units. 10 before 2. Nothing is uploaded.
FAQ
- What is the difference between UTF-16 and locale sort?
- JavaScript’s default sort compares UTF-16 code units, not Unicode code points. A supplementary character is two units, so it can sort before a smaller code point. localeCompare uses language rules instead (a before b, accents after base letters).
- Which sort should I use?
- Use locale sort (localeCompare) for user-visible text. UTF-16 sort is faster but less intuitive.
- How do I specify the locale?
- Pass a locale code to localeCompare: text.localeCompare(other, "en-US").
- Does this tool upload my data?
- No. All sorting runs in your browser using JavaScript. Nothing is sent to a server.