Internationalization Options in AngularJS and Salesforce: Part 1 – Localization

An ISV partner recently asked us what their options were for performing internationalization (language translations, locale formatting, time zones, etc.) within a JavaScript Framework – specifically angularJS. They were looking to increase their app’s performance but did not want to give up all the “goodness” that they receive by using Visualforce components (think locale formatting for dates or currency). The ISV TE team did some research and came back with some options that could be leveraged to perform internationalization in angularJS. This is the first of a four-part series blog to cover our findings. Part one of this series will cover Localization. Part two will cover TimeZones Conversions. Part three will cover Language Conversions. Finally, Part four will cover Currency Conversions.

The author of this article is Courtney Dascenzo,  Principal Technical Evangelist with ISV Partner Enablement Team

An ISV partner recently asked us what their options were for performing internationalization (language translations, locale formatting, time zones, etc.) within a JavaScript Framework – specifically angularJS.  They were looking to increase their app’s performance but did not want to give up all the “goodness” that they receive by using Visualforce components (think locale formatting for dates or currency).  The ISV TE team did some research and came back with some options that could be leveraged to perform internationalization in angularJS.  This is the first of a four-part series blog to cover our findings. Part one of this series will cover Localization. Part two will cover TimeZones Conversions. Part three will cover Language Conversions. Finally, Part four will cover Currency Conversions.

 

Background and Assumptions

  • The options presented are based on leveraging JavaScript Remote Objects or JavaScript Remote Actions to integrate with Salesforce.
  • The REST API could have been leveraged but was not due to API call utilization.
  • These options were built and tested in a Partner Developer Edition Org
  • Some options require features to be enabled that you may not want to introduce into a managed package such as multi-currency.
  • Packaging was not tested and the options were not security review tested.
  • A POC requirement was to match the look and feel of Salesforce so we leveraged the Salesforce stylesheets as static resources in the project. *** Never access Salesforce stylesheets dynamically as they can change from release to release.

 

Locale

Locales are typically loaded based on a default setting in your browser. In angularJS, this can be overridden by including the locale file after the angularJS script file in your index.html file is included. During bootstrap, this will force angular to load the specified locale file. The below example will force the locale to be Danish for all users.

<head>
   <title>AngularJS Locale Example</title>
   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
   <script src="https://code.angularjs.org/1.2.5/i18n/angular-locale_da-dk.js"></script>
</head>

This is great when working within one locale but typically, that is not the case. Most likely there is a need for locales to switch dynamically, based on a parameter or variable. We chose to leverage the local setting from the user’s profile setting in Salesforce. To do this, we created a remote action within an Apex custom controller to get some of the user’s information including locale, time zone, and currency. We can use these settings to dynamically change how the information in our app is presented to the end user. Below is an example of the remote action and it’s helper class.

@remoteAction
   public static UserProfile getUserProfile() {
      String userid = 'U' + userInfo.getUserId();  //included a U in front of the Id 
                                                   //to prevent a javascript error
      String sflocale = userInfo.getLocale();      //get user’s locale
      String anlocale = sflocale.toLowerCase();          
      anlocale = anlocale.replace('_','-');        // convert from locale format 
                                                   //of en_US to en-us
      String money = userInfo.getDefaultCurrency();  // get user’s currency
      String language = userInfo.getLanguage();      // get user’s language
      TimeZone tz = userInfo.getTimezone();          // get the timezone
      String timezone = tz.toString();               // convert to user’s timezone

      UserProfile userp = new UserProfile(userid, sflocale, anlocale, 
                                          money, language, timezone);
      return userp;     //return to AngularJS
   }
   //helper class to return an object
   public class UserProfile {                  
     public String userid{get;set;}
     public String sflocale{get;set;}
     public String anlocale{get;set;}
     public String money{get;set;}
     public String language{get;set;}
     public String timezone{get;set;}

     public UserProfile (String userid, String sflocale, String anlocale, String money, 
                         String language, String timezone) {
        this.userid = userid;
        this.sflocale = sflocale;
        this.anlocale = anlocale;
        this.money = money
        this.language = language;   
        this.timezone = timezone;
     }
  }

In angularJS, we can call this Remote Action and store the results in a factory or in $rootScope (since this was a POC, we choose $rootScope). Here is an example of how to call the action and how to store the results:

1. We chose to store the call to the Apex controller’s action in a variable for reuse in the visualforce page:

<script>
   var staticItems = {
      'saveCart':'{!$RemoteAction.StoreFrontControllerRM.saveCart}',
      'getUserProfile':'{!$RemoteAction.StoreFrontControllerRM.getUserProfile}',
      'getAllInvoices':'{!$RemoteAction.StoreFrontControllerRM.getAllInvoices}'
   };
</script>

2. We created an angular service to call the Remote Action in Salesforce and return the results.

getUserInfo : function(callback){
   Visualforce.remoting.Manager.invokeAction(staticItems['getUserProfile'],
   function(result, event){
      if ( event.status ) {
         callback(result);
      }
   });
}

3. Now we call the angular service from the angular controller and store the results to $rootScope variables. We also set the locale of the app using an angular Module called DynamicLocale that we downloaded from Github.

storeApp.controller('StoreController',['$scope', 'storeSERVICES', '$rootScope',
  'tmhDynamicLocale', function($scope, storeSERVICES, $rootScope, tmhDynamicLocale) {
     $scope.loadData = function(){
       if ($scope.loaded){
         //calling the service defined above
         storeSERVICES.getUserInfo(function(data) { 
           $rootScope.userId = data.userid;
           $rootScope.userAnLocale = data.anlocale; //setting the users locale
           $rootScope.userSfLocale = data.sflocale;
           $rootScope.userCurrency = data.money;
           $rootScope.userLanguage = data.language;
           $rootScope.userTimeZone = data.timezone;
           $rootScope.$apply($rootScope.userId);
           $rootScope.$apply($rootScope.userAnLocale); //applying it to $rootScope
           $rootScope.$apply($rootScope.userSfLocale);
           $rootScope.$apply($rootScope.userCurrency);
           $rootScope.$apply($rootScope.userLanguage);
           $rootScope.$apply($rootScope.userTimeZone);
           tmhDynamicLocale.set($rootScope.userAnLocale); //passing it to DynamicLocale
           $scope.loaded = false;
         });
       }
    }
}]);

4. DynamicLocale will automatically grab and load the correct Locale file based on the passed in variable:

tmhDynamicLocaleProvider.localeLocationPattern(
  'https://code.angularjs.org/1.2.20/i18n/angular-locale_{{locale}}.js');

There are other angular modules available on Github to perform the locale file swap. You can also write your own. If you decide to leverage the locale files published by the angular project, make sure they are complete. You may also want to download them and include them as static resources instead of resolving the URL dynamically.

This concludes part one on Internationalization Options in angularJS and Salesforce. Be sure to check out the next post in this series on Time Zone conversions.

Code samples for the whole series can be found in Github at: https://github.com/cdascenzo/salesforce_angular_internationalization.

Don’t want to mess with the code, spin up a trial and see the demo in action (some images do not resolve on the Heroku site, don’t worry, the sign up still works): https://pacific-inlet-1581.herokuapp.com/.

Published
July 27, 2015

Leave your comments...

Internationalization Options in AngularJS and Salesforce: Part 1 – Localization