ProvenPin Install

Babel locale-aware numbers, currency, percent and dates: working versions (tested 2026-10-16)

Format numbers, currency, percent and dates for en_US, de_DE and fr_FR with Babel, and see how symbol placement, currency digits and parse rules change per locale.

passed6 passed · 0 failed2026-10-16 21:56 UTC
Status
passed
Last passed
2026-10-16 21:56 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
babel@2.18.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38972656245

The code

Exactly the files that passed. Your agent can pull them with get_recipe("babel-locale-number-currency") (install), or see them on GitHub.

requirements.txt
babel==2.18.0
pytest==9.1.1
locales.py
from __future__ import annotations

from datetime import date
from decimal import Decimal

from babel.dates import format_date
from babel.numbers import format_currency, format_decimal, format_percent, parse_decimal

LOCALES = ("en_US", "de_DE", "fr_FR")


def money(amount: float | Decimal | int, currency: str, locale: str) -> str:
    # Babel moves the symbol around and may pick a disambiguated one per locale.
    return format_currency(amount, currency, locale=locale)


def percent(fraction: float | Decimal, locale: str) -> str:
    # Babel multiplies by 100 itself, pass the 0..1 fraction.
    return format_percent(fraction, locale=locale)


def decimal(value: float | Decimal, locale: str, pattern: str | None = None) -> str:
    # Default pattern is "#,##0.###", so it silently rounds to 3 fraction digits.
    return format_decimal(value, format=pattern, locale=locale)


def parse_amount(text: str, locale: str) -> Decimal:
    # Grouping and decimal symbols are taken from the locale, not guessed from text.
    return parse_decimal(text, locale=locale)


def long_date(value: date, locale: str) -> str:
    return format_date(value, format="long", locale=locale)
tests/test_locales.py
from datetime import date
from decimal import Decimal

import pytest
from babel.numbers import NumberFormatError

from locales import decimal, long_date, money, parse_amount, percent


def test_currency_symbol_and_placement_vary_by_locale():
    assert money(1234.5, "USD", "en_US") == "$1,234.50"
    assert money(1234.5, "USD", "de_DE") == "1.234,50\u00a0$"
    assert money(1234.5, "USD", "fr_FR") == "1\u202f234,50\u00a0$US"


def test_currency_digits_follow_the_currency_not_the_locale():
    assert money(1234, "JPY", "en_US") == "\u00a51,234"
    assert money(1234, "JPY", "fr_FR") == "1\u202f234\u00a0JPY"


def test_percent_spacing_is_locale_specific():
    assert percent(0.256, "en_US") == "26%"
    assert percent(0.256, "de_DE") == "26\u00a0%"
    assert percent(0.256, "fr_FR") == "26\u00a0%"


def test_decimal_rounds_to_three_fraction_digits_unless_pinned():
    assert decimal(1.23456, "en_US") == "1.235"
    assert decimal(1.23456, "de_DE") == "1,235"
    assert decimal(1.23456, "fr_FR", pattern="#,##0.#####") == "1,23456"


def test_parse_decimal_is_strict_about_the_matching_locale():
    assert parse_amount("1.234,56", "de_DE") == Decimal("1234.56")
    # Gotcha: en_US treats "," as grouping, drops it, and returns 1.23456 with no error.
    assert parse_amount("1.234,56", "en_US") == Decimal("1.23456")
    # fr_FR groups with a narrow no-break space, so a "." group separator is invalid.
    with pytest.raises(NumberFormatError):
        parse_amount("1.234,56", "fr_FR")


def test_long_date_order_and_month_case():
    assert long_date(date(2024, 3, 5), "en_US") == "March 5, 2024"
    assert long_date(date(2024, 3, 5), "de_DE") == "5. M\u00e4rz 2024"
    assert long_date(date(2024, 3, 5), "fr_FR") == "5 mars 2024"
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.