Created
January 29, 2016 14:04
-
-
Save teeli/cdf5d75034fc75621bba to your computer and use it in GitHub Desktop.
A drop-in non-jquery replacement for Silverstripe's i18n.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if(typeof(ss) == 'undefined') ss = {}; | |
/* | |
* Lightweight clientside i18n implementation. | |
* Caution: Only available after DOM loaded because we need to detect the language | |
* | |
* For non-i18n stub implementation, see framework/javascript/i18nx.js | |
* | |
* Based on jQuery i18n plugin: 1.0.0 Feb-10-2008 | |
* | |
* Dual licensed under the MIT and GPL licenses: | |
* http://www.opensource.org/licenses/mit-license.php | |
* http://www.gnu.org/licenses/gpl.html | |
* | |
* Based on 'javascript i18n that almost doesn't suck' by markos | |
* http://markos.gaivo.net/blog/?p=100 | |
*/ | |
ss.i18n = { | |
currentLocale: null, | |
defaultLocale: 'en_US', | |
lang: {}, | |
inited: false, | |
init: function() { | |
if(this.inited) return; | |
this.currentLocale = this.detectLocale(); | |
this.inited = true; | |
}, | |
/** | |
* set_locale() | |
* Set locale in long format, e.g. "de_AT" for Austrian German. | |
* @param string locale | |
*/ | |
setLocale: function(locale) { | |
this.currentLocale = locale; | |
}, | |
/** | |
* getLocale() | |
* Get locale in long format. Falls back to i18n.defaut_locale. | |
* @return string | |
*/ | |
getLocale: function() { | |
return (this.currentLocale) ? this.currentLocale : this.defaultLocale; | |
}, | |
/** | |
* _() | |
* The actual translation function. Looks the given string up in the | |
* dictionary and returns the translation if one exists. If a translation | |
* is not found, returns the original word | |
* | |
* @param string entity A "long" locale format, e.g. "de_DE" (Required) | |
* @param string fallbackString (Required) | |
* @param int priority (not used) | |
* @param string context Give translators context for the string | |
* @return string : Translated word | |
* | |
*/ | |
_t: function (entity, fallbackString, priority, context) { | |
this.init(); | |
var langName = this.getLocale().replace(/_[\w]+/i, ''); | |
var defaultlangName = this.defaultLocale.replace(/_[\w]+/i, ''); | |
if (this.lang && this.lang[this.getLocale()] && this.lang[this.getLocale()][entity]) { | |
return this.lang[this.getLocale()][entity]; | |
} else if (this.lang && this.lang[langName] && this.lang[langName][entity]) { | |
return this.lang[langName][entity]; | |
} else if (this.lang && this.lang[this.defaultLocale] && this.lang[this.defaultLocale][entity]) { | |
return this.lang[this.defaultLocale][entity]; | |
} else if (this.lang && this.lang[defaultlangName] && this.lang[defaultlangName][entity]) { | |
return this.lang[defaultlangName][entity]; | |
} else if(fallbackString) { | |
return fallbackString; | |
} else { | |
return ''; | |
} | |
}, | |
/** | |
* Add entities to a dictionary. If a dictionary doesn't | |
* exist for this locale, its automatically created. | |
* Existing entities are overwritten. | |
* | |
* @param string locale | |
* @param Object dict | |
*/ | |
addDictionary: function(locale, dict) { | |
if(!this.lang[locale]) this.lang[locale] = {}; | |
for(entity in dict) { | |
this.lang[locale][entity] = dict[entity]; | |
} | |
}, | |
/** | |
* Get dictionary for a specific locale. | |
* | |
* @param string locale | |
*/ | |
getDictionary: function(locale) { | |
return this.lang[locale]; | |
}, | |
/** | |
* stripStr() | |
* | |
* @param string str : The string to strip | |
* @return string result : Stripped string | |
* | |
*/ | |
stripStr: function(str) { | |
return str.replace(/^\s*/, "").replace(/\s*$/, ""); | |
}, | |
/** | |
* stripStrML() | |
* | |
* @param string str : The multi-line string to strip | |
* @return string result : Stripped string | |
* | |
*/ | |
stripStrML: function(str) { | |
// Split because m flag doesn't exist before JS1.5 and we need to | |
// strip newlines anyway | |
var parts = str.split('\n'); | |
for (var i=0; i<parts.length; i++) | |
parts[i] = stripStr(parts[i]); | |
// Don't join with empty strings, because it "concats" words | |
// And strip again | |
return stripStr(parts.join(" ")); | |
}, | |
/** | |
* Substitutes %s with parameters | |
* given in list. %%s is used to escape %s. | |
* | |
* @param string S : The string to perform the substitutions on. | |
* @return string The new string with substitutions made | |
*/ | |
sprintf: function(S) { | |
if (arguments.length == 1) return S; | |
var args = [], | |
len = arguments.length, | |
index = 0, | |
regx = new RegExp('(.?)(%s)', 'g'), | |
result; | |
for (var i=1; i<len; ++i) { | |
args.push(arguments[i]); | |
}; | |
result = S.replace(regx, function(match, subMatch1, subMatch2, offset, string){ | |
if (subMatch1 == '%') return match; // skip %%s | |
return subMatch1 + args[index++]; | |
}); | |
return result; | |
}, | |
/** | |
* Substitutes variables with a list of injections. | |
* | |
* @param string S : The string to perform the substitutions on. | |
* @param object map : An object with the substitions map e.g. {var: value} | |
* @return string The new string with substitutions made | |
*/ | |
inject: function(S, map) { | |
var regx = new RegExp("\{([A-Za-z0-9_]*)\}", "g"), | |
result; | |
result = S.replace(regx, function(match, key, offset, string){ | |
return (map[key]) ? map[key] : match; | |
}); | |
return result; | |
}, | |
/** | |
* Detect document language settings by looking at <meta> tags. | |
* If no match is found, returns this.defaultLocale. | |
* | |
* @todo get by <html lang=''> - needs modification of SSViewer | |
* | |
* @return string Locale in mixed lowercase/uppercase format suitable | |
* for usage in ss.i18n.lang arrays (e.g. 'en_US'). | |
*/ | |
detectLocale: function() { | |
var rawLocale; | |
var detectedLocale; | |
// get by container tag | |
rawLocale = document.querySelector('body').getAttribute('lang'); | |
// get by meta | |
if(!rawLocale) { | |
var metas = document.getElementsByTagName('meta'); | |
for(var i=0; i<metas.length; i++) { | |
if(metas[i].attributes['http-equiv'] && metas[i].attributes['http-equiv'].nodeValue.toLowerCase() == 'content-language') { | |
rawLocale = metas[i].attributes['content'].nodeValue; | |
} | |
} | |
} | |
// fallback to default locale | |
if(!rawLocale) rawLocale = this.defaultLocale; | |
var rawLocaleParts = rawLocale.match(/([^-|_]*)[-|_](.*)/); | |
// get locale (e.g. 'en_US') from common name (e.g. 'en') | |
// by looking at ss.i18n.lang tables | |
if(rawLocale.length == 2) { | |
for(compareLocale in ss.i18n.lang) { | |
if(compareLocale.substr(0,2).toLowerCase() == rawLocale.toLowerCase()) { | |
detectedLocale = compareLocale; | |
break; | |
} | |
} | |
} else if(rawLocaleParts) { | |
detectedLocale = rawLocaleParts[1].toLowerCase() + '_' + rawLocaleParts[2].toUpperCase(); | |
} | |
return detectedLocale; | |
}, | |
/** | |
* Attach an event listener to the given object. | |
* Modeled after behaviour.js, but externalized | |
* to keep the i18n library standalone for now. | |
*/ | |
addEvent: function(obj, evType, fn, useCapture){ | |
if (obj.addEventListener){ | |
obj.addEventListener(evType, fn, useCapture); | |
return true; | |
} else if (obj.attachEvent){ | |
var r = obj.attachEvent("on"+evType, fn); | |
return r; | |
} else { | |
alert("Handler could not be attached"); | |
} | |
} | |
}; | |
ss.i18n.addEvent(window, "load", function() { | |
ss.i18n.init(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment