Letters, Digits and Unicode Normalization

The letter counter answers a narrower question than the character counter: not how many code points a text contains, but how many of them are letters. Getting that number right takes three decisions — what Unicode calls a letter, what to do with accents that can be encoded two ways, and where every non-letter code point should land so nothing goes missing. This guide walks through each decision as the engine makes it. Every number below is computed at build time by the same letterCount() function the tool runs in your browser, so the prose cannot drift from the behavior.

Four buckets that always sum to the total

The letter counter sorts every code point in the text into exactly one of four buckets: letters, digits, spaces & line breaks, and punctuation & symbols. The first two are counted directly. The other two are derived: spaces & line breaks is the total minus the characters-without-spaces figure, and punctuation & symbols is whatever remains once letters and digits are subtracted from the no-spaces figure. Because each code point is classified once and only once, the four rows always add up to the total, and no code point is dropped.

Take "Room 42 is ready.". The engine reports 11 letters (those of Room, is and ready), 2 digits, 3 spaces, and 1 punctuation & symbols code point — the final period — for a total of 17. The character counter reports the same 17 for this text, because nothing in it needs normalizing; when the two pages disagree, the cause is always the normalization step described further down.

What Unicode calls a letter

A letter, to the engine, is any code point in Unicode's Letter category — the class that regular expressions write as \p{L}. That class is not an alphabet. It includes the Latin letters with and without accents, Greek, Cyrillic, Arabic, Chinese, Japanese and every other script Unicode encodes. The engine tests each code point against that class and nothing else, so it needs no language setting, and text that mixes scripts is counted the same way as text that uses one.

"Zürich, Москва, 東京" makes the point. A Latin word with an umlaut, a Cyrillic word and a pair of Chinese characters give 14 letters between them; the commas account for 2 code points and the spaces for 2, which brings the total to 18. The ü, each Cyrillic letter and each Chinese character count as one letter apiece, exactly as a Latin letter does.

Several things people expect to be letters are not: digits have their own category, emoji are symbols, and combining marks — the floating accents described below — are marks. The apostrophe in a contraction is punctuation here even though the word counter treats it as part of the word; the two tools ask different questions, as the guide to what counts as a word explains from the other side.

Digits are decimal digits

The digits bucket uses Unicode's decimal-digit class, \p{Nd}. That includes the familiar ASCII digits and the decimal digits of other numbering systems. It excludes number-like characters that Unicode does not classify as decimal digits; those go to punctuation & symbols, so the digit figure never overstates what the text contains.

"Area: 12 m², gate ٣" separates the cases. The engine counts 3 digits: the 1 and the 2 of 12, and the Arabic-Indic ٣. The superscript ² is not among them — it lands in punctuation & symbols alongside the colon and the comma, which is why that bucket reads 3. Letters come to 9 (Area, m and gate), spaces to 4, and the total to 19.

Normalization: why two spellings of é count the same

An accented letter can reach the counter in two encodings. The composed form stores é as a single code point. The decomposed form stores a plain e followed by a combining acute accent — two code points that render as one glyph and are indistinguishable on screen. Both are valid Unicode; which one you get depends on where the text came from.

Counted raw, the two spellings would differ: the composed é is one letter, while the decomposed pair is one letter (the e) plus one combining mark, which is not. So letterCount() first normalizes the text to Unicode NFC — Normalization Form C, the composed form — and only then walks the result code point by code point. Under NFC, e plus combining acute becomes the single code point é, so the decomposed spelling turns into the composed one before anything is counted.

The worked example uses "café" written both ways. Typed with a composed é, it is 4 code points on the character counter and 4 letters here. Written as c-a-f-e plus a combining acute (the escape \u0301 in program code) it is 5 code points on the character counter — but still 4 letters, and a total of 4 on this page, because normalization merged the pair before counting.

The effect scales with the number of decomposed accents. "résumé" built entirely from decomposed accents is 8 raw code points but 6 after normalization, matching the 6 of the composed spelling exactly — and both spellings report 6 letters.

The honest edge: marks that cannot compose

NFC composes a base letter and a mark only when Unicode defines a single code point for the combination. For the common accented Latin letters it does, but combining marks are general-purpose — an acute can sit on any base letter — and not every combination has a composed code point. Put a combining acute on a q and NFC has nowhere to fold it: the sequence survives as two code points, a letter q followed by a mark.

The engine does not paper over this. The q counts as a letter; the surviving mark is not a letter, not a digit and not whitespace, so it is counted under punctuation & symbols. That is the honest edge: the letter count is right, but the mark appears as a symbol rather than vanishing into the letter it sits on. Silently skipping every combining mark instead would break the rule that the four buckets sum to the total and every code point is accounted for.

"Compare é with q́", with both accents typed as base letter plus combining acute, demonstrates both paths in one string. Raw, it is 18 code points. After normalization the é composes and the q́ does not, so the letter counter reports a total of 17: 13 letters, 3 spaces, and 1 punctuation & symbols entry — the orphaned acute on the q.

Why this page's total can be lower than the character counter's

Both tools count Unicode code points; they differ only in when. The character counter counts the text exactly as pasted, because the question it answers — will this fit a limit? — is asked of the text as it arrived, where a decomposed é genuinely occupies two code points. The letter counter normalizes first, because the question it answers — how many letters? — should not depend on an invisible encoding choice. Normalization is specific to the letter counter: the word, sentence, paragraph and line counts read the text as typed.

So on decomposed input the letter page's total is lower than the character counter's by exactly the number of marks NFC managed to compose: 1 for the decomposed café, 2 for the decomposed résumé, and 1 for the mixed example, where only one of the two marks had a composed form. On composed or unaccented input the difference is 0. Each page states its convention, and the right one matches your question: the character counter's raw count for a length limit, since it counts the text as received rather than a normalized copy (the methodology notes that some platforms count UTF-16 code units instead of code points, so confirm the destination's own convention), and the letter counter for how many letters a text contains. The guide to characters, code points and emoji covers the raw-count side in the same detail.

Spaces, line breaks and everything else

The spaces & line breaks row uses the engine's whitespace test — JavaScript's \s, which matches Unicode whitespace — so it covers ordinary spaces, tabs and line breaks alike. Everything that is neither a letter, a digit nor whitespace lands in punctuation & symbols: punctuation marks, currency and mathematical symbols, emoji, and the uncomposable combining marks above.

Emoji are counted at the code-point level, exactly as the character counter counts them. "Great work 👍🏽" followed by a line break and "See you at 9" shows the arithmetic: 17 letters, 1 digit, 6 in spaces & line breaks (the word spaces plus the line break itself), and 2 in punctuation & symbols — the thumbs-up base and its skin-tone modifier, which are separate code points even though they draw as one picture. The total is 26, matching the character counter's 26: NFC composes letters with their marks, and the totals show it left the emoji pair untouched.

A simple sanity check reads the breakdown from the bottom up: from the total, subtract whitespace, digits and letters, and what is left is the punctuation & symbols row. If that remainder exceeds the visible punctuation, the text holds something you cannot see — an emoji modifier, an uncomposable mark, or a symbol that looked like a letter or digit but is not one.

Frequently asked questions

Why does the letter counter show a lower character total than the character counter for the same text?

Because the letter counter normalizes to Unicode NFC before counting and the character counter counts the raw code points as pasted. On text with decomposed accents the difference is the number of marks that composed: "résumé" typed with decomposed accents is 8 code points on the character counter and 6 here. On composed or unaccented text the two totals are identical.

Does an accented letter count as one letter or two?

One, whichever way it was encoded. A composed é is one code point and one letter; a plain e followed by a combining acute becomes that same composed é under NFC before anything is counted. "café" gives 4 letters in both spellings, and a total of 4 on the letter counter even when the decomposed input is 5 raw code points.

What happens to an accent that has no composed form?

It survives normalization as its own code point and is counted under punctuation & symbols — not merged into the letter it sits on, and not dropped. In "Compare é with q́", where both accents are typed as base letter plus combining acute, the acute on the q cannot compose, so the breakdown shows 13 letters and 1 punctuation & symbols entry in a total of 17. The four rows still sum to the total.

Do digits from other numbering systems count as digits?

Yes, whenever Unicode classifies them as decimal digits. In "Area: 12 m², gate ٣" the Arabic-Indic ٣ is one of the 3 digits, while the superscript ² is not a decimal digit and is counted among the 3 punctuation & symbols code points.

Are emoji counted as letters?

No. Emoji are counted code point by code point under punctuation & symbols. The two-line example "Great work 👍🏽" / "See you at 9" has 17 letters and 2 punctuation & symbols code points — the thumbs-up base and its skin-tone modifier — in a total of 26.

Do the four rows always add up to the total?

Yes. Every code point is classified exactly once — letter, digit, whitespace, or punctuation & symbols — so the rows sum to the total by construction. "Room 42 is ready." gives 11 + 2 + 3 + 1 = 17.

Counting happens locally in your browser — nothing you type is transmitted or stored. Every figure in this guide is computed at build time by the engine the letter counter ships, and letters are Unicode letter code points after NFC normalization. See the methodology page.