26

I am using ui-router for routing and angular-translate for translations. What i would like to achieve is having the selected language bind to the url like so:

www.mydomain.com/en/
www.mydomain.com/ru/
www.mydomain.com/en/about
www.mydomain.com/ru/about

and it will respond accordingly.

Tried to look for examples, but did not find anything. If someone implemented similar solution, i would love to hear how you did it.

Thanks

Leon
  • 1,136
  • 1
  • 11
  • 17

4 Answers4

30

I use something along these lines:

CoffeeScript

angular.module('app')
.config([
  '$stateProvider'
  ($stateProvider) ->
    $stateProvider.state 'app',
      abstract: true
      url: '/{locale}'
    $stateProvider.state 'app.root',
      url: ''
    $stateProvider.state 'app.root.about',
      url: '/about'
])

JavaScript

angular.module('app').config([
  '$stateProvider', function($stateProvider) {
    $stateProvider.state('app', {
      abstract: true,
      url: '/{locale}'
    });
    $stateProvider.state('app.root', {
      url: ''
    });
    return $stateProvider.state('app.root.about', {
      url: '/about'
    });
  }
]);

With this, you can inject $stateParams into your controller and get access to the locale there:

CoffeeScript

angular.module('app')
.controller('appCtrl', [
  '$scope', '$stateParams'
  ($scope, $stateParams) ->
    $scope.locale = $stateParams.locale
])

JavaScript

angular.module('app').controller('appCtrl', [
  '$scope', '$stateParams', function($scope, $stateParams) {
    return $scope.locale = $stateParams.locale;
  }
]);

Or, if you want to affect the whole page automatically, use the $stateChangeStart event in an application controller or similar:

CoffeeScript

$scope.$on '$stateChangeStart', (event, toState, toParams, fromState, fromParams) ->
  $translate.use(toParams.locale)

JavaScript

$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
  $translate.use(toParams.locale);
});

Note that if you're using angular-translate v1.x you should use $translate.uses instead of $translate.use.

mjtko
  • 1,040
  • 9
  • 12
  • 1
    Ah, yes - this seems to have changed between v1.x and v2.x and I'm using v1.x at the moment -- I'll make a note in the answer, thanks. – mjtko Jun 20 '14 at 08:54
  • I want the url to have a language code when user navigates to the main page of the webpage by `domain.com` -> `domain.com/en/` To do so I need to detect user language first and then add `{locale}` params to the url. Is there a good solution for it? – user2814599 Feb 03 '15 at 21:18
  • see my question and my solution in the update if it is still relevant :) http://stackoverflow.com/questions/28601801/angularjs-routing-in-multilanguage-app – Basilf Feb 19 '15 at 12:50
  • Hi, @mjtko How can I achieve the same thing using angular 12? – Prashant Odhavani Dec 18 '21 at 18:04
4

The solution is valid only if you want to have URLs of the below format:

domain.com/{locale}/about

hence:

domain.com/en/about domain.com/mt/about

Recently we were required to implement translations for the full URL, therefore:

domain.com/{locale}/{about}

where {about} is translated in the respective language:

domain.com/en/about domain.com/mt/fuqna

I don't know if the below approach is the best one, however it does work.

For starters, the first difference is that we set up ui-router states to be generated dynamically using a service which retrieves the routes from a JSON file. This is done similarly to @ChrisT's answer in: Angular - UI Router - programmatically add states

module.service("routingService", ["$http", function($http) {

    self.get = function(options) {           
        return self.getByLocale({
            market: options.urlMarketCode
        });
    };

    self.getByLocale = function(options) {
        var market = options.market;

        // loads the different .json files based on the different market values passed, ex: routes-en.json
        var configurationKey = "routes-" + market;

        return $http({
            method: "GET",
            url: configurationKey + ".json",
            headers: {
                "Content-Type": "application/json"
            }
        }).then(function(response) {
            if (response.data) {
                return response.data;
            }
            return undefined;
        }).catch(function(e) {
            console.log(e);
        });
    };

    return self;
}]);

We would then consume the above routingService in the run block of the application:

// run the module and register the state change handler
angular.module("sportsbook-app").run(["$state", "$rootScope", "routingService", "stateService",
    function ($state, $rootScope, routingService, stateService) {
        // retrieve the routing heirarchy from file
        routingService.get({
            urlMarketCode: $rootScope.language
        }).then(function (response) {
            if (response) {
                // add the routes to the $stateProvider
                stateService.generate(response);
            }
        });
    }
]);

And finally the stateService simply parses the JSON file and creates the routing hierarchy using ChrisT's runtimeStates.addState.

I will try to include a working demo in the near future.

Credits also go to @karl-agius.

Community
  • 1
  • 1
Kevin Farrugia
  • 6,431
  • 4
  • 38
  • 61
  • HI, I interested in this solution. Could you please point a link or article which describe this solution in detail? Thanks in advance. – Seto Oct 28 '15 at 09:40
  • @Seto I tried to include a plunker that simulates this scenario however I had to scale it down slightly. **Note that I am not sure if I would recommend this approach.** If you also want to include changing of language without refreshing you will probably need to listen to the `$stateChangeStart` events and update the states from there. **Code**: [Plunker](http://plnkr.co/edit/r51UEZ) **Demo URLs**: [http://run.plnkr.co/Fxo80MbXmuLfdG5c/#/en](http://run.plnkr.co/Fxo80MbXmuLfdG5c/#/en) [http://run.plnkr.co/Fxo80MbXmuLfdG5c/#/sv](http://run.plnkr.co/Fxo80MbXmuLfdG5c/#/en) – Kevin Farrugia Oct 29 '15 at 12:47
  • Hello @KevinFarrugia for en language i don't want to add locale. i.e. en => domain.com/about mt => domain.com/mt/fuqna what can i do? Please help. Thanks – Ashish Mehta Apr 09 '18 at 07:02
  • Hi @AshishMehta unfortunately I have not worked on this project (or similar) for a very long time and am not updated with the latest solutions; however I would assume that it is possible to have a default value for ```{locale}``` – Kevin Farrugia Apr 09 '18 at 07:59
2

I've written a blog post on the exact matter: http://fadeit.dk/post/angular-translate-ui-router-seo

ozooner
  • 552
  • 6
  • 11
  • 1
    Thanks for the link to your post @ozooner. you can check out the app i have built: www.azorly.com – Leon Apr 01 '15 at 06:40
  • 1
    Please quote the relevant part of the post in your answer, see http://stackoverflow.com/help/how-to-answer – ValarDohaeris Dec 15 '16 at 16:14
1

For people that would like to include the URL using ngRoute (I came here googling for exactly that), I've implemented it as follows.

(1) In my .htaccess I caught all URLs without a language subdomain and redirected it to the default (fr in my case). The only real downside is that I have to specify every language manually.

# https://stackoverflow.com/questions/19570572/htaccess-multi-language-site-with-sub-directories-and-default-301/19902914#19902914
# Add language to URL - redirect to default if missing    

RewriteBase /   

# empty url -> redirect to nl/
RewriteCond %{QUERY_STRING} !lang=(nl|fr)
RewriteRule ^$ fr/ [R=301,L]

# url is ONLY '/nl' or '/fr' -> redirect to /nl/ or /fr/ (adding slash)
RewriteRule ^(nl|fr)$  $1/ [R=301,L]

# now all urls have nl/ fr/ -> parse them
RewriteRule ^(nl|fr)/(.*)$  $2?lang=$1&%{query_STRING} [L]

(2) In my Angular project's config block I then simply parsed the URL to get the current language.

config.$inject = ['$translateProvider', '$windowProvider'];

function config($translateProvider, $windowProvider) {

    var $window,
        language;

        $window  = $windowProvider.$get();
        language = $window.location.pathname.replace(/\//g, '');

    //////

    $translateProvider
      .useStaticFilesLoader({
        prefix: 'translations/',
        suffix: '.json'
          })
      .useSanitizeValueStrategy('sanitizeParameters')
      .preferredLanguage( language )
}

(3) In order to get the language in my HTML files I also added it to the $rootScope.

run.$inject = ['$window', '$rootScope'];

function run($window, $rootScope ) {
    $rootScope.language = $window.location.pathname.replace(/\//g, '');
}
ChezFre
  • 6,502
  • 1
  • 19
  • 25