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:
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.