6

Here is an example json i18n language file for English:

{
  "project": {
    "SPONSORINFO": {
      "MAIN" : "Select the Sponsor Name",
      "SPONSORLIST": [
        {"spons" :"SponsorName 1" },
        {"spons" :"SponsorName 2" }
      ]
    }
  }
}

and here is my html view:

<div class="form-group" >
    <label for="form-field-select-1" translate="project.SPONSORINFO.MAIN">
    </label>
    <select class="form-control"  ng-model="myModel.sponsors">
       <option ng-repeat="s in projectJSON.project.SPONSORINFO.SPONSORLIST" value="{{s.spons}}">{{s.spons | translate}}</option>
    </select>
</div>

The translate="project.SPONSORINFO.MAIN" in the label is rightly showing the value "Select the Sponsor Name" as soon as the language toggle is clicked (no refresh is needed).

Question:

I am using the following function in my view controller to load the language file based on the selected language and pass it into $scope.projectJSON so that I can call it in ng-repeat in my html view:

var lang = $translate.use();
$http.get('assets/i18n/'+lang+'.json').success(function(data) {
    $scope.projectJSON= data;
});


The problem is that after toggling the language, the dropdown menus don't get changed to the selected language unless I refresh or change view and come back. How can I fix this issue so that it works like the label?

annex:

Translation global config in my main.js like the following:

app.config(['$translateProvider',
function ($translateProvider) {

    // prefix and suffix information  is required to specify a pattern
    // You can simply use the static-files loader with this pattern:
    $translateProvider.useStaticFilesLoader({
        prefix: 'assets/i18n/',
        suffix: '.json'
    });

    // Since you've now registered more then one translation table, angular-translate has to know which one to use.
    // This is where preferredLanguage(langKey) comes in.
    $translateProvider.preferredLanguage('en');

    // Store the language in the local storage
    $translateProvider.useLocalStorage();

}]);

translation config in my mainCtrl.js:

app.controller('AppCtrl', ['$rootScope', '$scope', '$state', '$translate', 
function ($rootScope, $scope, $state, $translate) {

    $scope.language = {
        // Handles language dropdown
        listIsOpen: false,
        // list of available languages
        available: {
            'en': 'English',
            //'it_IT': 'Italiano',
            'de_DE': 'Deutsch'
        },
        // display always the current ui language
        init: function () {
            var proposedLanguage = $translate.proposedLanguage() || $translate.use();
            var preferredLanguage = $translate.preferredLanguage();
            // we know we have set a preferred one in app.config
            $scope.language.selected = $scope.language.available[(proposedLanguage || preferredLanguage)];
        },
        set: function (localeId, ev) {
            $translate.use(localeId);
            $scope.language.selected = $scope.language.available[localeId];
            $scope.language.listIsOpen = !$scope.language.listIsOpen;
        }
    };

    $scope.language.init();
cplus
  • 1,115
  • 4
  • 22
  • 55
  • Try to use promise pattern: $http.get(..).then(function(response){..}) – Vadim Oct 26 '16 at 18:49
  • @Vadim please write down your suggestion as a complete answer. – cplus Oct 26 '16 at 18:49
  • hey would it be possible for you put them together and put on a code snippet or plunkr, in working state to checkout? – Sreekanth Oct 26 '16 at 19:02
  • @Mpondomise, did you solve your problem? – Max Koretskyi Oct 26 '16 at 19:11
  • @Maximus not yet! I am still looking for solution. Do you have any? – cplus Oct 26 '16 at 19:15
  • @Mpondomise, so what you're saying is that after this line is executed `$scope.projectJSON= data;` the value in ` – Max Koretskyi Oct 26 '16 at 19:28
  • @Mpondomise , I see you are trying to read the Main label and the ng-repeat in two different ways. Main label as - project.SPONSORINFO.MAIN" and in the ng-repeat as projectJSON.project.SPONSORINFO.SPONSORLIST. is this correct? – Sreekanth Oct 26 '16 at 19:31
  • @Sreekanth yes, I want to read my ng-repeat also like the other one. I don't want to load the language specific json separately. because it is already being automatically done for the LABEL, which is being loaded. but when it comes to ng-repeat, I can't do the same. – cplus Oct 26 '16 at 19:33
  • in that case , ng-repeat="s in projectJSON.project.SPONSORINFO.SPONSORLIST" should be ng-repeat="s in project.SPONSORINFO.SPONSORLIST" isnt it? instead of an additional projectJSON at the begining – Sreekanth Oct 26 '16 at 19:39
  • @Sreekanth yes, but it is not working! – cplus Oct 26 '16 at 19:40
  • You should use `ng-options` in the first place. – a better oliver Oct 31 '16 at 12:14
  • @zeroflagL how does it change the problem? does it at all change the problem? could you suggest an answer to this question? – cplus Oct 31 '16 at 12:20

5 Answers5

2

You are iterating array from translation json file. This translation json file is loaded by $translate service and you will not have access on loaded content, but you need the data from this json file to iterate it, thus you have to make your own request to fetch this array. Maybe you do not want but you have to make $http.get call.

In your code one request is made by executing this line var lang = $translate.use(newLang); and second call is done by $http.get BUT in case if $http.get is resolved before call in $translate.use is resolved It will not translate content in dropdown because request in $translate.use is not resolved yet and $translate service does not have these translations to translate.

What you can do is to listen on $translateChangeSuccess event (emitted by $translate service) on $rootScope and then make your ajax call in this handler.

I have tested the following code in your example and it works fine.

[UPDATED]

$rootScope.$on('$translateChangeSuccess', function () {
  // Get new current new language
  var lang = $translate.use();
  $http.get('assets/i18n/'+lang+'.json').success(function(data) {
    $scope.projectJSON = data;
  });
});

For detailed explanation about events section on angular translate module, checkout this link.

Whenever you call $translate.use(localeId) it will make ajax call internally and when this call is resolved it will emit $translateChangeSuccess event, then you make your request, load data and update $scope.projectJSON.
The only thing is to trigger this event first time, when you refresh browser, when $translate.use is not called.

For this you can just call $translate.use one time on page reload.

[UPDATED]

Put the following code at the end of your $scope.language.init function.

$translate.use(proposedLanguage || preferredLanguage);

This should solve your problem.

Zura Sekhniashvili
  • 1,147
  • 7
  • 19
  • Basically I shouldn' need to do this $http, because the LABEL is being translated with the code in the main.js and mainCtrl.js. I am using this hack to separately load my language json again (because it is already done by main.js and mainCtrl) to feed into the dropdown menus. That's why it doesn't translate immediately, it should be refreshed. where as the LABEL is being translated on the spot. – cplus Oct 30 '16 at 08:18
  • Without additional request you won't be able to access translation data. So you have to make this request if you want to get data. May I know why do you want to show data from translation json file? What is your final purpose? – Zura Sekhniashvili Oct 30 '16 at 09:04
  • I don't want to load the translation json AT ALL! I want it to work for dropdown items as it is working for other parts of the app, without loading the json file! I have to manually load the SAME json again for dropdown menus! However, the translation is working for the titles of the table without reloading the json. – cplus Oct 30 '16 at 16:52
  • the last three lines should be included in which file? main.js , mainCtrl.js, or the view controller? How about this line `$scope.language.selected = $scope.language.available[(proposedLanguage || preferredLanguage)]; ` should it be deleted? – cplus Nov 01 '16 at 10:04
  • actually this is working!! but, when the page is accessed for the first time after login, or in other words, without clicking on the language toggle, the dropdown menus are not loaded! they get loaded only after i toggle the language and they show the dropdown menus in the chosen language. which is fine! but they don't show unless you change the language and then afterwards it works fine! how to load the dropdown menu by default for the default language? – cplus Nov 01 '16 at 10:12
  • I don't get the point of this code here `$scope.changeLanguage = function(newLang) { $translate.use(newLang); }` what is it supposed to do? whether included or not, it doesn't play any role in the story! I might be missing something here. – cplus Nov 01 '16 at 10:29
  • `$scope.changeLanguage = function(newLang) { $translate.use(newLang); }` this is doing the same as your `$scope.language.set` function, just I copied it from my example. – Zura Sekhniashvili Nov 01 '16 at 10:39
  • Last three lines should be included in your `init` method. When the page is loaded it gets language and gives to `$translate` service to make call and to load translations. Put these lines in your init method – Zura Sekhniashvili Nov 01 '16 at 10:42
  • @ZuraSekhniashvili `$scope.changeLanguage = function(newLang) { $translate.use(newLang); }`where should it be included then? it is not being called anywhere, as far as I know, what is it supposed to do? where should it go? did you read my comment above regarding the small problem on first page load and dropdown not showing until toggle is clicked? – cplus Nov 01 '16 at 10:47
  • Forget about this `$scope.changeLanguage = function(newLang) { $translate.use(newLang); }`. You do not need this. I tried my answer to be general, but I think you did not understand, so I updated my answer. Let me know if you still need help – Zura Sekhniashvili Nov 01 '16 at 10:56
  • @ZuraSekhniashvili here is how the code is looking like in the init part: http://pastebin.com/Y0GWkkJw I am missing something? it is giving me a GET error, `assets/i18n/English.json` not found! actually my file name is `en.json`not `English.json` – cplus Nov 01 '16 at 12:14
  • @Mpondomise try this `$translate.use(proposedLanguage || preferredLanguage);` instead of this one `$translate.use($scope.language.selected);`. – Zura Sekhniashvili Nov 01 '16 at 12:27
  • Put this `$translate.use(proposedLanguage || preferredLanguage);` at the end of init function. – Zura Sekhniashvili Nov 01 '16 at 12:28
1

Try to use promise pattern:

$http.get(..).then(function(response){..})

So, looks like problem not in ng-repeat, so I would change the title of the question. Back to the subject:

$http.get('assets/i18n/'+lang+'.json').then(function(response) {
    //check what exactly are you getting in the response
    //console.log(response)
    $scope.projectJSON = response;
});

And I would check what is the response actually, probably, you need to do

$scope.projectJSON = response.data;
Vadim
  • 633
  • 1
  • 8
  • 17
0

Old

Because you are using an async function angular won't recognize the change immediately. After downloading the files add

$scope.$apply();

To make angular rescan everything and apply changes. Here you can read more about it.

That means for you ($$phase check by @lascort)

$http.get('assets/i18n/'+lang+'.json').success(function(data) {
$scope.projectJSON= data;
$translate.use(lang);
if (!$scope.$$phase)
    $scope.$apply();
  $translate.refresh();
});

Edit

But official resources recommend to use their loaders and not load manually: Here

You are using a staticFilesLoader so install the following extension:

bower install angular-translate-loader-static-files

Then in your language change function:

$translateProvider.preferredLanguage(lang);

This should work (says the documentation). Maybe you have to do a $scope.$apply(); but I think this is already included.

Astasian
  • 429
  • 4
  • 15
  • where should I add this? Could you write a complete answer? thanks – cplus Oct 26 '16 at 18:21
  • I usually use $scope.$apply() with an if before, so it doesn't log errors when there's a digest in progress. Like so: `if (!$scope.$$phase) $scope.$apply()` – martskins Oct 26 '16 at 18:29
  • also.. on a non related matter.. `success` and `error` are deprecated. You should be using `then` and `catch` – martskins Oct 26 '16 at 18:31
  • @lascort could you write down your suggestion as an answer please? so that I can see exactly what you mean. – cplus Oct 26 '16 at 18:36
  • I added it to the answer – Astasian Oct 26 '16 at 18:38
  • Astasian, your answer is not working. nor the edited version. I still have to refresh to see the chosen language's translations. – cplus Oct 26 '16 at 18:39
  • Please try this version. It seems, that you have to apply the update to $translate too. – Astasian Oct 26 '16 at 18:47
  • i tried it, still no result! Only when I refresh my page or change view to another and then come back. Otherwise, when I toggle on the language button, the dropdown menus are still in the previous language. – cplus Oct 26 '16 at 18:51
  • Can you please retry it? Added $translate.use(Lang); – Astasian Oct 26 '16 at 18:55
  • again, i tried your edit `$translate.use(lang);` , no result! – cplus Oct 26 '16 at 18:55
  • yeah I know what the official sources say. But how can I fix the problem now? What should I do exactly? I am confused. – cplus Oct 26 '16 at 19:19
0

Have you tried reloading the state?

$state.reload();

app.controller('AppCtrl', ['$rootScope', '$scope', '$state', '$translate', function ($rootScope, $scope, $state, $translate) {

$scope.language = {
    // Handles language dropdown
    listIsOpen: false,
    // list of available languages
    available: {
        'en': 'English',
        //'it_IT': 'Italiano',
        'de_DE': 'Deutsch'
    },
    // display always the current ui language
    init: function () {
        var proposedLanguage = $translate.proposedLanguage() || $translate.use();
        var preferredLanguage = $translate.preferredLanguage();
        // we know we have set a preferred one in app.config
        $scope.language.selected = $scope.language.available[(proposedLanguage || preferredLanguage)];
    },
    set: function (localeId, ev) {
        $translate.use(localeId);
        $scope.language.selected = $scope.language.available[localeId];
        $scope.language.listIsOpen = !$scope.language.listIsOpen;
    },
    uiReload: function(){
      $state.reload();
    }
};

$scope.language.init();
$scope.language.uiReload();    //call this for ui-router
A.Sharma
  • 2,771
  • 1
  • 11
  • 24
  • I am using ui-router! what should I do? – cplus Oct 26 '16 at 19:14
  • I have updated the answer. I believe an equivalent to refreshing the state is $state.reload() as seen here: http://stackoverflow.com/questions/21714655/reloading-current-state-refresh-data – A.Sharma Oct 26 '16 at 19:21
  • my app breaks, doesn't load anymore. here is my complete controller code. http://pastebin.com/iCG4RCe2 i might have some other functions which are conflicting. – cplus Oct 26 '16 at 19:31
  • Get rid of $route and $templatecache. You may not have referenced the associated angular file nor included the ngRoute service in the app declaration. Get rid of it and only try the $state.reload(); – A.Sharma Oct 26 '16 at 19:37
  • it is not working. i might be mis-implementing the code, could you please edit the answer so that I can see exactly what you mean. – cplus Oct 26 '16 at 19:40
  • Updated the code. Can you just create a Plunker and add your code to it? https://plnkr.co/ – A.Sharma Oct 26 '16 at 19:42
0

Lets go through step by step:
1) The translate="project.SPONSORINFO.MAIN" in the label is rightly showing the value "Select the Sponsor Name" without refresh needed
2) the dropdown menus don't get changed to the selected language unless you refresh or change view and come back.

So, the toggle for translation is actually working.

But why it does not work for dropdown menus? One thing we can note is that label for project.SPONSORINFO.MAIN is a static text while s.spons is dynamic based on value return.

The select option is being compiled first time when entering the page and not being recompiled after you toggle your languages changes. As such, when you toggle the translation, you retrieve the text, but the select option element is not being recompiled and thus not being translated on the fly till your next reentering/reload of the view.

You have few ways to solve the problem:
1) programmitically refresh the state/route once translate is complete. (quick fix but bad way)
2) Write an angular directive that recompile the element once detect language changes. (better approach)

An idea of how to achieve second solution:

var lang = $translate.use();
$http.get('assets/i18n/'+lang+'.json').success(function(data) {
    $scope.projectJSON= data;
    $scope.newLanguage = $translate.use();
});

.directive('recompileLanguage', function($compile) {
  return {
    restrict: 'E',
    scope: {
      lang: '='
    },
    link: function(scope, element, attr) {
      $compile(element.contents())(scope);
      scope.$watch('lang', funcion (newValue, oldvalue) {
        $compile(element.contents())(scope);
      });
    }
  }
});

<select recompile-language lang={{newLanguage}}> </select>
whyyie
  • 792
  • 3
  • 10
  • where should I put the directive? inside my view controller js file? or in my main.js file? my http code is in my view controller js file. – cplus Oct 26 '16 at 19:37
  • where should I put that select element? where to put the directive? could you please give some explanation? thanks – cplus Oct 26 '16 at 19:43
  • You retain the select element on where you put initially. This is how you put the directive https://plnkr.co/edit/?p=preview – whyyie Oct 26 '16 at 19:49
  • your plunker link is not complete. – cplus Oct 26 '16 at 19:51