Alphabetical Sorting: Why localeCompare and Unicode Order Differ
When a developer sorts a list of text "alphabetically" by calling Array.sort() without a second thought, the result can look very different depending on the language involved. This guide explains how JavaScript's two sorting approaches — plain default sort versus localeCompare — use completely different comparison logic under the hood, and how that difference actually plays out for Korean, Chinese, and Japanese text.
1. JS's default sort doesn't know what "language" is
Calling [a, b].sort() with no arguments converts each element to a string, then compares UTF-16 code unit values numerically, one at a time. This is pure binary comparison — it carries no concept whatsoever of "which item comes first in some particular language's dictionary." For the English alphabet, where case and ordering roughly line up with Unicode's layout, this default happens to work out fine most of the time ("Apple" sorting before "banana" is really just because uppercase A's code value, 65, is lower than lowercase b's, 98) — but wherever a language's dictionary order diverges from Unicode's layout, the result comes out wrong.
2. localeCompare: sorting by Unicode CLDR rules
String.prototype.localeCompare() (or the more configurable Intl.Collator) is different. This method uses the Unicode CLDR (Common Locale Data Repository) collation rules built into the JavaScript engine to compare strings in the dictionary order that speakers of that language actually expect. This matters especially for Korean — Hangul syllables are composed of initial, medial, and final jamo, so raw Unicode code point order doesn't always line up precisely with the intuitive "ga-na-da" ordering, which is why locale-aware comparison methods like localeCompare are needed to guarantee a dictionary-stable result.
lines.sort((a,b)=>a.localeCompare(b)) — it uses localeCompare from the start, not a plain code-unit sort. That means sorting a Korean list with this tool gives you a properly ordered "ga-na-da" result governed by locale collation rules.
3. Why "alphabetical order" gets fuzzy for Chinese and Japanese
This is the heart of the guide. English (A-Z) and Korean (ga-hui) both have one clearly defined, standard dictionary ordering. Chinese hanzi and Japanese kanji (excluding kana), on the other hand, simply don't have "one natural alphabetical order" to begin with. There are at least three competing conventions for sorting these characters: by pronunciation (pinyin order for Chinese), by stroke count (the number of strokes making up the character), and by radical (the classification radical used in dictionaries) — and none of them is agreed upon as the single "standard." As a result, even when you pass zh or ja to Intl.Collator, there's no single standard collation to apply to logographic characters, so it effectively falls back to Unicode code point order. This isn't a bug — it's the unavoidable outcome of a problem that doesn't have one linguistically correct answer.
4. How the actual tool behaves
This site's Alphabetizer tool openly acknowledges this limitation. Its actual on-page FAQ states plainly that "Japanese (hiragana/katakana) and Chinese (kanji) are sorted by Unicode order rather than language-specific dictionary order," and recommends using a dedicated collation library if you need precise language-specific sorting. In other words, the tool takes full advantage of localeCompare for Korean, English, and the like, but for logographic content it simply inherits the limitation baked into the JavaScript standard API itself.
5. What to remember in practice
- Sorting English/Korean lists:
localeCompare(orIntl.Collator) gives you the expected dictionary order in the vast majority of cases. - Sorting Chinese/Japanese kanji lists: recognize that the very concept of "alphabetical order" is ambiguous here, and if you need a specific ordering rule (pinyin order, for example), you'll need a dedicated library for that language (e.g. convert to pinyin, then sort).
- Text mixed with numbers: alphabetical sort treats digits as characters too, so "10" ends up sorting before "2" in dictionary order — if you need value-based sorting, use a dedicated numeric sort feature instead.
Frequently Asked Questions
Q. Do the default Array.sort() and localeCompare always produce different results?
A. No. For a list of plain lowercase English text, where Unicode code values happen to line up with dictionary order, the results are identical. The difference shows up once case gets mixed in, or with something like Korean where code point order and linguistic convention diverge subtly.
Q. Which approach does this site's sorting tool use?
A. It uses localeCompare from the ground up. That means it's not a plain code-unit sort but one that follows locale collation rules, correctly reflecting Korean "ga-na-da" order as well.
Q. How do I sort Chinese or Japanese characters by pronunciation?
A. The standard Intl.Collator alone won't do it. You'd first need to convert the characters to a phonetic representation — pinyin for Chinese, or yomigana for Japanese — and then sort based on that converted string. That kind of conversion requires separate dictionary data or a dedicated library.
Q. Are Intl.Collator and localeCompare the same thing?
A. They use the same underlying collation engine. The difference is that localeCompare is a simple method that compares two strings on the fly, while Intl.Collator lets you pre-configure sorting options (case sensitivity, ignoring diacritics, etc.) and reuse them — which gives it an edge in performance and fine-grained control when sorting large volumes of data.