AshReports.Cldr (ash_reports v0.1.0)
CLDR backend module for AshReports internationalization.
This module provides comprehensive locale support for AshReports, including number formatting, currency formatting, date/time formatting, and locale management. It serves as the central CLDR backend for all internationalization functionality within the reporting system.
Features
- Locale Management: Support for 10+ major world locales with fallback
- Number Formatting: Locale-aware decimal, percentage, and scientific notation
- Currency Formatting: Multi-currency support with proper symbols and formatting
- Date/Time Formatting: Locale-specific date and time representations
- Performance Optimization: Efficient locale switching with format caching
- Fallback Support: Graceful degradation to default locale when needed
Supported Locales
The system supports the following locales with comprehensive formatting rules:
- English:
en(default) - English (UK):
en-GB - Spanish:
es - French:
fr - German:
de - Italian:
it - Portuguese:
pt - Japanese:
ja - Chinese (Simplified):
zh - Chinese (Traditional):
zh-Hant - Korean:
ko - Russian:
ru - Arabic:
ar - Hindi:
hi
Configuration
Configure the CLDR backend in your application config:
config :ash_reports, AshReports.Cldr,
default_locale: "en",
locales: ["en", "en-GB", "es", "fr", "de"],
providers: [
Cldr.Number,
Cldr.Currency,
Cldr.DateTime,
Cldr.Calendar
]Usage Examples
Basic Locale Operations
# Set current locale
AshReports.Cldr.set_locale("fr")
# Get current locale
locale = AshReports.Cldr.current_locale()
# Check if locale is supported
AshReports.Cldr.locale_supported?("de")Number Formatting
# Format numbers with locale-specific formatting
AshReports.Cldr.format_number(1234.56, locale: "fr")
# => "1 234,56"
AshReports.Cldr.format_number(1234.56, locale: "en")
# => "1,234.56"Currency Formatting
# Format currency amounts
AshReports.Cldr.format_currency(1234.56, :USD, locale: "en")
# => "$1,234.56"
AshReports.Cldr.format_currency(1234.56, :EUR, locale: "fr")
# => "1 234,56 €"Date/Time Formatting
# Format dates with locale-specific patterns
date = ~D[2024-03-15]
AshReports.Cldr.format_date(date, locale: "en", format: :long)
# => "March 15, 2024"
AshReports.Cldr.format_date(date, locale: "fr", format: :long)
# => "15 mars 2024"Integration with Renderers
The CLDR backend integrates seamlessly with all Phase 3 renderers:
- HTML Renderer: Locale-aware CSS direction and number formatting
- PDF Renderer: Locale-specific typography and formatting
- JSON Renderer: Locale metadata and formatted data output
- HEEX Renderer: Phoenix LiveView i18n integration
Performance Considerations
- Format Caching: Compiled formatters are cached for performance
- Lazy Loading: Locale data is loaded on-demand
- Memory Efficiency: Only requested locales are kept in memory
- Process Safety: Locale state is process-isolated
Error Handling
The module provides graceful error handling with fallback mechanisms:
- Unsupported locales fall back to the default locale
- Invalid format options use sensible defaults
- Formatting errors return the original value with a warning
Summary
Functions
Gets the current process locale.
Gets the decimal separator for a locale.
Detects the locale from various sources.
Formats a currency amount according to the specified locale.
Formats a date according to the specified locale and format style.
Formats a datetime according to the specified locale and format style.
Formats a number according to the specified locale and options.
Formats a time according to the specified locale and format style.
Checks if a locale is supported by the CLDR backend.
Sets the locale for the current process.
Gets locale-specific text direction (ltr or rtl).
Gets the thousands separator for a locale.
Functions
@spec current_locale() :: String.t()
Gets the current process locale.
Returns the locale set for the current process, or the default locale if no locale has been explicitly set.
Examples
iex> AshReports.Cldr.current_locale()
"en"
Gets the decimal separator for a locale.
Examples
iex> AshReports.Cldr.decimal_separator("en")
"."
iex> AshReports.Cldr.decimal_separator("fr")
","
@spec default_locale() :: String.t()
Detects the locale from various sources.
Attempts to detect the locale from:
- Explicit locale parameter
- Process locale setting
- HTTP Accept-Language header
- Application configuration
- System locale
- Default fallback locale
Parameters
sources- Optional map containing locale detection sources::locale- Explicit locale:accept_language- HTTP Accept-Language header:user_preference- User's saved locale preference
Examples
iex> AshReports.Cldr.detect_locale(%{locale: "fr"})
{:ok, "fr"}
iex> AshReports.Cldr.detect_locale(%{accept_language: "en-GB,en;q=0.9"})
{:ok, "en-GB"}
@spec format_currency(number(), atom() | String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
Formats a currency amount according to the specified locale.
Parameters
amount- The monetary amount to formatcurrency- The currency code (atom or string)options- Formatting options including::locale- The locale to use for formatting (default: current locale):format- The currency format style (:standard, :accounting, :short)
Examples
iex> AshReports.Cldr.format_currency(1234.56, :USD)
{:ok, "$1,234.56"}
iex> AshReports.Cldr.format_currency(1234.56, :EUR, locale: "fr")
{:ok, "1 234,56 €"}
iex> AshReports.Cldr.format_currency(-500, :USD, format: :accounting)
{:ok, "($500.00)"}
@spec format_date( Date.t() | DateTime.t() | NaiveDateTime.t(), keyword() ) :: {:ok, String.t()} | {:error, term()}
Formats a date according to the specified locale and format style.
Parameters
date- The date to format (Date, DateTime, or NaiveDateTime)options- Formatting options including::locale- The locale to use for formatting (default: current locale):format- The format style (:short, :medium, :long, :full) or custom pattern
Examples
iex> date = ~D[2024-03-15]
iex> AshReports.Cldr.format_date(date, format: :long)
{:ok, "March 15, 2024"}
iex> AshReports.Cldr.format_date(date, locale: "fr", format: :long)
{:ok, "15 mars 2024"}
@spec format_datetime( DateTime.t() | NaiveDateTime.t(), keyword() ) :: {:ok, String.t()} | {:error, term()}
Formats a datetime according to the specified locale and format style.
Parameters
datetime- The datetime to formatoptions- Formatting options including::locale- The locale to use for formatting (default: current locale):date_format- The date format style:time_format- The time format style:format- Combined datetime format style
Examples
iex> datetime = ~U[2024-03-15 14:30:00Z]
iex> AshReports.Cldr.format_datetime(datetime)
{:ok, "Mar 15, 2024, 2:30:00 PM"}
Formats a number according to the specified locale and options.
Parameters
number- The number to formatoptions- Formatting options including::locale- The locale to use for formatting (default: current locale):format- The format style (:decimal, :currency, :percent, :scientific):precision- Number of decimal places:currency- Currency code when using :currency format
Examples
iex> AshReports.Cldr.format_number(1234.56)
{:ok, "1,234.56"}
iex> AshReports.Cldr.format_number(1234.56, locale: "fr")
{:ok, "1 234,56"}
iex> AshReports.Cldr.format_number(0.1234, format: :percent)
{:ok, "12.34%"}
@spec format_time( Time.t() | DateTime.t() | NaiveDateTime.t(), keyword() ) :: {:ok, String.t()} | {:error, term()}
Formats a time according to the specified locale and format style.
Parameters
time- The time to format (Time, DateTime, or NaiveDateTime)options- Formatting options including::locale- The locale to use for formatting (default: current locale):format- The format style (:short, :medium, :long, :full) or custom pattern
Examples
iex> time = ~T[14:30:00]
iex> AshReports.Cldr.format_time(time)
{:ok, "2:30:00 PM"}
iex> AshReports.Cldr.format_time(time, locale: "fr")
{:ok, "14:30:00"}
Checks if a locale is supported by the CLDR backend.
Examples
iex> AshReports.Cldr.locale_supported?("en")
true
iex> AshReports.Cldr.locale_supported?("xx-XX")
false
Sets the locale for the current process.
Parameters
locale- The locale to set (must be supported)
Examples
iex> AshReports.Cldr.set_locale("fr")
:ok
iex> AshReports.Cldr.set_locale("xx-XX")
{:error, "Unsupported locale: xx-XX"}
Gets locale-specific text direction (ltr or rtl).
Examples
iex> AshReports.Cldr.text_direction("en")
"ltr"
iex> AshReports.Cldr.text_direction("ar")
"rtl"
Gets the thousands separator for a locale.
Examples
iex> AshReports.Cldr.thousands_separator("en")
","
iex> AshReports.Cldr.thousands_separator("fr")
" "