How to translate Angular-UI-Bootstrap datepicker?

20,655

Solution 1

Someone said [The real problem is that you cannot change your $locale during runtime]. In fact you can. You can see a working plunker here.

Solution 2

You can create your own directive like this:

angular
    .module('myApp.common')
    .directive('datepickerPopupWrap', datepickerPopupWrap);

datepickerPopupWrap.$inject = ['$rootScope'];

function datepickerPopupWrap($rootScope, $compile) {

    return {

        restrict: 'A',
        require: 'ngModel',

        link: function($scope, $element, attrs, ngModel) {
            // Force popup rerender whenever locale changes
            $rootScope.$on('localeChanged', ngModel.$render);
        }
    };

}

The directive name must be datepickerPopupWrap, so it's executed together with the default ui-bootstrap directive that renders the popup.

Then, whenever you change the locale with angular-dynamic-locale, do this:

tmhDynamicLocale.set(languageKey).then(function() {

    // Set the language in angular-translate
    $translate.use(languageKey);

    // Broadcast the event so datepickers would rerender
    $rootScope.$broadcast('localeChanged');
});

Solution 3

I was not happy using the given approaches, so I figured out this one by using angular-translate and the possibility, to overwrite the angular-ui-bootstrap-templates like this (Source from ui-bootstrap-tpls.js):

For uib/template/datepicker/day.html:

angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("uib/template/datepicker/day.html",
        "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
        "  <thead>\n" +
        "    <tr>\n" +
        "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
        "      <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{ title | uppercase | localizeMonth }}</strong></button></th>\n" +
        "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
        "    </tr>\n" +
        "    <tr>\n" +
        "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
        "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{ ('DAY_' + label.abbr | uppercase) | translate}}</small></th>\n" +
        "    </tr>\n" +
        "  </thead>\n" +
        "  <tbody>\n" +
        "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
        "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
        "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
        "        id=\"{{::dt.uid}}\"\n" +
        "        ng-class=\"::dt.customClass\">\n" +
        "        <button type=\"button\" class=\"btn btn-default btn-sm\"\n" +
        "          uib-is-class=\"\n" +
        "            'btn-info' for selectedDt,\n" +
        "            'active' for activeDt\n" +
        "            on dt\"\n" +
        "          ng-click=\"select(dt.date)\"\n" +
        "          ng-disabled=\"::dt.disabled\"\n" +
        "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
        "      </td>\n" +
        "    </tr>\n" +
        "  </tbody>\n" +
        "</table>\n" +
        "");
}]);

For uib/template/datepicker/month.html:

angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("uib/template/datepicker/month.html",
        "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
        "  <thead>\n" +
        "    <tr>\n" +
        "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
        "      <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
        "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
        "    </tr>\n" +
        "  </thead>\n" +
        "  <tbody>\n" +
        "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
        "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
        "        id=\"{{::dt.uid}}\"\n" +
        "        ng-class=\"::dt.customClass\">\n" +
        "        <button type=\"button\" class=\"btn btn-default\"\n" +
        "          uib-is-class=\"\n" +
        "            'btn-info' for selectedDt,\n" +
        "            'active' for activeDt\n" +
        "            on dt\"\n" +
        "          ng-click=\"select(dt.date)\"\n" +
        "          ng-disabled=\"::dt.disabled\"\n" +
        "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{ ('MONTH_' + dt.label | uppercase) | translate }}</span></button>\n" +
        "      </td>\n" +
        "    </tr>\n" +
        "  </tbody>\n" +
        "</table>\n" +
        "");
}]);

You also need to extend your language file by (this is for the german language):

, 'MONTH_JANUARY':                  'Januar'
, 'MONTH_FEBRUARY':                 'Februar'
, 'MONTH_MARCH':                    'März'
, 'MONTH_APRIL':                    'April'
, 'MONTH_MAY':                      'May'
, 'MONTH_JUNE':                     'June'
, 'MONTH_JULY':                     'July'
, 'MONTH_AUGUST':                   'August'
, 'MONTH_SEPTEMBER':                'September'
, 'MONTH_OCTOBER':                  'October'
, 'MONTH_NOVEMBER':                 'November'
, 'MONTH_DECEMBER':                 'December'

And since the current month of the datepicker is orignally rendered as {{title}} provied by scope.title = dateFilter(this.activeDate, this.formatDayTitle); (line 2216 in ui-bootstrap-tpls.js), you have to load a filter to localize the current month (Thanks to this post):

/* global app */
app.filter('localizeMonth', function($interpolate)
{
    return function (input)
    {
        return input
            .replace(/JANUARY/g,   $interpolate('{{ \'MONTH_JANUARY\'   | translate}}'))
            .replace(/FEBRUARY/g,  $interpolate('{{ \'MONTH_FEBRUARY\'  | translate}}'))
            .replace(/MARCH/g,     $interpolate('{{ \'MONTH_MARCH\'     | translate}}'))
            .replace(/APRIL/g,     $interpolate('{{ \'MONTH_APRIL\'     | translate}}'))
            .replace(/MAY/g,       $interpolate('{{ \'MONTH_MAY\'       | translate}}'))
            .replace(/JUNE/g,      $interpolate('{{ \'MONTH_JUNE\'      | translate}}'))
            .replace(/JULY/g,      $interpolate('{{ \'MONTH_JULY\'      | translate}}'))
            .replace(/AUGUST/g,    $interpolate('{{ \'MONTH_AUGUST\'    | translate}}'))
            .replace(/SEPTEMBER/g, $interpolate('{{ \'MONTH_SEPTEMBER\' | translate}}'))
            .replace(/OCTOBER/g,   $interpolate('{{ \'MONTH_OCTOBER\'   | translate}}'))
            .replace(/NOVEMBER/g,  $interpolate('{{ \'MONTH_NOVEMBER\'  | translate}}'))
            .replace(/DECEMBER/g,  $interpolate('{{ \'MONTH_DECEMBER\'  | translate}}'))
        ;
    };
});

I think this solution is at least as ugly as the other hacks that were invented here, but you don't have to trigger redraws or similar things by yourself.

Share:
20,655
ThCC
Author by

ThCC

I was a newbie in a lot. Now I have more knowledge in AngularJS, Python, Sql Server and PostgreSql. But there is still much to learn. And here I go!

Updated on February 13, 2020

Comments

  • ThCC
    ThCC about 4 years

    The documentation for the datepicker (Angle-UI-BootStrap) reports:

    Everything is formatted using the date filter and thus is also localized.

    Checking the documentation for the date filter can have access to the concept of i18n and i10n for AngularJs. However, the two approaches which are provided, can not be used in my application. The two approaches are:

    1. Pre-bundled rule sets
    2. Including locale js script in index.html page

    In my application, I check the language of the client after he had performed login. So my index.html page is already created and configured.

    There is no other way to translate the datepicker? A dynamically way... something like changing a value of the $scope that alters the interface performing the translation of the component?

    Here's a plunker demonstrating the approach of introducing in the index.html the translation for pt-BR.

    Update: I asked this question in an issue of the Angle-UI-Bootstrap, and here is the response I received from @bekos:

    @ThCC The real problem is that you cannot change your $locale during runtime, or at least I don't know of a way to do this. Even if you solve the problem with the datepicker, you will still have problem in every other filter that is locale dependent, like currency. I think it is better to ask a more general question in the AngularJS issues.

    If anyone has another solution is always welcome. If I get a solution I will return here.

  • ra00l
    ra00l almost 10 years
    the punker you reference is empty!
  • Serge Tahé
    Serge Tahé almost 10 years
    @ra00l Strange... Here is another link
  • thomas.winckell
    thomas.winckell almost 10 years
    Instead of change the angular locale files as you do in your plunker, you can use Angular Dynamic Locale (github.com/lgalfaso/angular-dynamic-locale), but, to make it work with the datepicker, you need to change the model to apply the changes (as you do in your plunker).
  • Martin Nuc
    Martin Nuc over 9 years
    This doesn't work when date is null - for example when you click in the popup on the clear button.
  • npe
    npe about 9 years
    This would work, if you'd manually called the ngModel.$render() - see my answer.
  • Parth Doshi
    Parth Doshi almost 9 years
    Hello. Is there any way to change the language without adding the specific language script ?