October 12, 2026
    Sajjad Khazipura
    Multilingual Data, Verification, LAKEer

    Off by 1,000×: How AI Misreads Numbers in Multilingual Documents

    Reading numbers correctly in contracts written under any national convention

    A telecom operator in Madrid signs a service-level agreement (SLA) for a corporate MPLS network. The annex contains this table:

    Concepto Valor
    Disponibilidad mensual garantizada 99,95 %
    Latencia máxima (ida y vuelta) 50 ms
    Penalización por hora de indisponibilidad 1.234,56 €

    Six months later the customer asks whether the provider met its obligations and how much it is owed.

    To answer, a system has to read the three values. In Spain they mean 99.95 percent, 50 milliseconds and 1,234.56 euros. A system that assumes US conventions reads them differently. It stores 1.234,56 as 1.23 euros, and it reads 99,95 either as invalid or as 9,995, depending on how it handles the comma.

    No error is raised. The penalty is stored a thousand times too small, and the knowledge graph treats it as a fact.

    The model did not invent anything here. The pipeline read the document, extracted the value, typed it as a number and stored it. Each step completed normally, and the result is wrong.

    The example is Spanish, but the problem is not. Most of Europe and Latin America write 1.234,56, France writes 1 234,56, India writes 12,34,567, and the US writes 1,234.56. Any system that reads contracts from more than one country has to handle all of them.

    Why a misread number is worse than a missing one

    When retrieval misses a fact, the gap is usually visible. The answer says the penalty clause was not found, or a verifier marks the claim as unsupported.

    A misread number is stored as evidence. If the answer says “the penalty is €1.23 per hour”, a verifier that checks the claim against the stored value finds a match and approves it. The verifier works as designed, but the value it checks against is wrong.

    Earlier posts in this series argued that a confidence score is not verification and that each claim must be checked against evidence. This post adds the second part: verification is only as reliable as the evidence it uses. If ingestion corrupts a number, every later check inherits the error.

    Why guessing the convention fails

    One approach is to guess which convention a document uses. Each guessing method fails in some cases.

    Guessing from the number. 1.234,56 has two separators, so the last one must be the decimal point. 1.250 has one separator followed by three digits. That can be a thousands separator, or a value with three decimals such as 1.250 kg, 0.125 or 3.459 €/L. The text alone does not decide it, and SLAs contain many values of this form.

    Guessing from the language. Spanish does not imply a comma decimal. Mexico writes Spanish with US-style numbers, for example $1,250.00 MXN, and so does much of Central America. Mapping Spanish to comma decimals would misread Mexican contracts.

    Guessing from the rest of the document. This means finding an unambiguous number and reading the ambiguous ones the same way. It fails when a document has no unambiguous number, and the same clause can then be read differently in two files.

    We found the problem in our own code. A pre-production review of LAKEer’s multilingual support found that our currency extractor read €1.234,56 as 1.23, with no error and full confidence. No customer data was affected, because no non-English tenant had been onboarded yet.

    Figure 1 shows how each part of LAKEer read 1.234,56. The old currency extractor stored 1.23, a conversion step stored 1.23456, and the table loader left the column as text. Only a strict parser that follows published locale rules gives a defined answer in every locale.

    Figure 1. Five readers of 1.234,56

    ReaderUS
    en_US
    Mexico
    es_MX
    Spain
    es_ES
    Brazil
    pt_BR
    Germany
    de_DE
    France
    fr_FR
    India
    en_IN
    Old currency extractor1.231.231.231.231.231.231.23
    Old value conversion1.234561.234561.234561.234561.234561.234561.23456
    Old table loadertexttexttexttexttexttexttext
    Locale-blind fix1234.561234.561234.561234.561234.561234.561234.56
    Strict CLDRrejectedrejected1234.561234.561234.56rejectedrejected

    Wrong, stored silently Right Not a number Guessed: no rule says so Strict CLDR
    The same text under seven locales. Each row is one way LAKEer has read it. Only the strict CLDR row follows a published rule in every column.

    Table columns had the same problem. A column of European-style thousands (1.234, 12.000) was read as decimals, so sums and averages over it were a thousand times too small.

    Across 147 readings of common number formats in seven locales (US, Mexico, Spain, Brazil, Germany, France and India), the old parser returned 19 wrong values without an error. Every −15 written with a typographic minus lost its sign, and every Spanish 1.234 was stored as 1.234.

    Figure 2. What the old parser stored, with no error

    In the documentUS
    en_US
    Mexico
    es_MX
    Spain
    es_ES
    Brazil
    pt_BR
    Germany
    de_DE
    France
    fr_FR
    India
    en_IN
    −1515
    (≠−15)
    15
    (≠−15)
    15
    (≠−15)
    15
    (≠−15)
    15
    (≠−15)
    15
    (≠−15)
    15
    (≠−15)
    1.2341.2341.2341.234
    (≠1234)
    1.234
    (≠1234)
    1.234
    (≠1234)
    1.2341.234
    1,234123412341234
    (≠1.234)
    1234
    (≠1.234)
    1234
    (≠1.234)
    1234
    (≠1.234)
    1234
    3.4593.4593.4593.459
    (≠3459)
    3.459
    (≠3459)
    3.459
    (≠3459)
    3.4593.459
    1 234,56 (space)111111
    (≠1234.56)
    1
    12 345 (space)121212121212
    (≠12 345)
    12

    Correct Wrong, stored silently (intended value in brackets) Two numbers in a non-space locale
    Each cell is what the old parser stored for that text under that locale. Red cells are wrong values stored without an error. The intended value is in brackets.

    What large platforms do

    We looked at how systems that ingest numbers at scale handle this. None of them guesses.

    • Amazon’s Selling Partner API accepts only machine format: a . decimal, no grouping and an ISO currency code. For inventory spreadsheets prepared by people, the marketplace sets the locale. A file with the wrong separator gets an upload error and is not reinterpreted.
    • Google Merchant Center and Meta Commerce Manager require . decimals with no commas in product feeds.
    • Excel and Power BI let the user declare a locale when changing a column’s type, then parse strictly under it.

    In each case, machine-generated data uses one locale-free format, and data prepared by people is read strictly under one declared locale. Values that do not fit are rejected, not reinterpreted.

    What LAKEer does now

    We adopted the same approach, using the locale rules of the Unicode Common Locale Data Repository (CLDR), which Java, .NET, the major browsers and most spreadsheet software also use. The design has five rules.

    1. Two input channels. Machine-produced values, such as the extraction model’s output, JSON and APIs, are read in one canonical format: . decimal and no grouping. Numbers written by people, in document text and table cells, are parsed strictly under the tenant’s locale.

    2. The locale is declared. A locale names a language and a country: es_ES for Spain, es_MX for Mexico. LAKEer can recommend one, but an administrator confirms it before the first document is ingested. Only the customer knows whether the documents come from Madrid or Monterrey.

    3. Rejected values are kept. Text that does not fit the declared locale keeps its original text and is flagged as unparsed, and each build reports the count. Amazon can return a spreadsheet to the seller, but a signed contract cannot be returned to its author, so the rejection goes to the administrator.

    4. Source text is stored with each number. A claim such as “the penalty is €1,234.56 per hour” can then be checked against the characters 1.234,56 € in the contract, and a wrong locale can be found and corrected later.

    5. The locale applies only to reading. After parsing, numbers are stored in canonical form. Table columns become numeric columns and calculations do not depend on the locale.

    The same separation applies to answers. When a Spanish contract is queried in English, a language model that rewrites 1.234,56 € as €1,234.56 can introduce a thousand-fold error in the other direction. So numbers in answers are formatted by code from the stored value, and verification compares values, not text. This part is designed but not yet built; today an answer shows the number as the model writes it.

    The strict parser rejects more values than a lenient one. On the same 147 readings it accepted 61, rejected 86 and misread none. A rejected value is reported. A misread value stays hidden until someone notices a wrong result.

    Figure 3 shows how strict the published rules are. Spain, Brazil and Germany reject 1234.56, France rejects 1,234.56, and India rejects 1,234,567 because it groups digits in lakhs and crores. The only ambiguous row is 1.234, and the declared locale decides it.

    Figure 3. What strict CLDR accepts, locale by locale

    In the documentUS
    en_US
    Mexico
    es_MX
    Spain
    es_ES
    Brazil
    pt_BR
    Germany
    de_DE
    France
    fr_FR
    India
    en_IN
    1234.561234.561234.56rejectedrejectedrejected1234.561234.56
    1,234.561234.561234.56rejectedrejectedrejectedrejected1234.56
    1.234,56rejectedrejected1234.561234.561234.56rejectedrejected
    1.2341.2341.2341234123412341.2341.234
    3,5rejectedrejected3.53.53.53.5rejected
    1,234,5671 234 5671 234 567rejectedrejectedrejectedrejectedrejected
    12,34,567.89rejectedrejectedrejectedrejectedrejectedrejected1 234 567.89
    1 234,56 (space)rejectedrejectedrejectedrejectedrejectedrejectedrejected
    1 234,56 (narrow nbsp)rejectedrejectedrejectedrejectedrejected1234.56rejected
    −15 (typographic minus)rejectedrejectedrejectedrejectedrejectedrejectedrejected

    Accepted, same value everywhere Value depends on locale Rejected
    Each cell is what a strict CLDR parser returns for that text under that locale. Orange cells show where locales accept the same text but give different values.

    One limit remains. A tenant with both Spanish and Mexican contracts will see rejections, because one locale per tenant cannot cover both. Per-document locale detection is on the roadmap. Until then, the per-build count of unparsed values shows the administrator when it is needed.

    Knowing a number

    A theme of this series is that knowing a fact requires more than a stored value. It also requires knowing where the value came from, what rule produced it, and whether that rule was correct.

    Regulation points the same way. The EU AI Act requires providers of high-risk systems to keep records that make outputs traceable (Article 12) and to support their accuracy claims (Article 15). A number stored with its source text, the locale used to read it and a flag when it could not be read is that kind of record. It shows how an answer was produced.

    A number in a contract is a simple example. 1.234,56 means 1,234.56 in Spain, Germany or Brazil and is not a valid number under US rules. A system that answers questions about contract obligations has to know which reading applies, record how it decided, and report when it could not decide.

    In homage: Aristotle (384–322 BC)

    This post owes its central idea to Aristotle, Plato’s student and the founder of the Lyceum in Athens. In On Interpretation, he observed that written marks and spoken words are not the same for everyone, and that a word means something by convention, not by nature. Nothing in the sound or shape of a word ties it to what it stands for. People who share a language agree on that link.

    Numbers are no different. The characters 1.234,56 mean 1,234.56 under the convention used in Madrid and are not a valid number under the one used in Monterrey. The marks are identical; only the convention differs. So the meaning cannot be read off the text, and a system that guesses is really guessing which convention the writer used. That is why LAKEer does not guess. It asks the customer, who knows where the documents come from, to declare the convention.

    Try it: how would your number be read?

    Type a number as it appears in a document and pick the country it came from. Compare a naive parser with a strict, locale-aware reading.

    Examples:
    Naive parser (assumes US format)
    1.23456
    Strict reading for Germany
    1,234.56

    The naive value is off by 1,000×, and it was stored without any error.

    We use cookies for analytics and personalization. Privacy Policy