OroCRM Localization

Localization or internationalization process means adapting a software to different languages, regional particularities and technical requirements of a target market. Well-designed software usually does not require code changes and deep engineering knowledges for localization or/and internationalization processes.

For example, we can use different address formats depending on a country:

Great Britain (United Kingdom, England, Scotland):
Mr. Walter C. Brown
49 Featherstone Street
LONDON
EC1Y 8SY
UNITED KINGDOM
United States of America:
John Doe
Smallsys inc
795 e Dragram
Tucson az 85705
USA
Germany:
Herrn                      
John Doe
Wittekindshof 
Schulstrasse 4
32547 Bad Oyenhausen
GERMANY
Ukraine:
John Doe
ul. Astronomicheskaya 22, kv. 33
Kharkov
12345
UKRAINE

In OroCRM every user can set up a localization for his own location. It is possible to use predefined set of localizations or create a new one.
For that go to System > Configuration > Localization:

localization options
OroCRM has a lot of ways for customizing locale options. Locale affects formatting of numbers, addresses, names and dates:

– Primary Location – if format address per country option is disabled, or there is no country-specific formatting, the addresses will be formatted according to the rules of primary location.

– Format Address Per Country – when this option is selected, addresses will be formatted according to the rules of their countries. Otherwise, the primary location formatting will be applied.

– First Quarter Starts On – defines the starting date of the first quarter in a year.

– Timezone – defines the timezone of your location. All dates and times will be displayed in the specified timezone.

– Currency – defines the default currency.

– Temperature Unit- defines the temperature unit. These settings will be applied for weather on the map.

– Wind Speed Unit – defines the wind speed unit. These settings will be applied for weather on the map.

Also, you can get the settings in your code, for that you need to call the service oro_locale.settings.
For example:

PHP

$localeSettings = $this->get('oro_locale.settings');
$language = $localeSettings->getLanguage();
$calendar = $localeSettings->getLanguage();
$country = $localeSettings->getCountry();
$timeZone = $localeSettings->getTimeZone();

Twig

{{ oro_locale() }}
{{ oro_currency() }}

JS

require(['orolocale/js/locale-settings’],
function(localeSettings){
localeSettings.getLanguage();
localeSettings.getCountry();
});

As you can see, OroCRM has really powerful facilities to localize your application. Below we will describe the main items of localization.

Name formating

As you may also know, different countries and regions have different conventions for formatting users names. The name formats are grouped by locale, and they can be found in name_format.yml.
For example: vendor/oro/platform/src/Oro/Bundle/LocaleBundle/Resources/config/oro/name_format.yml

en: %prefix% %first_name% %middle_name% %last_name% %suffix%
en_US: %prefix% %first_name% %middle_name% %last_name% %suffix%
ru: %last_name% %first_name% %middle_name%
ru_RU: %last_name% %first_name% %middle_name%

To format name with specific locale format use the following code:

PHP

// for en_US locale
$formatter = $container->get('oro_locale.formatter.name');
$person->setNamePrefix('Mr.');
$person->setFirstName(John);
$person->setMiddleName(J);
$person->setLastName(Doe);
$person->setNameSuffix('Sn.');
$formatter->format($person, 'en_US');

// for ru_RU locale
$formatter = $container->get('oro_locale.formatter.name');
$person->setFirstName(John);
$person->setMiddleName(J);
$person->setLastName(Doe);
$formatter->format($person, ru_RU);

Twig

Use format method from name formatter, it has similar logic:

{{ user|oro_format_name }}

JS

require(['orolocale/js/formatter/name'],
function(nameFormatter) {
   var formattedName = nameFormatter.format({
       prefix: 'Mr.',
       first_name: John,
       middle_name: J,
       last_name: Doe,
       suffix: 'Sn.'
   });
});

Address Formatting

Like in case of names, address format may depend on different conventions. Address formats are grouped by country and they can be found in address_format.yml.

Example of format configuration for US:
vendor/oro/crm/src/OroCRM/Bundle/MagentoBundle/Resources/config/oro/address_format.yml

PN:
   format: '%name%\n%organization%\n%street%\n%CITY%\n%COUNTRY%\n%POSTAL_CODE%'
   require: [street, city, postal_code]
PR:
   format: '%NAME%\n%ORGANIZATION%\n%STREET%\n%CITY% PR %postal_code%\n%COUNTRY%'
   require: [street, city, postal_code]
   zip_name_type: zip
   postprefix: PR

PHP

$formatter = $container->get('oro_locale.formatter.address');
$region->getCode();
$country->getIso2Code();
$region->setCountry($country);
$address = new Address();
$address->setStreet('16243 Ivy Lake Dr.');
$address->setCity('Odessa');
$address->setRegion($region);
$address->setPostalCode(33556);
$address->setOrganization('Atwix');
$address->setCountry($country);
$formatter->format($address);

Twig

{{ address|oro_format_address('US') }}

JS

require(['orolocale/js/formatter/address'],
function(addressFormatter) {
   var data = this.model.toJSON();
   data.formatted_address = addressFormatter.format({
       prefix: 'Mr.',
       suffix: 'Sn.',
       first_name: 'John',
       middle_name: 'J',
       last_name: 'Doe',
       organization: 'Atwix',
       street: '16243 Ivy Lake Dr.',
       city: 'Odessa',
       country: 'US',
       country_iso2: data.countryIso2,
       country_iso3: data.countryIso3,
       postal_code: '33556',
       region: data.region,
       region_code: data.regionCode
   });
});

Data and Time formating

Countries may also have different conventions for formatting data and time.
Each format uses its own localized format for date and time.
Pay attention that default date format is \IntlDateFormatter::MEDIUM, and default time format is \IntlDateFormatter::SHORT.

The “format” functions provide a basic functionality for date and time representation changes.
It allows setting up the date and time format types from Intl library: current locale as a string, current timezone as a string and custom format pattern (in this case, date and time format types will not be used).

PHP DateTime Formatter:

$formatter->format(new \DateTime('2015-12-12 09:00:00'));
// Dec 12, 2015 09:00 AM

$formatter->format(new \DateTime('2015-12-12 09:00:00'), \IntlDateFormatter::FULL, \IntlDateFormatter::MEDIUM);
// Thursday, December 12, 2015 09:00:00 AM

$formatter->format(new \DateTime('2015-12-12 09:00:00'), null, null, 'ru');
// 12.12.2015 9:00

$formatter->format(new \DateTime('2015-12-12 09:00:00'), null, null, null, 'America/Los_Angeles');
// Dec 12, 2015 9:00 AM

$formatter->format(new \DateTime('2015-12-12 09:00:00'), null, null, null, null, 'yyyy-MM-dd|HH:mm:ss');
// 2015-12-12|09:00:00

$formatter->formatDate(new \DateTime('2015-12-12 09:00:00'));
// Dec 12, 2015

$formatter->formatDate(new \DateTime('2015-12-12 09:00:00'), \IntlDateFormatter::FULL);
// Thursday, December 12, 2015

$formatter->formatDate(new \DateTime('2015-12-12 09:00:00'), null, 'ru');
// 12.12.2015

$formatter->formatDate(new \DateTime('2015-12-12 09:00:00'), null, null, 'America/Toronto');
// Dec 12, 2015

OroPlatform application has several libraries that work with DateTime values.
Each library has its own DateTime format placeholders. And note that for every library must be a format converter that contains rules of converting standard internal format to specific library format. Intl library format is used for internal format representation.

Developer can use the converter registry (DateTimeFormatConverterRegistry) that simply collects and stores the existing format converters and allows to receive appropriate converter by its alias.

LocaleBundle contains the following format converters:
– intl (IntlDateTimeFormatConverter) – default format converter that simply returns Intl formats
– moment (MomentDateTimeFormatConverter) – format converter for moment.js library

getDateFormat

It returns localized date format for specific library and optionally receives date format type form Intl library and custom locale.

$converterRegistry->getFormatConverter('intl')->getDateFormat();
// MMM d, y
$converterRegistry->getFormatConverter('moment')->getDateFormat();
// MMM D, YYYY

getTimeFormat

It returns localized time format for specific library and optionally receives time format type form Intl library and custom locale.

$converterRegistry->getFormatConverter('intl')->getTimeFormat();
//h:mm a
$converterRegistry->getFormatConverter('moment')->getTimeFormat();
//h:mm A

$converterRegistry->getFormatConverter('intl')->getTimeFormat(\IntlDateFormatter::MEDIUM, 'ru');
// H:mm:ss
$converterRegistry->getFormatConverter('moment')->getTimeFormat(\IntlDateFormatter::MEDIUM, 'ru');
// H:mm:ss

getDateTimeFormat

It returns localized datetime format for specific library and, in this case, it optionally receives date and time format types form Intl library and custom locale.

$converterRegistry->getFormatConverter('intl')->getDateTimeFormat();
// MMM d, y h:mm a
$converterRegistry->getFormatConverter('moment')->getDateTimeFormat();
// MMM D, YYYY h:mm A

$converterRegistry->getFormatConverter('intl')->getDateTimeFormat(
   \IntlDateFormatter::FULL,
   \IntlDateFormatter::MEDIUM,
   'ru'
);
// EEEE, d MMMM y 'г'. H:mm:ss
 $converterRegistry->getFormatConverter('moment')->getDateTimeFormat(
   \IntlDateFormatter::FULL,
   \IntlDateFormatter::MEDIUM,
   'ru'
);
// dddd, D MMMM YYYY [г]. H:mm:ss

Twig

LocaleBundle has two twig extensions that provide with formatter filters and format converter functions.

{{ entity.lastLogin|oro_format_date }}
{{ entity.lastLogin|oro_format_date({'locale': us}) }}
{{ entity.lastLogin|oro_format_time }}
{{ entity.lastLogin|oro_format_time({'locale': us}) }}
{{ entity.lastLogin|oro_format_datetime }}
{{ entity.lastLogin|oro_format_datetime({'locale': us}) }}

JS

From the frontend side, there is JavaScript datetime converter that provides functions to format datetime values. Formatter uses library moment.js to work with datetime values, so localized formats injected from locale settings configuration.

datetimeFormatter.getDateTimeFormat();
// MMM D, YYYY h:mm A

datetimeFormatter.isDateValid('qwerty');
// false

datetimeFormatter.isDateTimeValid('dec 12 2015 09:00 am');
// true

datetimeFormatter.formatDate('2015-12-12');
// Dec 12, 2015

datetimeFormatter.formatDateTime(new Date());
// Dec, 2015 09:00 AM

datetimeFormatter.convertDateToBackendFormat('Dec 12, 2015');
// 2015-12-12

datetimeFormatter.convertDateTimeToBackendFormat(Des 12, 2015 9:00 AM');
// 2015-12-012T09:00:00+0200

Number formatting

Numbers can have a lot of types and formats, we may use default PHP or JS format methods or use methods provided by OroCRM.

We use specific class “numberformatter” for numbers formatting:
Oro\Bundle\LocaleBundle\Formatter\NumberFormatter (service: oro_locale.formatter.number)
This class formats different styles of numbers in localized format and proxies Intl extension class. Methods of NumberFormatter can receive values of original Intl NumberFormatter constants.
Moreover, each constant can be passed to an appropriate method of NumberFormatter as a string name.
For example, few methods of NumberFormatter class:

$numberFormatter->format(1234.56789, \NumberFormatter::DECIMAL);
// outputs: "1,234.568" if default locale is en_US

$numberFormatter->formatCurrency(1234.56789);
// outputs: "$1,234.57" if default locale is en_US and currency is 'USD'

$numberFormatter->formatDecimal(1234.56789);
// outputs: "1,234.568" if default locale is en_US and currency is 'USD'

$numberFormatter->formatPercent(1);
// outputs: "100%"

$numberFormatter->formatSpellout(1);
// outputs: "one"

$numberFormatter->formatOrdinal(1);
// outputs: "1st"

Twig

Also, you can use twig methods for formatting numbers:

{{ 10000|oro_format_number('decimal') }}
{{ 1234.56789|oro_format_decimal }}
{{ 100000|oro_format_currency }}
{{ 1|oro_format_percent }}

JS

On JS side, the number formatter is available via module “orolocale/js/formatter/number”. This module provides with the following functions for different cases:

//Formats number to decimal localized format.
require(['orolocale/js/formatter/number'], function(numberFormatter) {
     numberFormatter.formatDecimal(10000);
   // 10,000.000 
});

//Formats number to integer localized format.
require(['orolocale/js/formatter/number'], function(numberFormatter) {
    numberFormatter.formatInteger(10000);
 // 10,000
});

//Formats number to percent localized format.
require(['orolocale/js/formatter/number'], function(numberFormatter) {
  numberFormatter.formatPercent(.5);
 // 50% 
});

//Formats number to currency localized format. If currency is not specified, then default one will be used.
require(['orolocale/js/formatter/number'], function(numberFormatter) {
    numberFormatter.formatCurrency(-50000.45);
 // $50,000.45
});

//Parses a number from localized number string. Can be used to parse all styles of localized numbers.

require(['orolocale/js/formatter/number'], function(numberFormatter) {
     numberFormatter.unformat('$50,000.45');
   // 50000.45
   
  numberFormatter.unformat('95%');
   // 0.95

     numberFormatter.unformat('(1,000,000.456)');
// -1000000.456

  numberFormatter.unformat(atwix);
// NaN
});

In this article we’ve described the general concepts of OroCRM localization. Meanwhile, you can find more information in OroCRM documentation. As you can see, using localization you can create convenient application for your users and we hope you will find our tips helpful.