Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions