Node.js Localization
Just stumble upon this problem when I was in the making of wenku10 services.

This site still in progress but I thought I could share my way of retrieving localized strings.

gettext is ugly

First of all, I don't like gettext. _( "I hate seeing things like this inside the source code" ).

Instead, I would like to imagine that the translate-ables are virtual static constant. Which means a decent way of declaration should be something looked like this:

LocaleString.Zone.Type.Category.THE_STRING_I_WANT

This will reloved to the file:
locale/<lang-code>/Zone/Type/Category.js:
module.exports = {
    THE_STRING_I_WANT: "The string I want"
};

ES6 Proxy

In PHP, there are magic functions __get, __set, __toString to archive this implementation.

Similarly, in node.js, there is proxy!

Now let's just dive into the code1:

localization.js
"use strict";

const ProxyLocale = function( name, _locale, _parent )
{
	var _thisProxy = new Proxy( _locale, {

		get: ( target, prop ) => {

			switch( prop )
			{
				case Symbol.toPrimitive:
					return () => _locale( global.lang || "en-US" );

				case "name":
					return _parent ? `${_parent.name}.${name}` : name;

				case "inspect":
					return _parent
						? () => `LocaleString[ Locale${_parent.name}.${name} ]`
						: () => `LocaleString`
						;
			}

			if( target[ prop ] == undefined )
			{
				target[ prop ] = ProxyLocale(
					prop
					, function( lang, stack )
					{
						if( !stack )
						{
							stack = stack || [ prop, name ];
						}
						else
						{
							stack[1] = name + "." + stack[1];
						}

						return _locale( lang, stack );
					}
					, _thisProxy );
			}

			return Reflect.get( target, prop );
		}

	} );

	return _thisProxy;
};

const rLocale = function( lang, stack )
{
	var Zone = "LocaleSX." + lang + stack[1];

	try
	{
		// Resolve your string here, cache it if you want
		console.log( "[DEBUG] " + Zone + ":" + stack[0] );

		return "My awesome translated string";
	}
	catch( e )
	{
		console.log( e );
	}

	return Zone + "." + stack[0];
};

module.exports = ProxyLocale( "", rLocale );

test.js
"use strict";
const Locale = require( "./localization.js" );

console.log( Locale.Zone.Category.Penguin.MY_AWESOME_STRING );
console.log( Locale.Zone.Category.Penguin.MY_AWESOME_STRING + "" );
console.log( Locale.Zone.Category.Penguin.MY_AWESOME_STRING( "zh-TW" ) );

// Output
/*
LocaleString[ Locale.Zone.Category.Penguin.MY_AWESOME_STRING ]
[DBBUG] LocaleSX.en-US.Zone.Category.Penguin:MY_AWESOME_STRING
My awesome translated string
[DBBUG] LocaleSX.zh-TW.Zone.Category.Penguin:MY_AWESOME_STRING
My awesome translated string
*/

As you can see, by chaining the proxy together. The missing object is created dynamically when accessed. Resolved when converted to primitive.

To be honest, the recursive behaviour is hard to get. These type of things are usually made by trial and error. For example, I didn't know console.log will call inspect on proxy until I tried.

Performance

Didn't test. But the resolving object does induce some degree of overhead. However the object only need to be created once so it should be ok... actually I don't know. Will test later.
  1. The code is modified from botansx-modular package. Which I wroted for BotanSS. which is also a custom brewed framework.
Profile picture
斟酌 鵬兄
Sun Jul 03 2016 12:24:12 GMT+0000 (Coordinated Universal Time)
Last modified: Sun Jul 03 2016 13:30:56 GMT+0000 (Coordinated Universal Time)
Comments
No comments here.
Do you even comment?
website: 
Not a valid website
Invalid email format
Please enter your email
*Name: 
Please enter a name
Submit
抱歉,Google Recaptcha 服務被牆掉了,所以不能回覆了