Skip to main content
added 402 characters in body
Source Link
Joseph
  • 25.4k
  • 2
  • 26
  • 37

Now translations are just "view logic". It doesn't change anything in the underlying data. It's like a "face" of an Xbox controller. No matter how many faces you have, you may even have them cataloged alphabetically, you'll always have the buttons, sticks and triggers working in the same way.

So let's apply the same concept to Angular. The only thing your controller should be aware about is the data. As for how we will know what text to put in to the UI, we represent the text as "keys". These will correspond to text defined somewhere in Angular, like a Factoryconstant. Where the key and your translation meet is the UI, via filters.

controller -(key, data)-> UI -------(key)-------> filter 
                                                  filter <-(current language)- service
                                                  filter <-(translation key)- constant
                          UI <-(translated text)- filter

// Sample translation key
app.constant('Translations', {
  en: {
    'PAGE_TITLE': 'Hi',
  },
  es: {
    'PAGE_TITLE': 'Hola!',
  }
});

// Sample factory which only holds our current language state
app.factory('LanguageService', function(){
  var currentLanguage = 'en';

  return {
    setCurrentLanguage: function(value){
      currentLanguage = value;
    },
    getCurrentLanguage: function(){
      return currentLanguage;
    }
  }
});

// Sample filter which transforms our UI value
app.filter('translate', function(Translations, LanguageService){
  
  // We receive the input, translate according to the current language and key
  // and return the output.
  return function(input){
    // Translations.en.PAGE_TITLE
    return Translations[LanguageService.getCurrentLanguage()][input] || '';
  };
});

// Sample controller
app.controller('MyController', function(LanguageService){
  $scope.data = {
    // Note that it only holds the "key" to the text. The filter will figure it
    // out for you. This data will be the same across all languages.
    title: 'PAGE_TITLE',
    url: '/home',
  };

  // The only time your controller is 
  $scope.changeLanguage = function(value){
    LanguageService.setCurrentLanguage(value);
  };
});

// Sample UI
<!-- This passes title to the translate, then renders the return value -->
<div><a href="{{ url }}">{{ title | translate }}</div>a>
<button type="button" ng-click="setCurrentLanguage('en')>English</button>
<button type="button" ng-click="setCurrentLanguage('es')>Español</button>

As you can see, the UI isn't aware of language-specific stuff. The only thing it knows is that there's a title and URL. Any language-specific things should be represented as keys in the data, in this case title. Since url remains the same, we can simply render it in the UI straight away.

The only inconvenience I see is that your data should carry keys instead of text, and that it should correspond to the keys found in the constants.

Now translations are just "view logic". It doesn't change anything in the underlying data. It's like a "face" of an Xbox controller. No matter how many faces you have, you'll always have the buttons, sticks and triggers working in the same way.

So the only thing your controller should be aware about is the data. As for how we will know what text to put in to the UI, we represent the text as "keys". These will correspond to text defined somewhere in Angular, like a Factory. Where the key and your translation meet is the UI, via filters.

// Sample translation key
app.constant('Translations', {
  en: {
    'PAGE_TITLE': 'Hi',
  },
  es: {
    'PAGE_TITLE': 'Hola!',
  }
});

// Sample factory which only holds our current language state
app.factory('LanguageService', function(){
  var currentLanguage = 'en';

  return {
    setCurrentLanguage: function(value){
      currentLanguage = value;
    },
    getCurrentLanguage: function(){
      return currentLanguage;
    }
  }
});

// Sample filter which transforms our UI value
app.filter('translate', function(Translations, LanguageService){
  
  // We receive the input, translate according to the current language and key
  // and return the output.
  return function(input){
    // Translations.en.PAGE_TITLE
    return Translations[LanguageService.getCurrentLanguage()][input] || '';
  };
});

// Sample controller
app.controller('MyController', function(LanguageService){
  $scope.data = {
    // Note that it only holds the "key" to the text. The filter will figure it
    // out for you. This data will be the same across all languages.
    title: 'PAGE_TITLE',
    url: '/home',
  };

  // The only time your controller is 
  $scope.changeLanguage = function(value){
    LanguageService.setCurrentLanguage(value);
  };
});

// Sample UI
<!-- This passes title to the translate, then renders the return value -->
<div>{{ title | translate }}</div>
<button type="button" ng-click="setCurrentLanguage('en')>English</button>
<button type="button" ng-click="setCurrentLanguage('es')>Español</button>

As you can see, the UI isn't aware of language-specific stuff. The only thing it knows is that there's a title. The only inconvenience I see is that your data should carry keys instead of text, and that it should correspond to the keys found in the constants.

Now translations are just "view logic". It doesn't change anything in the underlying data. It's like a "face" of an Xbox controller. No matter how many faces you have, you may even have them cataloged alphabetically, you'll always have the buttons, sticks and triggers working in the same way.

So let's apply the same concept to Angular. The only thing your controller should be aware about is the data. As for how we will know what text to put in to the UI, we represent the text as "keys". These will correspond to text defined somewhere in Angular, like a constant. Where the key and your translation meet is the UI, via filters.

controller -(key, data)-> UI -------(key)-------> filter 
                                                  filter <-(current language)- service
                                                  filter <-(translation key)- constant
                          UI <-(translated text)- filter

// Sample translation key
app.constant('Translations', {
  en: {
    'PAGE_TITLE': 'Hi',
  },
  es: {
    'PAGE_TITLE': 'Hola!',
  }
});

// Sample factory which only holds our current language state
app.factory('LanguageService', function(){
  var currentLanguage = 'en';

  return {
    setCurrentLanguage: function(value){
      currentLanguage = value;
    },
    getCurrentLanguage: function(){
      return currentLanguage;
    }
  }
});

// Sample filter which transforms our UI value
app.filter('translate', function(Translations, LanguageService){
  
  // We receive the input, translate according to the current language and key
  // and return the output.
  return function(input){
    // Translations.en.PAGE_TITLE
    return Translations[LanguageService.getCurrentLanguage()][input] || '';
  };
});

// Sample controller
app.controller('MyController', function(LanguageService){
  $scope.data = {
    // Note that it only holds the "key" to the text. The filter will figure it
    // out for you. This data will be the same across all languages.
    title: 'PAGE_TITLE',
    url: '/home',
  };

  // The only time your controller is 
  $scope.changeLanguage = function(value){
    LanguageService.setCurrentLanguage(value);
  };
});

// Sample UI
<!-- This passes title to the translate, then renders the return value -->
<a href="{{ url }}">{{ title | translate }}</a>
<button type="button" ng-click="setCurrentLanguage('en')>English</button>
<button type="button" ng-click="setCurrentLanguage('es')>Español</button>

As you can see, the UI isn't aware of language-specific stuff. The only thing it knows is that there's a title and URL. Any language-specific things should be represented as keys in the data, in this case title. Since url remains the same, we can simply render it in the UI straight away.

The only inconvenience I see is that your data should carry keys instead of text, and that it should correspond to the keys found in the constants.

Source Link
Joseph
  • 25.4k
  • 2
  • 26
  • 37

TL;DR: Just use an existing i18n plugin for angular. There's Angular Translate which I have used for a while and is pretty good.

Now translations are just "view logic". It doesn't change anything in the underlying data. It's like a "face" of an Xbox controller. No matter how many faces you have, you'll always have the buttons, sticks and triggers working in the same way.

So the only thing your controller should be aware about is the data. As for how we will know what text to put in to the UI, we represent the text as "keys". These will correspond to text defined somewhere in Angular, like a Factory. Where the key and your translation meet is the UI, via filters.

// Sample translation key
app.constant('Translations', {
  en: {
    'PAGE_TITLE': 'Hi',
  },
  es: {
    'PAGE_TITLE': 'Hola!',
  }
});

// Sample factory which only holds our current language state
app.factory('LanguageService', function(){
  var currentLanguage = 'en';

  return {
    setCurrentLanguage: function(value){
      currentLanguage = value;
    },
    getCurrentLanguage: function(){
      return currentLanguage;
    }
  }
});

// Sample filter which transforms our UI value
app.filter('translate', function(Translations, LanguageService){
  
  // We receive the input, translate according to the current language and key
  // and return the output.
  return function(input){
    // Translations.en.PAGE_TITLE
    return Translations[LanguageService.getCurrentLanguage()][input] || '';
  };
});

// Sample controller
app.controller('MyController', function(LanguageService){
  $scope.data = {
    // Note that it only holds the "key" to the text. The filter will figure it
    // out for you. This data will be the same across all languages.
    title: 'PAGE_TITLE',
    url: '/home',
  };

  // The only time your controller is 
  $scope.changeLanguage = function(value){
    LanguageService.setCurrentLanguage(value);
  };
});

// Sample UI
<!-- This passes title to the translate, then renders the return value -->
<div>{{ title | translate }}</div>
<button type="button" ng-click="setCurrentLanguage('en')>English</button>
<button type="button" ng-click="setCurrentLanguage('es')>Español</button>

As you can see, the UI isn't aware of language-specific stuff. The only thing it knows is that there's a title. The only inconvenience I see is that your data should carry keys instead of text, and that it should correspond to the keys found in the constants.

In order to add more translations, you just add more key-value pairs in the constant, making sure each key is in different languages. The filter will take care of transforming your data on the UI level given that the data provides the right keys.

Long story short, this is pretty much how Angular Translate works.