Skip to content

Transition from currency-aware formatting to locale-aware formatting #98

@fabiocarneiro

Description

@fabiocarneiro

The money2 library currently relies on a "self-sustained" approach where formatting logic and patterns are hardcoded within currency definitions. While there is a shared understanding within the community that currency-based formatting is not ideal, the solution discussed thus far has been to build an internal database of locales.

I propose that we instead adopt the intl package as a core dependency for formatting. While this library will continue to maintain its own dataset of currencies (ISO codes, names, and precision), we should offload the responsibility of locale-specific representation to a dedicated, widely-used localization engine.

The Problem: The Limits of Self-Sustained Logic

The current model treats formatting as a property of the Currency, whereas industry standards treat it as a property of the Locale.

  • Incompatibility with Device Settings: Formatting is "locked" at the app or currency level and does not automatically adjust to the user's device settings. A user in Germany and a user in the US viewing the same currency should see different separators (e.g., $1.000,00 vs. $1,000.00), but the library currently forces a static style.
  • Diminishing Returns on Heuristics: As seen in PR parse decimal inference #97 ("parse decimal inference"), we have implemented heuristics to improve separator accuracy without breaking backward compatibility. While PR parse decimal inference #97 was a successful stop-gap, it highlights that we are now relying on "guessing" intent. This is inherently more fragile than using a standardized, data-driven locale engine.
  • The Maintenance Trap of Localization: While this library must maintain currency-specific data (which is not provided by intl), maintaining a separate database of locales is a massive undertaking. Leveraging intl allows us to focus on financial logic while letting a standard library handle the regional display logic.

Proposed Solution: Standardized Dependency for Formatting

Rather than reinventing the localization wheel, we should leverage the infrastructure provided by the intl package:

  1. Adopt intl as the Internal Formatting Engine: Replace custom formatting logic and heuristics with the standard intl library.
  2. Preserve the Fowler Pattern: The Money and Currency objects should remain clean Value Objects. Formatting logic should be decoupled into a locale-aware resolver called internally by .format() or .toString().
  3. Initialization-Phase Warning: Since it is difficult to distinguish between legacy and new format strings during runtime usage, I propose a warning during library or currency initialization. This informs the developer that the library is moving toward a locale-first model and that the current approach of declaring formats via the currency is being deprecated.

Areas for Discussion: Fallback and Deprecation

  • Deprecation Acknowledgment: How should developers acknowledge this shift to silence the initialization warning? (e.g., a global configuration flag like MoneyConfig.acknowledgeLocaleMigration()).
  • Fallback Strategy: What should happen if a Locale cannot be automatically detected or resolved?
    • Option A: Fail Initialization: Make intl a hard requirement and throw an error if no valid locale context is found.
    • Option B: Neutral/ISO Fallback: Fallback to a neutral "Safety" format (e.g., USD 1,234.56).
    • Option C: Global Default: Allow a Money.defaultLocale to be defined once at the application level.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions