Claude Code for locale: Python Locale-Aware Formatting — Claude Skills 360 Blog
Blog / AI / Claude Code for locale: Python Locale-Aware Formatting
AI

Claude Code for locale: Python Locale-Aware Formatting

Published: September 23, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s locale module provides locale-aware number, currency, and string formatting using POSIX locale categories. import locale. setlocale: locale.setlocale(locale.LC_ALL, "de_DE.UTF-8") — activates locale; "" uses system default; not thread-safe. getlocale: locale.getlocale(locale.LC_NUMERIC)("de_DE", "UTF-8"). localeconv: lc = locale.localeconv() → dict with "decimal_point", "thousands_sep", "grouping", "currency_symbol", "frac_digits", "int_curr_symbol", "mon_thousands_sep". format_string: locale.format_string("%.2f", 1234567.89, grouping=True)"1,234,567.89" (en_US) or "1.234.567,89" (de_DE). currency: locale.currency(1234.50, grouping=True)"$1,234.50" or "1.234,50 €". str: locale.str(3.14) → locale-formatted float string. atof: locale.atof("1.234,56")1234.56 (de_DE). atoi: locale.atoi("1.234") → 1234. strcoll: locale.strcoll("ä", "z") — returns <0, 0, >0 for locale-aware comparison. strxfrm: locale.strxfrm(s) — for use as sort key. normalize: locale.normalize("en_US")"en_US.ISO8859-1". locale.LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME — individual categories. Claude Code generates locale-aware formatters, number parsers, currency renderers, and collation sorters.

CLAUDE.md for locale

## locale Stack
- Stdlib: import locale
- Set:    locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
- Num:    locale.format_string("%.2f", n, grouping=True)
- Money:  locale.currency(amount, grouping=True)
- Parse:  locale.atof("1,234.56"); locale.atoi("1,234")
- Sort:   sorted(strings, key=locale.strxfrm)
- Conv:   lc = locale.localeconv()  # decimal_point, thousands_sep, ...

locale Locale-Aware Formatting Pipeline

# app/localeutil.py — format, parse, currency, collation, convention, context manager
from __future__ import annotations

import locale
import threading
from contextlib import contextmanager
from dataclasses import dataclass
from typing import Generator


# ─────────────────────────────────────────────────────────────────────────────
# 1. Thread-safe locale context manager
# ─────────────────────────────────────────────────────────────────────────────

_locale_lock = threading.Lock()


@contextmanager
def use_locale(
    name: str,
    category: int = locale.LC_ALL,
) -> Generator[None, None, None]:
    """
    Activate a locale for the duration of a with-block, restoring it afterwards.
    Uses a module-level lock to prevent concurrent locale changes in threaded apps.

    Example:
        with use_locale("de_DE.UTF-8"):
            print(locale.format_string("%.2f", 1234.5, grouping=True))
            # → "1.234,50"
    """
    with _locale_lock:
        prev = locale.setlocale(category)
        try:
            locale.setlocale(category, name)
            yield
        finally:
            locale.setlocale(category, prev)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Number formatting
# ─────────────────────────────────────────────────────────────────────────────

def format_number(
    value: int | float,
    decimals: int = 2,
    grouping: bool = True,
    locale_name: str | None = None,
) -> str:
    """
    Format a number with locale-aware decimal point and thousands separator.

    Example:
        format_number(1234567.89)               # "1,234,567.89" (en_US)
        format_number(1234567.89, locale_name="de_DE.UTF-8")  # "1.234.567,89"
    """
    fmt = f"%.{decimals}f"
    if locale_name:
        with use_locale(locale_name):
            return locale.format_string(fmt, value, grouping=grouping)
    return locale.format_string(fmt, value, grouping=grouping)


def format_integer(
    value: int,
    grouping: bool = True,
    locale_name: str | None = None,
) -> str:
    """
    Format an integer with locale-aware thousands separator.

    Example:
        format_integer(1000000)   # "1,000,000" (en_US)
    """
    if locale_name:
        with use_locale(locale_name):
            return locale.format_string("%d", value, grouping=grouping)
    return locale.format_string("%d", value, grouping=grouping)


def format_percent(
    value: float,
    decimals: int = 1,
    locale_name: str | None = None,
) -> str:
    """
    Format a ratio (0.0–1.0) as a locale-aware percentage string.

    Example:
        format_percent(0.1234)   # "12.3%"
    """
    pct = value * 100
    num = format_number(pct, decimals=decimals, locale_name=locale_name)
    return f"{num}%"


# ─────────────────────────────────────────────────────────────────────────────
# 3. Currency formatting
# ─────────────────────────────────────────────────────────────────────────────

def format_currency(
    amount: float,
    grouping: bool = True,
    international: bool = False,
    locale_name: str | None = None,
) -> str:
    """
    Format a monetary value with the locale's currency symbol.

    international=True uses the ISO 4217 code (e.g. "USD") instead of symbol.

    Example:
        format_currency(1234.50)                              # "$1,234.50"
        format_currency(1234.50, locale_name="de_DE.UTF-8")  # "1.234,50 €"
        format_currency(1234.50, international=True)         # "USD 1,234.50"
    """
    if locale_name:
        with use_locale(locale_name):
            return locale.currency(amount, grouping=grouping, international=international)
    return locale.currency(amount, grouping=grouping, international=international)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Number parsing
# ─────────────────────────────────────────────────────────────────────────────

def parse_float(s: str, locale_name: str | None = None) -> float:
    """
    Parse a locale-formatted float string to a Python float.

    Example:
        parse_float("1.234,56", "de_DE.UTF-8")   # 1234.56
        parse_float("1,234.56")                   # 1234.56 (en_US)
    """
    if locale_name:
        with use_locale(locale_name):
            return locale.atof(s)
    return locale.atof(s)


def parse_int(s: str, locale_name: str | None = None) -> int:
    """
    Parse a locale-formatted integer string.

    Example:
        parse_int("1.234", "de_DE.UTF-8")   # 1234
        parse_int("1,234")                   # 1234 (en_US)
    """
    if locale_name:
        with use_locale(locale_name):
            return locale.atoi(s)
    return locale.atoi(s)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Locale conventions and collation
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class LocaleConventions:
    """Snapshot of locale.localeconv() for a given locale."""
    locale_name:      str
    decimal_point:    str
    thousands_sep:    str
    grouping:         list[int]
    currency_symbol:  str
    int_curr_symbol:  str
    frac_digits:      int
    mon_decimal_point: str
    mon_thousands_sep: str

    def __str__(self) -> str:
        return (f"{self.locale_name}: "
                f"dec={self.decimal_point!r} "
                f"thou={self.thousands_sep!r} "
                f"sym={self.currency_symbol!r} "
                f"frac={self.frac_digits}")


def get_conventions(locale_name: str) -> LocaleConventions:
    """
    Return locale convention data for a given locale name.

    Example:
        c = get_conventions("en_US.UTF-8")
        print(c.decimal_point, c.thousands_sep)   # "." ","
        c2 = get_conventions("de_DE.UTF-8")
        print(c2.decimal_point, c2.thousands_sep)  # "," "."
    """
    with use_locale(locale_name):
        lc = locale.localeconv()
    return LocaleConventions(
        locale_name=locale_name,
        decimal_point=lc["decimal_point"],
        thousands_sep=lc["thousands_sep"],
        grouping=lc["grouping"],
        currency_symbol=lc["currency_symbol"],
        int_curr_symbol=lc["int_curr_symbol"],
        frac_digits=lc["frac_digits"],
        mon_decimal_point=lc["mon_decimal_point"],
        mon_thousands_sep=lc["mon_thousands_sep"],
    )


def locale_sort(
    strings: list[str],
    locale_name: str | None = None,
    reverse: bool = False,
) -> list[str]:
    """
    Sort strings using locale-aware collation (strcoll/strxfrm).

    Example:
        locale_sort(["ä", "z", "a"], "de_DE.UTF-8")   # ["a", "ä", "z"]
        locale_sort(["café", "cave", "cab"])            # locale-ordered
    """
    if locale_name:
        with use_locale(locale_name, locale.LC_COLLATE):
            return sorted(strings, key=locale.strxfrm, reverse=reverse)
    return sorted(strings, key=locale.strxfrm, reverse=reverse)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== locale demo ===")

    # Try to use en_US; fall back gracefully if not installed
    def _try_locale(name: str) -> str | None:
        try:
            with use_locale(name):
                pass
            return name
        except locale.Error:
            return None

    en = _try_locale("en_US.UTF-8") or _try_locale("en_US") or ""
    de = _try_locale("de_DE.UTF-8") or _try_locale("de_DE") or ""

    # ── number formatting ──────────────────────────────────────────────────────
    print("\n--- format_number ---")
    for lname in filter(None, [en, de]):
        print(f"  [{lname}] format_number(1234567.89) = "
              f"{format_number(1234567.89, locale_name=lname or None)!r}")

    # ── currency ───────────────────────────────────────────────────────────────
    print("\n--- format_currency ---")
    for lname in filter(None, [en, de]):
        print(f"  [{lname}] format_currency(1234.50)   = "
              f"{format_currency(1234.50, locale_name=lname or None)!r}")

    # ── parse_float ────────────────────────────────────────────────────────────
    print("\n--- parse_float ---")
    if en:
        print(f"  [en_US] parse_float('1,234.56') = {parse_float('1,234.56', en)}")
    if de:
        print(f"  [de_DE] parse_float('1.234,56') = {parse_float('1.234,56', de)}")

    # ── format_percent ─────────────────────────────────────────────────────────
    print("\n--- format_percent ---")
    if en:
        print(f"  [en_US] format_percent(0.1234) = {format_percent(0.1234, locale_name=en)!r}")

    # ── localeconv snapshot ────────────────────────────────────────────────────
    print("\n--- get_conventions ---")
    for lname in filter(None, [en, de]):
        try:
            c = get_conventions(lname)
            print(f"  {c}")
        except Exception as e:
            print(f"  [{lname}] unavailable: {e}")

    # ── locale_sort ────────────────────────────────────────────────────────────
    print("\n--- locale_sort ---")
    words = ["café", "cave", "cab", "cap", "can"]
    if en:
        print(f"  [en_US] {locale_sort(words, en)}")
    # ASCII fallback
    print(f"  [C]     {sorted(words)}")

    # ── use_locale context manager ─────────────────────────────────────────────
    print("\n--- use_locale context manager ---")
    if de:
        with use_locale(de):
            num = locale.format_string("%.2f", 9876543.21, grouping=True)
        print(f"  [de_DE] 9876543.21 → {num!r}")

    print("\n=== done ===")

For the babel alternative — Babel (PyPI) provides locale-aware number and currency formatting without the thread-safety issues of locale.setlocale(), because Babel’s formatters accept a locale parameter per call (format_number(1234.56, locale="de_DE")) rather than mutating process-global state— use Babel in multi-threaded web applications (Flask, Django) where concurrent requests need different locales simultaneously, or when you need CLDR-backed data for 700+ locales; use the stdlib locale module for CLI tools, single-threaded scripts, or when you need POSIX collation via strcoll/strxfrm. For the decimal alternative — Python’s decimal.Decimal handles arbitrary-precision arithmetic and rounds monetary values correctly using ROUND_HALF_UP or ROUND_HALF_EVEN (banker’s rounding) — use decimal.Decimal for financial calculations to avoid IEEE 754 floating-point rounding errors (0.1 + 0.2 != 0.3); combine decimal with locale.currency() or Babel’s format_currency() for values that need both precise arithmetic and locale-aware display. The Claude Skills 360 bundle includes locale skill sets covering use_locale() thread-safe context manager with lock, format_number()/format_integer()/format_percent() number formatters, format_currency() monetary formatter with international flag, parse_float()/parse_int() locale-aware parsers, LocaleConventions dataclass with get_conventions(), and locale_sort() collation-aware sorter. Start with the free tier to try locale-aware formatting patterns and locale pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free