Germany Number Format
Try It — Live Preview
Format Rules
Germany formatting implementation notes
For Germany, format numbers with the explicit locale de-DE; the reference value 1234567.89 renders as 1.234.567,89.
Germany's reference format uses "," as the decimal mark and period (".") for grouping. The integer uses repeating groups of three digits. Pass "de-DE" directly to Intl.NumberFormat instead of replacing punctuation in a preformatted string.
EUR is shown with € after the amount with a separating space; the same reference value renders as 1.234.567,89 €. The percent sign appears after the amount with a separating space. Use the ISO currency code for storage and interchange because a visible symbol can be shared by unrelated currencies.
Western 0–9 digits are the page's reference digit system, and the negative pattern is -n. Treat de-DE as the reference locale for this page, but still respect the user's explicitly selected language and numbering-system preferences. Validate output with formatToParts() when spacing, signs, or bidirectional literals affect the interface.
- Locale: de-DE
- Decimal and grouping: 1.234.567,89
- Currency: EUR → 1.234.567,89 €
- Digits: Western 0–9 digits
Reviewed 19 July 2026. Sources: Unicode locale number formats · Intl.NumberFormat reference
What's different here
- Decimal mark is a comma (,) (en-US uses a period (.)).
- Thousands are grouped with a period (.) (en-US uses a comma (,)).
- The currency symbol goes after the amount with a space (en-US: immediately before the amount).
- The percent sign goes after the number with a space (en-US: immediately after the number).
- Uses the same number pattern (decimal mark, grouping, symbol and percent placement) as Denmark, Greece, Romania, Slovenia, Croatia and Serbia in Europe, plus 1 page in other regions.
- Nearby differences: France (fr-FR) groups with a narrow no-break space (U+202F) instead of a period (.); Italy (it-IT) places the percent sign immediately after the number; Spain (es-ES) places the percent sign immediately after the number; Belgium (fr-BE) groups with a narrow no-break space (U+202F) instead of a period (.).
- Currency: EUR has ISO minor unit 2.
- Postal address order: name → building → street + street number → postcode + city → country. See address formats.
German number format
German (de-DE) writes 1234567.89 as 1.234.567,89: a comma for decimals, a period between thousands. The trap for English-speaking developers is reading it back, because 1.234 in a German form means one thousand two hundred thirty-four, not one point two three four. Format with Intl.NumberFormat('de-DE'), and parse with the locale's separators.
de-DE vs de-AT vs de-CH
Same language, three formats. Values are CLDR 48.2 / Intl output for 1234567.89:
| de-DE (Germany) | de-AT (Austria) | de-CH (Switzerland) | |
|---|---|---|---|
| Decimal mark | , | , | . |
| Thousands (plain number) | . | no-break space (U+00A0) | apostrophe: U+0027 (CLDR 48+); U+2019 on older runtimes |
| Plain number | 1.234.567,89 | 1 234 567,89 | 1'234'567.89 |
| Currency | 1.234.567,89 € | € 1.234.567,89 | CHF 1'234'567.89 |
| Percent (0.13) | 13 % | 13 % | 13% |
Two traps: de-CH uses a period for decimals, the opposite of Germany. de-AT moves the € in front of the amount and groups currency amounts with a period, but plain numbers with a space.
Parsing user input
Number() and parseFloat() only understand the English format:
Number("1.234,56"); // → NaN
parseFloat("1.234,56"); // → 1.234parseFloat silently returns 1.234, a thousand times too small. Read the separators from the locale instead:
function parseLocaleNumber(input, locale) {
const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
const group = parts.find(p => p.type === 'group').value;
const decimal = parts.find(p => p.type === 'decimal').value;
return Number(input.trim().split(group).join('').replace(decimal, '.'));
}
parseLocaleNumber("1.234.567,89", "de-DE"); // → 1234567.89
parseLocaleNumber("1,234", "de-DE"); // → 1.234The second result is correct German: 1,234 means one point two three four. If your form accepts both formats, ask for the locale or reject ambiguous input.
Compact notation
new Intl.NumberFormat('de-DE', { notation: 'compact' }).format(1234); // → "1234"
new Intl.NumberFormat('de-DE', { notation: 'compact' }).format(1234567); // → "1,2 Mio."
new Intl.NumberFormat('de-DE', { notation: 'compact' }).format(1234567890); // → "1,2 Mrd."
new Intl.NumberFormat('de-DE', { notation: 'compact' }).format(1200000000000); // → "1,2 Bio."
new Intl.NumberFormat('de-DE', { notation: 'compact', compactDisplay: 'long' }).format(1234); // → "1,2 Tausend"Short compact notation has no abbreviation for thousands, so 1,234 stays 1234. Use compactDisplay: 'long' if you need a word.
Q&A
Where does the € sign go in German?
In de-DE, after the amount with a space: 1.234.567,89 €. Austria (de-AT) puts it in front: € 1.234.567,89.
What do Mio., Mrd. and Bio. mean?
Millionen (10⁶), Milliarden (10⁹) and Billionen (10¹²). A German Milliarde is an English billion, and a German Billion is an English trillion, so 1,2 Bio. is 1.2 trillion. Let Intl pick the abbreviation rather than translating "B".
Is Austrian or Swiss German formatted the same as German?
No. de-CH uses a period for decimals and an apostrophe for thousands. de-AT groups plain numbers with a no-break space and puts € before the amount.
How do I read "1.234" from a German form?
As one thousand two hundred thirty-four. Parse with the locale's separators, as shown above, not with parseFloat.
Code Examples
// Basic number
new Intl.NumberFormat('de-DE').format(1234567.89)
// → "1.234.567,89"
// Currency
new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(1234567.89)
// → "1.234.567,89 €"
// Percentage
new Intl.NumberFormat('de-DE', {
style: 'percent',
minimumFractionDigits: 1
}).format(0.125)
// Compact notation
new Intl.NumberFormat('de-DE', {
notation: 'compact'
}).format(1234567)Negative Number Formats
new Intl.NumberFormat('de-DE').format(-1234567.89)new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', currencySign: 'accounting' }).format(-1234567.89)Edge Cases & Notes
Period as thousands separator is the most common developer pitfall. Compact: Mio./Mrd./Bio.
Continue your localization implementation
Connect this reference to the format, validation, and testing guidance used elsewhere on the site.
Formatting implementation
Numbers, currencies, dates, time zones, numbering systems, and production tests.
Failure patterns
Hardcoded copy, broken layouts, missing context, bidi, and cultural adaptation.
Standards reference
ISO country, currency, and language codes; BCP 47; E.164; and UPU S42.
Validation coverage
Provider evidence, country test cases, geocoding, deliverability, and phone checks.
Locale-aware numbers
Separators, grouping, currency precision, and Intl.NumberFormat patterns.
Localization testing
Pseudolocalization, test matrices, native review, and regional release gates.
Address and phone standards
UPU S42, E.164, adaptive forms, storage, parsing, and validation.
I18n architecture
Messages, Unicode, RTL, locale services, fallbacks, and delivery workflow.
Currency reference
ISO 4217 codes, symbols, minor units, and implementation examples.
Address formats
Country templates, field order, and accessible international forms.
Phone formats
E.164 storage, regional display, parsing, and validation.
Compare locales
Inspect the same numeric value across several country formats.
Interactive tools
Try number, address, and phone formatting in your browser.
