Tuesday, February 4, 2014

Localize datetimes in display templates in Content by search web parts in Sharepoint 2013

Display templates are plain html files which are used in Content by search web parts in Sharepoint 2013 for displaying information. In this post I will show how to localize datetimes using locale or language of current SPWeb.

The main problem with localization is that inside display templates you may use only javascript and client object model, but not server code. I.e. you can’t just write:

   1:  var ci = new CultureInfo(SPContext.Current.Web.Locale.LCID);
   2:  var date = ...;
   3:  var localizedDate = date.ToString(ci);

We need to do it on client side. If you will make investigation of what methods are available in javascript for dates localization you will find toLocaleString method of Date object. The problem that this method uses current client’s locale and doesn’t allow to pass specific LCID. It may be needed when current client’s locale and locale, which you want to use for dates localization, are not the same.

The good thing however is that MS ajax (which is available in SharePoint OTB) adds to Date localeFormat method, which allows to display datetime using specified format. The question is how to get datetime format for specific LCID on client side?

In order to do it I used the following way. There is useful standard global javascript object _spPageContextInfo, which contains many useful settings from server side which you may use on client side (see How to get URL of current site collection and other server side properties on client site in Sharepoint). It doesn’t contain date time format, so we will extend it with necessary information (e.g. in masterpage);

   1:  <script type="text/javascript">
   2:      jQuery(function() {                
   3:          if (typeof (_spPageContextInfo) != "undefined" &&
   4:              _spPageContextInfo != null) {
   5:              <%
   6:                  var currentWeb = SPContext.Current.Web;
   7:                  var ci = new CultureInfo(currentWeb.Locale.LCID);
   8:                  var cultureSerialized = new JavaScriptSerializer().Serialize(
   9:                      new
  10:                      {
  11:                          name = ci.Name,
  12:                          dateTimeFormat = ci.DateTimeFormat,
  13:                          numberFormat = ci.NumberFormat
  14:                      });
  15:              %>
  16:              _spPageContextInfo.currentCultureSerialized = <%= cultureSerialized %>;
  17:          }
  18:      });
  19:  </script>

It will add the following script to output if current locale is 1033 (en-us):

   1:  _spPageContextInfo.currentCultureSerialized =
   2:  {
   3:     "name":"en-US",
   4:     "dateTimeFormat":{
   5:        "AMDesignator":"AM",
   6:        "Calendar":{
   7:           "MinSupportedDateTime":"\/Date(-62135596800000)\/",
   8:           "MaxSupportedDateTime":"\/Date(253402293599999)\/",
   9:           "AlgorithmType":1,
  10:           "CalendarType":1,
  11:           "Eras":[
  12:              1
  13:           ],
  14:           "TwoDigitYearMax":2029,
  15:           "IsReadOnly":false
  16:        },
  17:        "DateSeparator":"/",
  18:        "FirstDayOfWeek":0,
  19:        "CalendarWeekRule":0,
  20:        "FullDateTimePattern":"dddd, MMMM d, yyyy h:mm:ss tt",
  21:        "LongDatePattern":"dddd, MMMM d, yyyy",
  22:        "LongTimePattern":"h:mm:ss tt",
  23:        "MonthDayPattern":"MMMM d",
  24:        "PMDesignator":"PM",
  25:        "RFC1123Pattern":
  26:  "ddd, dd MMM yyyy HH\u0027:\u0027mm\u0027:\u0027ss \u0027GMT\u0027",
  27:        "ShortDatePattern":"M/d/yyyy",
  28:        "ShortTimePattern":"h:mm tt",
  29:        "SortableDateTimePattern":
  30:  "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss",
  31:        "TimeSeparator":":",
  32:        "UniversalSortableDateTimePattern":
  33:  "yyyy\u0027-\u0027MM\u0027-\u0027dd HH\u0027:\u0027mm\u0027:\u0027ss\u0027Z\u0027",
  34:        "YearMonthPattern":"MMMM yyyy",
  35:        "AbbreviatedDayNames":[
  36:           "Sun",
  37:           "Mon",
  38:           "Tue",
  39:           "Wed",
  40:           "Thu",
  41:           "Fri",
  42:           "Sat"
  43:        ],
  44:        "ShortestDayNames":[
  45:           "Su",
  46:           "Mo",
  47:           "Tu",
  48:           "We",
  49:           "Th",
  50:           "Fr",
  51:           "Sa"
  52:        ],
  53:        "DayNames":[
  54:           "Sunday",
  55:           "Monday",
  56:           "Tuesday",
  57:           "Wednesday",
  58:           "Thursday",
  59:           "Friday",
  60:           "Saturday"
  61:        ],
  62:        "AbbreviatedMonthNames":[
  63:           "Jan",
  64:           "Feb",
  65:           "Mar",
  66:           "Apr",
  67:           "May",
  68:           "Jun",
  69:           "Jul",
  70:           "Aug",
  71:           "Sep",
  72:           "Oct",
  73:           "Nov",
  74:           "Dec",
  75:           ""
  76:        ],
  77:        "MonthNames":[
  78:           "January",
  79:           "February",
  80:           "March",
  81:           "April",
  82:           "May",
  83:           "June",
  84:           "July",
  85:           "August",
  86:           "September",
  87:           "October",
  88:           "November",
  89:           "December",
  90:           ""
  91:        ],
  92:        "IsReadOnly":false,
  93:        "NativeCalendarName":"Gregorian Calendar",
  94:        "AbbreviatedMonthGenitiveNames":[
  95:           "Jan",
  96:           "Feb",
  97:           "Mar",
  98:           "Apr",
  99:           "May",
 100:           "Jun",
 101:           "Jul",
 102:           "Aug",
 103:           "Sep",
 104:           "Oct",
 105:           "Nov",
 106:           "Dec",
 107:           ""
 108:        ],
 109:        "MonthGenitiveNames":[
 110:           "January",
 111:           "February",
 112:           "March",
 113:           "April",
 114:           "May",
 115:           "June",
 116:           "July",
 117:           "August",
 118:           "September",
 119:           "October",
 120:           "November",
 121:           "December",
 122:           ""
 123:        ]
 124:     },
 125:     "numberFormat":{
 126:        "CurrencyDecimalDigits":2,
 127:        "CurrencyDecimalSeparator":".",
 128:        "IsReadOnly":false,
 129:        "CurrencyGroupSizes":[
 130:           3
 131:        ],
 132:        "NumberGroupSizes":[
 133:           3
 134:        ],
 135:        "PercentGroupSizes":[
 136:           3
 137:        ],
 138:        "CurrencyGroupSeparator":",",
 139:        "CurrencySymbol":"$",
 140:        "NaNSymbol":"NaN",
 141:        "CurrencyNegativePattern":0,
 142:        "NumberNegativePattern":1,
 143:        "PercentPositivePattern":0,
 144:        "PercentNegativePattern":0,
 145:        "NegativeInfinitySymbol":"-Infinity",
 146:        "NegativeSign":"-",
 147:        "NumberDecimalDigits":2,
 148:        "NumberDecimalSeparator":".",
 149:        "NumberGroupSeparator":",",
 150:        "CurrencyPositivePattern":0,
 151:        "PositiveInfinitySymbol":"Infinity",
 152:        "PositiveSign":"+",
 153:        "PercentDecimalDigits":2,
 154:        "PercentDecimalSeparator":".",
 155:        "PercentGroupSeparator":",",
 156:        "PercentSymbol":"%",
 157:        "PerMilleSymbol":"‰",
 158:        "NativeDigits":[
 159:           "0",
 160:           "1",
 161:           "2",
 162:           "3",
 163:           "4",
 164:           "5",
 165:           "6",
 166:           "7",
 167:           "8",
 168:           "9"
 169:        ],
 170:        "DigitSubstitution":1
 171:     }
 172:  };

For other locales it will contain localized information. Now, when we have necessary datetime format on client side we can use it in display templates by overriding default datetime renderer:

   1:  customDateRenderer=function(a) {
   2:      if(!Srch.U.n(a) && !a.isEmpty &&
   3:          Date.isInstanceOfType(a.value)) {
   4:          try {
   5:              return a.value.localeFormat
   6:                 (_spPageContextInfo.currentCultureSerialized.
   7:                     dateTimeFormat.ShortDatePattern);
   8:          }
   9:          catch(er) {
  10:              return Srch.ValueInfo.Renderers.
  11:                  defaultRenderedValueHtmlEncoded(a);
  12:          }
  13:      }
  14:      else {
  15:          return Srch.ValueInfo.Renderers.
  16:              defaultRenderedValueHtmlEncoded(a);
  17:      }
  18:  };
  19:  ...
  20:  var dt = $getItemValue(ctx, "Published");
  21:  dt.overrideValueRenderer(customDateRenderer);

After that datetimes will be localized in display templates based on locale of current SPWeb.

4 comments:

  1. Hi,

    Great post !!!

    I have requirement to localize web part titles in my projects using Display template.
    Could you please tell me how can be this done .

    Thanks in advance for your great help.

    Regards,
    Avinash Kumar

    ReplyDelete
  2. hi,
    web part titles are not localized via display templates. Using display templates you may localize content inside web parts, but not their titles. In order to localize web part titles you need to use resx resources and specify Title property in appropriate .webpart file using special syntax: "$Resources:filename,key;" where filename is the name of resx files which should be provisioned into 15/Resources folder with your wsp and key - is the resource string key inside this resx file.

    ReplyDelete
  3. hi,

    Thans for the quick reply.

    As i am new to O365, could yu please help me with the details how to achive this.


    Regards,
    Avinash Kumar

    ReplyDelete
  4. if you need to do it on O365 you only may use OTB resources files from Sharepoint installation which are installed there (can't use custom resx files because they can be installed to 15/Resources folder only with farm solution). Search content of these resources in 15/Resources for required strings, may be you will find something (e.g. they contain frequently used translations for Documents, Meetings, Events, Calendar and others).

    ReplyDelete