ThCC
ThCC

Reputation: 141

How to translate Angular-UI-Bootstrap datepicker?

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.

Upvotes: 5

Views: 21022

Answers (3)

Thomas Kekeisen
Thomas Kekeisen

Reputation: 4406

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.

Upvotes: 1

npe
npe

Reputation: 15699

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');
});

Upvotes: 2

Serge Tah&#233;
Serge Tah&#233;

Reputation: 2079

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.

Upvotes: 9

Related Questions