A PHP internationalization library, powered by CLDR data.

Related tags

Localization intl
Overview

intl

Build Status

A PHP 7.1+ internationalization library, powered by CLDR data.

Features:

  • NumberFormatter and CurrencyFormatter, inspired by intl.
  • Currencies
  • Languages

Looking for a list of countries and subdivisions? Check out commerceguys/addressing.

Why not use the intl extension?

The intl extension isn't present by default on PHP installs, requiring it can hurt software adoption. Behind the scenes the extension relies on libicu which includes the CLDR dataset, but depending on the OS/distribution used, could be several major CLDR releases behind.

Since the CLDR dataset is freely available in JSON form, it is possible to reimplement the intl functionality in pure PHP code while ensuring that the dataset is always fresh.

Having access to the CLDR dataset also makes it possible to offer additional APIs, such as listing all currencies.

More backstory can be found in this blog post.

Formatting numbers

Allows formatting numbers (decimals, percents, currency amounts) using locale-specific rules.

Two formatters are provided for this purpose: NumberFormatter and CurrencyFormatter.

use CommerceGuys\Intl\Currency\CurrencyRepository;
use CommerceGuys\Intl\NumberFormat\NumberFormatRepository;
use CommerceGuys\Intl\Formatter\NumberFormatter;
use CommerceGuys\Intl\Formatter\CurrencyFormatter;

$numberFormatRepository = new NumberFormatRepository;
// Options can be provided to the constructor or the
// individual methods, the locale defaults to 'en' when missing.
$numberFormatter = new NumberFormatter($numberFormatRepository);
echo $numberFormatter->format('1234.99'); // 1,234.99
echo $numberFormatter->format('0.75', ['style' => 'percent']); // 75%

$currencyRepository = new CurrencyRepository;
$currencyFormatter = new CurrencyFormatter($numberFormatRepository, $currencyRepository);
echo $currencyFormatter->format('2.99', 'USD'); // $2.99
// The accounting style shows negative numbers differently and is used
// primarily for amounts shown on invoices.
echo $currencyFormatter->format('-2.99', 'USD', ['style' => 'accounting']); // (2.99$)

// Arabic, Arabic extended, Bengali, Devanagari digits are supported as expected.
$currencyFormatter = new CurrencyFormatter($numberFormatRepository, $currencyRepository, ['locale' => 'ar']);
echo $currencyFormatter->format('1230.99', 'USD'); // US$ ١٬٢٣٠٫٩٩

// Parse formatted values into numeric values.
echo $currencyFormatter->parse('US$ ١٬٢٣٠٫٩٩', 'USD'); // 1230.99

Currencies

use CommerceGuys\Intl\Currency\CurrencyRepository;

// Reads the currency definitions from resources/currency.
$currencyRepository = new CurrencyRepository;

// Get the USD currency using the default locale (en).
$currency = $currencyRepository->get('USD');
echo $currency->getCurrencyCode(); // USD
echo $currency->getNumericCode(); // 840
echo $currency->getFractionDigits(); // 2
echo $currency->getName(); // US Dollar
echo $currency->getSymbol(); // $
echo $currency->getLocale(); // en

// Get the USD currency using the fr-FR locale.
$currency = $currencyRepository->get('USD', 'fr-FR');
echo $currency->getName(); // dollar des États-Unis
echo $currency->getSymbol(); // $US
echo $currency->getLocale(); // fr-FR

// Get all currencies, keyed by currency code.
$allCurrencies = $currencyRepository->getAll();

Languages

use CommerceGuys\Intl\Language\LanguageRepository;

// Reads the language definitions from resources/language.
$languageRepository = new LanguageRepository;

// Get the german language using the default locale (en).
$language = $languageRepository->get('de');
echo $language->getLanguageCode(); // de
echo $language->getName(); // German

// Get the german language using the fr-FR locale.
$language = $languageRepository->get('de', 'fr-FR');
echo $language->getName(); // allemand

// Get all languages, keyed by language code.
$allLanguages = $languageRepository->getAll();

Related projects

Laravel integration

Comments
  • Hey guys this is kind of big, when using the Language Repository I get an unknown language when calling get('en').

    Hey guys this is kind of big, when using the Language Repository I get an unknown language when calling get('en').

    • when using the Language Repository I get an unknown language when calling get('en')
    • also looping threw them there is no English whatsoever

    What is up with that?

    opened by dewwwald 7
  • Add support for currencyGroup and currencyDecimal

    Add support for currencyGroup and currencyDecimal

    As outlined in #80 , CLDR supports currencyGroup and currencyDecimal for de-AT and fr-CH.

    This PR adds support for these symbols by extracting the two relevant snippets in FormatterTrait::localizeNumber() and FormatterTrait::parseNumber() into two methods which are then overridden in CurrencyFormatter.

    I've also added tests for the two supported locales for both currencies (to ensure it's working) and numbers (to ensure it only affects currencies).

    Thank you for this package, I'm happily using it in a few projects!

    Let me know if you'd like me to make any changes.

    opened by limenet 5
  • Walloon should not be filtered out

    Walloon should not be filtered out

    • Walloon (official iso 'wa' code)
    • Taiwanese (or Taiyu)
    • Montenegrin (official iso 'cnr')

    We make some translations in and to these languages. Could you please add them so we can add them to our website? Or how can we add them ourselves?

    Thank you

    opened by herrwalter 5
  • spanish, euskera, gallego and catalán have the same number_format

    spanish, euskera, gallego and catalán have the same number_format

    In catalan, gallego or euskera cultures, we expect the same number format as in spanish es_ES culture, ie:

    spanish (es or es_ES): 12,5 € euskera (eu or eu_ES): 12,5 € gallego (gl or gl_ES): 12,5 €

    opened by asiermarques 5
  • Inuktitut is omitted even though it is not in the $ignoredLocales array

    Inuktitut is omitted even though it is not in the $ignoredLocales array

    Hello,

    I'm looking into using this library for a project in Canada and as such need support for Inuktitut (iu) (which is an official language in the territory of Nunavut). I notice that Inuktitut does not appear in the generated resources, even though it is not one of the $ignoredLocales: https://github.com/commerceguys/intl/blob/90b4f75c4917927a1960c0dcaa002a91ab97f5d5/scripts/generate_base.php#L31-L57

    I appreciate the rationale behind #79 but I'm wondering:

    1. Why it's not included in the library even though it's not explicitly ignored.
    2. If this language could be included in the library, as there are regulatory requirements for its support in Nunavut which our application must meet.

    Thanks in advance!

    opened by greatislander 4
  • Run Travis CI for PHP 7.4

    Run Travis CI for PHP 7.4

    When I submitted #82 , I noticed Travis wasn't testing against PHP 7.4. This PR also lists PHP 7.4 (current stable) as test targets for Travis.

    Reason why I created a separate PR is to address potential PHP 7.4 failures separately from the new features in my other PR. I hope that's oaky!

    opened by limenet 4
  • Implement rounding in the formatters

    Implement rounding in the formatters

    Every single number formatter out there (intl, number_format, Java's DecimalFormatter, the Ruby on Rails helper) also rounds. I've always found it bizarre (showing a value should not modify it), but silently truncating (like we do now) isn't ideal either.

    The generally expected rounding modes are: ceiling, floor, up, down, half up, half down, half even (default in intl/icu), half odd. We can support the last four with round(). The first two will be tricky because ceil() and floor() have floating point bugs (which round() doesn't). Rounding down is truncating. Rounding up would need special code.

    Maybe we can get away with supporting only the round() ones?

    opened by bojanz 4
  • Maximum and minimum fraction digits are always set in defaultOptions

    Maximum and minimum fraction digits are always set in defaultOptions

    This line of code https://github.com/commerceguys/intl/blob/0fc176165f45d9f3dd9af50a05293c3fa99e4691/src/Formatter/CurrencyFormatter.php#L105 and also https://github.com/commerceguys/intl/blob/0fc176165f45d9f3dd9af50a05293c3fa99e4691/src/Formatter/CurrencyFormatter.php#L108 makes no sense because in defaultOptions those array items are already set, so if your options array doesn't contain the minimum_fraction_digits or/and maximum_fraction_digits they will never be taken from the Currency config with the method getFractionDigits().

    This causes the problems like this:

    1. Currency is EUR
    2. Currency fractionDigits is 2 (set in yml file, talking here about Drupal 8 implementation).
    3. In price formatter no minimum or maximum fraction digits is set, so the format method in CurrencyFormatter uses the defaultOptions instead, they have as values: null, but still isset() returns true in lines 105 and 108.
    4. Formatted price with number 5.123123 is shown like 5.123123 instead of 5.12 .

    Proposed solution:

    • replace !isset($options['minimum_fraction_digits'])) to empty($options['minimum_fraction_digits']))
    • replace !isset($options['maximum_fraction_digits'])) to empty($options['maximum_fraction_digits']))

    This way null value will force the method to get the settings from the Currency itself.

    Pull request link: https://github.com/commerceguys/intl/pull/77

    opened by artemvd 2
  • Example in readme makes no sense.

    Example in readme makes no sense.

    One of the examples in the readme makes no sense.

    How can 1234.99 suddenly equal 123,456.99?

    echo $numberFormatter->format('1234.99'); // 123,456.99
    
    opened by viovet-dan 2
  • NumberFormatter no longer accepts floats

    NumberFormatter no longer accepts floats

    I've noticed that NumberFormatter underwent some changes from 0.* tot 1.*. More specifically the percent style got some breaking changes. I'm just curious if the following ones are intended.

    // version 0.*
    $formatter->format(75, ['style' => 'percent']); // 75%
    
    // version 1.*
    $formatter->format(75, ['style' => 'percent']); // 7500%
    
    // version 0.*
    $formatter->format(0.75, ['style' => 'percent']); // 0.75%
    
    // version 1.*
    $formatter->format(0.75, ['style' => 'percent']); // Exception (floats cannot be passed)
    $formatter->format('0.75', ['style' => 'percent']); // 75%
    
    opened by Propaganistas 2
  • Currency codes for CN, VG and ZA are incorrect

    Currency codes for CN, VG and ZA are incorrect

    Thanks for your great library!

    The currency codes in base.json for CN, VG and ZA are incorrect due to the array ordering of the CLDR data. (See currencyData.json#L979, #L3221, and #L3348.) CN has CNX instead of CNY, VG has GBP instead of USD and ZA has ZAL instead of ZAR. These are all historic or non-tender currencies.

    In most countries in the list, the current currency is the last in the array, and in generate.php#L78 you are using key(end($currencyData['region'][$countryCode])) to retrieve it. These three countries are differently sorted for some reason, and the current currency is not the last in the array.

    Rather than rely on the sorting, which could be arbitrary (although it only has these 3 exceptions at the moment), could you use the _from, _to and _tender values to filter the currency array before returning the last key?

    Here is a messy example that I made quickly to identify what was wrong with these three countries:

    // Determine the current currency for this country.
    if (isset($currencyData['region'][$countryCode])) {
    
        $currencyCandidates = $currencyData['region'][$countryCode];
        $currencyCandidates = array_filter($currencyData['region'][$countryCode], function($currency) {
    
            $currency = end($currency);
    
            if (!isset($currency['_tender']) || $currency['_tender'] !== 'false') {
                $active = isset($currency['_to']) ? new DateTime <= new DateTime($currency['_to']) : true;
                $active = $active && (isset($currency['_from']) ? new DateTime >= new DateTime($currency['_from']) : true);
    
                return $active;
            }
    
        });
    
        if (!empty($currencyCandidates)) {
            $currentCurrency = key(end($currencyCandidates));
        }
        else {
            $currentCurrency = null;
        }
    
        $baseData[$countryCode]['currency_code'] = $currentCurrency;
    }
    
    opened by kitbs 2
  • Investigate quirks when rendering prices in Farsi

    Investigate quirks when rendering prices in Farsi

    Our number format for Farsi ("fa") puts the currency symbol before the amount.

    However, PHP's native NumberFormatter varies the symbol location based on the currency:

    $formatter = new \NumberFormatter('fa', \NumberFormatter::CURRENCY);
    echo $formatter->formatCurrency('645000', 'USD'); // Symbol before the amount
    echo $formatter->formatCurrency('645000', 'IRR'); // Symbol after the amount
    

    This could be related to #65, where our logic is too simplistic.

    opened by bojanz 0
  • Fix number trim in case of multibyte characters

    Fix number trim in case of multibyte characters

    As discussed in https://www.drupal.org/project/commerce/issues/2936294, trim() only works for ASCII and does not work on multibyte utf8 characters. Let's use a preg_replace() instead.

    opened by czigor 3
  • The currency symbol/code is not always properly spaced

    The currency symbol/code is not always properly spaced

    We currently rely on the pattern to give us the complete layout of the final number. But CLDR has additional rules that say when a space should be inserted around a currency symbol/code, which look like this:

    "currencySpacing": {
                "beforeCurrency": {
                  "currencyMatch": "[:^S:]",
                  "surroundingMatch": "[:digit:]",
                  "insertBetween": " "
                },
                "afterCurrency": {
                  "currencyMatch": "[:^S:]",
                  "surroundingMatch": "[:digit:]",
                  "insertBetween": " "
                }
              },
    

    Yes, that's quite confusing, which is why I missed it previously.

    Looks like this is a good opportunity to check how our formatting logic compares with the ICU4J one.

    Relevant links: https://github.com/angular/angular/issues/20708 https://github.com/andyearnshaw/Intl.js/issues/221

    opened by bojanz 1
  • Update README.md

    Update README.md

    Show an example that would have helped me out. This examples shows the conversion of a number from english to french and highlights how a number should be parsed in it's source language before formatting in the target language.

    opened by cosmicdreams 0
  • Date formatting plans?

    Date formatting plans?

    First of all, thanks for all the hard work put into this, really useful for myself and my team. This is not really an issue report but more of a question.

    Do you guys have any estimation/plans regarding introducing date formats support? Thanks

    opened by nocive 5
Releases(v1.1.1)
Owner
Commerce Guys
Commerce Guys
Internationalization tools, particularly message translation.

Aura.Intl The Aura.Intl package provides internationalization (I18N) tools, specifically package-oriented per-locale message translation. Installation

Aura for PHP 86 Dec 18, 2022
pH7CMS Internationalization package.

?? pH7CMS Internationalization (I18N) package ?? Get new languages for your pH7CMS website!

pH7 Social Dating CMS (pH7CMS) 18 Oct 5, 2022
PHP library to collect and manipulate gettext (.po, .mo, .php, .json, etc)

Gettext Note: this is the documentation of the new 5.x version. Go to 4.x branch if you're looking for the old 4.x version Created by Oscar Otero http

Gettext 651 Dec 29, 2022
Geographer is a PHP library that knows how any country, state or city is called in any language

Geographer Geographer is a PHP library that knows how any country, state or city is called in any language. Documentation on the official website Incl

Menara Solutions 757 Nov 24, 2022
Official PHP library for the DeepL language translation API.

deepl-php Official PHP client library for the DeepL API. The DeepL API is a language translation API that allows other computer programs to send texts

DeepL 78 Dec 23, 2022
🗓 A library to help you work with dates in multiple languages, based on Carbon.

Date This date library extends Carbon with multi-language support. Methods such as format, diffForHumans, parse, createFromFormat and the new timespan

Jens Segers 1.8k Dec 30, 2022
[virion] Language management library for automatic translation

libtranslator :: library for automatic translation ✔️ Multilingual support for plugin messages ✔️ Translation language is set according to the player

PocketMine-MP projects of PresentKim 5 Jul 29, 2022
Patchwork UTF-8 for PHP: Extensive, portable and performant handling of UTF-8 and grapheme clusters for PHP

Patchwork UTF-8 for PHP Patchwork UTF-8 gives PHP developpers extensive, portable and performant handling of UTF-8 and grapheme clusters. It provides

Nicolas Grekas 80 Sep 28, 2022
A convenience package for php multilingual web applications

PHP Translation Install Lifecycle Configuration Content of PHP File Content of Json File Content Of Database Table Use Of Array Or Json Database PHP T

Ahmet Barut 3 Jul 7, 2022
A morphological solution for Russian and English language written completely in PHP.

Morphos A morphological solution for Russian and English language written completely in PHP. Tests & Quality: Features [✓] Inflection of Personal name

Sergey 723 Jan 4, 2023
Composer package providing translation features for PHP apps

PHP translation This is a composer package providing translation support for PHP applications. It is similar to gettext, in usage, with these differen

Sérgio Carvalho 0 Aug 15, 2022
FBT - a internationalization framework for PHP designed to be not just powerful and flexible, but also simple and intuitive

FBT is an internationalization framework for PHP designed to be not just powerful and flexible, but also simple and intuitive. It helps with the follo

Richard Dobroň 4 Dec 23, 2022
Internationalization tools, particularly message translation.

Aura.Intl The Aura.Intl package provides internationalization (I18N) tools, specifically package-oriented per-locale message translation. Installation

Aura for PHP 86 Dec 18, 2022
pH7CMS Internationalization package.

?? pH7CMS Internationalization (I18N) package ?? Get new languages for your pH7CMS website!

pH7 Social Dating CMS (pH7CMS) 18 Oct 5, 2022
Melek Berita Backend is a service for crawling data from various websites and processing the data to be used for news data needs.

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Chacha Nurholis 2 Oct 9, 2022
Icônes Form Widget for October and winter CMS. One library, over 100,000 vector icons, and 108+ icon sets powered by Iconify

Icônes Form Widget ?? ?? ?? Access thousands of SVG icons as a backend form widget. One library, over 100,000 vector icons, Modern replacement for ico

Adil Chehabi 12 Aug 29, 2022
Shoutit is a PHP powered shoutbox which uses mySQL to store shouts

This is a very basic PHP/MySQL application. built to prove my usuage of basic php + MySQL. It is very easy to use, has error checking, and very fast. Users can post shouts along with their names & comment.

Roman Hossain Shaon 10 Oct 10, 2022
A PHP CMS powered by Laravel 5 and Sentry

Bootstrap CMS Bootstrap CMS was created by, and is maintained by Graham Campbell, and is a PHP CMS powered by Laravel 5.1 and Sentry. It utilises many

Bootstrap CMS 2.5k Dec 27, 2022
A magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, & Livewire.

Malzahar A magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, & Livewire. Re

null 26 Nov 17, 2022