Skip to content

Implement MoneyFormatter with Intl support #11

Open

Description

@henriquemoody

Summary

Create a MoneyFormatter that uses PHP's Intl extension (NumberFormatter) for currency formatting with a clean, minimal API.

Proposed Design

The formatter should leverage NumberFormatter for locale-aware currency formatting while keeping a simple interface:

use Respect\StringFormatter\MoneyFormatter;

// Simple case - uses default locale
$formatter = new MoneyFormatter('USD');
echo $formatter->format('1234.56');
// Output: "$1,234.56"

// With custom locale
$formatter = new MoneyFormatter('BRL', 'pt_BR');
echo $formatter->format('1234.56');
// Output: "R$ 1.234,56"

// With custom precision
$formatter = new MoneyFormatter('JPY', precision: 0);
echo $formatter->format('1234.56');
// Output: "¥1,235"

Constructor Signature

public function __construct(
    string $currency,
    string|null $locale = null,
    int|null $precision = null
)
  • $currency: ISO 4217 currency code (e.g., 'USD', 'EUR', 'BRL')
  • $locale: Optional locale for formatting (defaults to system locale)
  • $precision: Optional decimal precision (defaults to currency's default)

Key Features

  1. Intl integration: Use NumberFormatter::CURRENCY for all formatting
  2. Locale-aware: Respect locale-specific formatting (symbol position, separators)
  3. Precision control: Override currency defaults when needed
  4. Error handling: Validate currency codes and throw InvalidFormatterException
  5. Currency validation: Use NumberFormatter to verify valid currency codes

Questions/Decisions Needed

  • Should we support currency code-only output (no symbol)?
  • Should we support negative numbers with specific formatting?
  • Should we format as accounting style for negatives?

Activity

  1. alganet commented on Feb 5, 2026

    @alganet
    Member

    It would be weird for an user of Respect\Validation that wants to format a template to use currency formatting to need both intl and sokil/iso-codes at the same time.

    We should probably take a step back and rethink how we manage graceful fallbacks in internationalization dependencies, and work on a strategy that works for all our package offerings.

  2. henriquemoody commented on Feb 6, 2026

    @henriquemoody
    MemberAuthor

    I agree, we need to rethink that aspect. I think we can use them for different scenarios.

    Unfortunately, we cannot choose one or another, because they provide different things. Intl doesn't have support for country subdivisions, for example, we still need sokil/iso-codes for that.

  3. alganet commented on Feb 6, 2026

    @alganet
    Member

    CLDR does offer subdivisions and also currency data. It also offers unit information (metric, imperial), and all sorts of things (emoji descriptions, keyboard mappings, etc).

    But I don't think it's complete for currencies (it doesn't have symbol-narrow for R$ for example). Also, no postal codes or TLD-country association.

    From my initial research, there is no silver bullet database that offers everything.

    Also, from a developer's perspective, I almost never want to fully internationalize to all countries. What I want is english and a couple of other languages. Full I18N+L10N is a rare need (but worth pursuing). It would be nice to have a library that supports granular imports:

        dependencies:
            - regional-data-core
            - regional-data-locale-pt-BR
            - regional-data-locale-en-US
    

    Each granule would only contain data for the locale it needs (the intersection of a language and a country).

    We could then rely on such regional-data-core and leave the database management to the library user.

    Unfortunatelly, there is nothing like that around, except in distros like debian that do offer this kind of granularity.

  4. henriquemoody commented on Feb 6, 2026

    @henriquemoody
    MemberAuthor

    CLDR is indeed amazing.

    There's a PHP package (or packages) that encapsulate it all. Ideally, we would have a collection of packages that could be installed according to specific needs, for example composer require cldr/languages, composer require cldr/countries, composer require cldr/currencies.

  5. alganet commented on Feb 6, 2026

    @alganet
    Member

    I actually want both. I want to be able to do:

     - dependencies: [ locale-data/currencies, locale-data/en, locale-data/es ] # A financial app in english and spanish
     - dependencies: [ locale-data/en-us, locale-data/en-ca, locale-data/fr-ca locale-data/es-mx ] # An app specific for north americans
     - ...
    

    Specific slices with some overlap (for example, currencies would have lots of currency data but no translations).

    By the way I just reserved github.com/locale-data 🐼 I've been digging this hole for the past few days, and it's deep. I think I can reasonably make a solid package collection in 3 months or so. Until then, if there's a package that offers less granular data, I would gladly accept it.

  6. alganet commented on Feb 22, 2026

    @alganet
    Member

    I strongly suggest using some kind of currency polyfill for now.

    I have in fact started to work on a common data repository, which is here: https://github.com/data-common/meta

    I decided to make it agnostic (not PHP related), and leverage my portable shell scripting skills for it. It has become a passion project, and I cannot delay delivering this feature here because of it.

    In a near future, I expect to provide data-common/currencies, data-common/domains, and data-common/locale-* packages with granular information ready to use in several programming languages.

    I'm currently investigating how to make one repo export packages for lots of different package managers, so one can either composer require or npm or cargo or whatever from the same source.

    All of this might take a while.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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