2

I'm trying to create an multilingual javascript object. Something like:

function language(lang){
    this.error = {
        fname : function(lang){
            var fname = {
                vn : "Tên",
                en : "First Name",
            };
            return fname.lang;
        },
    };
}

When I attempted to call fname with this,

var lang = new language("en");
console.log(lang.error.fname);

it turned out function(lang){var fname = {vn : "Tên",en : "First Name",};return fname.lang;} instead of First Name. What's wrong with my code? And how could I get fname value correctly?

I'm pretty new to JS. I appreciate if somebody helps me figure out this issue.

MrCode
  • 63,975
  • 10
  • 90
  • 112
Lewis
  • 14,132
  • 12
  • 66
  • 87

3 Answers3

1

You need to call fname like a function

lang.error.fname()

Also, you should modify your function, because currently you ovveride lang variable inside fname() function:

 fname : function(){
        var fname = {
            vn : "Tên",
            en : "First Name",
        };
        return fname[lang];
    }

FIDDLE

Artem Petrosian
  • 2,964
  • 1
  • 22
  • 30
1

As a quick fix, you need to use the [] syntax when returning the value, and remove the lang argument from the fname() function, so it becomes:

Fiddle

function language(lang){
    this.error = {
        fname : function(){
            var fname = {
                vn : "Tên",
                en : "First Name",
            };
            return fname[lang];
        },
    };
}

language = new language('en');
console.log( language.error.fname() );

This works, but it isn't a very good structure. Look at the syntax for getting a translation, it's very strange looking, why is fname() a function? Also look at how the translations are defined, they are mixed in with lots of JavaScript functions and logic, this makes it hard to maintain, and impossible for anyone to add translations that aren't familiar with JavaScript.


Let's look at a different structure:

Fiddle

function language(lang){
    var translations = {
        vn : {
            error_fname : "Tên",
        },
        en : {
            error_fname : 'First Name',
            error_lname : 'Last Name'
        }
    };

    var currentLang = 'en'; // default

    function get(key){
        return translations[currentLang][key];
    }

    // public pointers
    this.get = get;

    // constructor
    if(typeof lang != 'undefined' && translations[lang]){
        currentLang = lang;   
    }
}

var language = new language('en');
console.log( language.get('error_fname') );

First look at how the translations are defined. It's very clear and there is one en object for all english translations. All of the english translations live in the en object - makes sense right? There is no JavaScript logic mixed in with the translations, which means you can do things like use a JSON structure that can be read by translators that aren't familiar with JavaScript. You could even give translators a basic .ini file, and have it transformed into this JavaScript/JSON structure because of its simplicity.

There are other benefits, such as the translations are encapsulated, and aren't directly accessible without going through the get() method.


Here's a different way of doing it, that uses the . syntax for getting a translation:

Fiddle

function language(lang){
    var translations = {
        vn : {
            error_fname : "Tên",
        },
        en : {
            error_fname : 'First Name',
            error_lname : 'Last Name'
        }
    };

    var currentLang = 'en'; // default

    if(typeof lang != 'undefined' && translations[lang]){
        currentLang = lang;   
    }

    return translations[currentLang];
}

var language = language('en');
console.log( language.error_fname );

There is no get() function, which you might or might not prefer. Personally I prefer the other way because of the encapsulation and the OOP style.

MrCode
  • 63,975
  • 10
  • 90
  • 112
  • Your answer is awesome. I got your point. Big thanks for this. – Lewis May 01 '14 at 09:20
  • You mentioned about `.ini` approach. How could this method be done in this situation? Thank you, again. – Lewis May 01 '14 at 10:08
  • 1
    To use an external ini file for the translations, you would have to have some server side code to parse the ini and encode it into JSON, you can then assign the JSON directly to the `translations` variable. Some server side languages have built in parsers for ini files (such as PHP). – MrCode May 01 '14 at 11:51
0

The way you have written it is a bit odd:

function language(lang){ // Constructor, that's ok
    this.error = {  // An attribute that is another object
        fname : function(lang){ // this attribute has a function
            var fname = { // the function has another object as a local variable with the same name!
                vn : "Tên",
                en : "First Name",
            };
            return fname.lang; //the object has vn and en attributes, but no lang
        },
    };
}

I'll try to define what kind of structure you want and then rewrite that mess from scratch.

Pablo Lozano
  • 10,122
  • 2
  • 38
  • 59