From 3f30a89ed5262cc29e187942e5e2146ad542c91b Mon Sep 17 00:00:00 2001 From: Alfonso Bribiesca Date: Tue, 5 Feb 2019 21:55:45 -0600 Subject: [PATCH] Prioritize dot notations if has a parent key (#74) --- dist/lang.min.js | 2 +- src/lang.js | 35 +++++++++++++++++++++++++++-------- test/fixture/messages.json | 2 ++ test/spec/lang_get_spec.js | 4 ++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/dist/lang.min.js b/dist/lang.min.js index dd78f5d..714c3b9 100644 --- a/dist/lang.min.js +++ b/dist/lang.min.js @@ -6,4 +6,4 @@ * @site https://github.com/rmariuzzo/Lang.js * @author Rubens Mariuzzo */ -(function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.Lang=factory()}})(this,function(){"use strict";function inferLocale(){if(typeof document!=="undefined"&&document.documentElement){return document.documentElement.lang}}function convertNumber(str){if(str==="-Inf"){return-Infinity}else if(str==="+Inf"||str==="Inf"||str==="*"){return Infinity}return parseInt(str,10)}var intervalRegexp=/^({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])$/;var anyIntervalRegexp=/({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])/;var defaults={locale:"en"};var Lang=function(options){options=options||{};this.locale=options.locale||inferLocale()||defaults.locale;this.fallback=options.fallback;this.messages=options.messages};Lang.prototype.setMessages=function(messages){this.messages=messages};Lang.prototype.getLocale=function(){return this.locale||this.fallback};Lang.prototype.setLocale=function(locale){this.locale=locale};Lang.prototype.getFallback=function(){return this.fallback};Lang.prototype.setFallback=function(fallback){this.fallback=fallback};Lang.prototype.has=function(key,locale){if(typeof key!=="string"||!this.messages){return false}return this._getMessage(key,locale)!==null};Lang.prototype.get=function(key,replacements,locale){if(!this.has(key,locale)){return key}var message=this._getMessage(key,locale);if(message===null){return key}if(replacements){message=this._applyReplacements(message,replacements)}return message};Lang.prototype.trans=function(key,replacements){return this.get(key,replacements)};Lang.prototype.choice=function(key,number,replacements,locale){replacements=typeof replacements!=="undefined"?replacements:{};replacements.count=number;var message=this.get(key,replacements,locale);if(message===null||message===undefined){return message}var messageParts=message.split("|");var explicitRules=[];for(var i=0;i=leftNumber:count>leftNumber)&&(rightDelimiter==="]"?count<=rightNumber:count=2&&count%10<=4&&(count%100<10||count%100>=20)?1:2;case"cs":case"sk":return count==1?0:count>=2&&count<=4?1:2;case"ga":return count==1?0:count==2?1:2;case"lt":return count%10==1&&count%100!=11?0:count%10>=2&&(count%100<10||count%100>=20)?1:2;case"sl":return count%100==1?0:count%100==2?1:count%100==3||count%100==4?2:3;case"mk":return count%10==1?0:1;case"mt":return count==1?0:count===0||count%100>1&&count%100<11?1:count%100>10&&count%100<20?2:3;case"lv":return count===0?0:count%10==1&&count%100!=11?1:2;case"pl":return count==1?0:count%10>=2&&count%10<=4&&(count%100<12||count%100>14)?1:2;case"cy":return count==1?0:count==2?1:count==8||count==11?2:3;case"ro":return count==1?0:count===0||count%100>0&&count%100<20?1:2;case"ar":return count===0?0:count==1?1:count==2?2:count%100>=3&&count%100<=10?3:count%100>=11&&count%100<=99?4:5;default:return 0}};return Lang}); \ No newline at end of file +(function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.Lang=factory()}})(this,function(){"use strict";function inferLocale(){if(typeof document!=="undefined"&&document.documentElement){return document.documentElement.lang}}function convertNumber(str){if(str==="-Inf"){return-Infinity}else if(str==="+Inf"||str==="Inf"||str==="*"){return Infinity}return parseInt(str,10)}var intervalRegexp=/^({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])$/;var anyIntervalRegexp=/({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])/;var defaults={locale:"en"};var Lang=function(options){options=options||{};this.locale=options.locale||inferLocale()||defaults.locale;this.fallback=options.fallback;this.messages=options.messages};Lang.prototype.setMessages=function(messages){this.messages=messages};Lang.prototype.getLocale=function(){return this.locale||this.fallback};Lang.prototype.setLocale=function(locale){this.locale=locale};Lang.prototype.getFallback=function(){return this.fallback};Lang.prototype.setFallback=function(fallback){this.fallback=fallback};Lang.prototype.has=function(key,locale){if(typeof key!=="string"||!this.messages){return false}return this._getMessage(key,locale)!==null};Lang.prototype.get=function(key,replacements,locale){if(!this.has(key,locale)){return key}var message=this._getMessage(key,locale);if(message===null){return key}if(replacements){message=this._applyReplacements(message,replacements)}return message};Lang.prototype.trans=function(key,replacements){return this.get(key,replacements)};Lang.prototype.choice=function(key,number,replacements,locale){replacements=typeof replacements!=="undefined"?replacements:{};replacements.count=number;var message=this.get(key,replacements,locale);if(message===null||message===undefined){return message}var messageParts=message.split("|");var explicitRules=[];for(var i=0;i=leftNumber:count>leftNumber)&&(rightDelimiter==="]"?count<=rightNumber:count=2&&count%10<=4&&(count%100<10||count%100>=20)?1:2;case"cs":case"sk":return count==1?0:count>=2&&count<=4?1:2;case"ga":return count==1?0:count==2?1:2;case"lt":return count%10==1&&count%100!=11?0:count%10>=2&&(count%100<10||count%100>=20)?1:2;case"sl":return count%100==1?0:count%100==2?1:count%100==3||count%100==4?2:3;case"mk":return count%10==1?0:1;case"mt":return count==1?0:count===0||count%100>1&&count%100<11?1:count%100>10&&count%100<20?2:3;case"lv":return count===0?0:count%10==1&&count%100!=11?1:2;case"pl":return count==1?0:count%10>=2&&count%10<=4&&(count%100<12||count%100>14)?1:2;case"cy":return count==1?0:count==2?1:count==8||count==11?2:3;case"ro":return count==1?0:count===0||count%100>0&&count%100<20?1:2;case"ar":return count===0?0:count==1?1:count==2?2:count%100>=3&&count%100<=10?3:count%100>=11&&count%100<=99?4:5;default:return 0}};return Lang}); \ No newline at end of file diff --git a/src/lang.js b/src/lang.js index 6335d98..781de3d 100644 --- a/src/lang.js +++ b/src/lang.js @@ -272,6 +272,7 @@ */ Lang.prototype._getMessage = function(key, locale) { locale = locale || this.getLocale(); + key = this._parseKey(key, locale); // Ensure message source exists. @@ -282,14 +283,9 @@ // Get message from default locale. var message = this.messages[key.source]; var entries = key.entries.slice(); - var subKey = ''; - while (entries.length && message !== undefined) { - var subKey = !subKey ? entries.shift() : subKey.concat('.', entries.shift()); - if (message[subKey] !== undefined) { - message = message[subKey] - subKey = ''; - } - } + var subKey = entries.join('.'); + message = message !== undefined ? this._getValueInKey(message, subKey) : undefined; + // Get message from fallback locale. if (typeof message !== 'string' && this.messages[key.sourceFallback]) { @@ -312,6 +308,29 @@ return message; }; + Lang.prototype._getValueInKey = function(obj, str) { + // If the full key exists just return the value + if (typeof obj[str] === 'string') { + return obj[str] + } + + str = str.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties + str = str.replace(/^\./, ''); // strip a leading dot + + var parts = str.split('.'); + + for (var i = 0, n = parts.length; i < n; ++i) { + var currentKey = parts.slice(0, i + 1).join('.'); + var restOfTheKey = parts.slice(i + 1, parts.length).join('.') + + if (obj[currentKey]) { + return this._getValueInKey(obj[currentKey], restOfTheKey) + } + } + + return obj; + }; + /** * Return the locale to be used between default and fallback. * @param {String} key diff --git a/test/fixture/messages.json b/test/fixture/messages.json index 2fab709..6c0fb40 100644 --- a/test/fixture/messages.json +++ b/test/fixture/messages.json @@ -109,6 +109,8 @@ }, "plural": "one apple|a million apples", "dot.in.key": "Dot In Key", + "with_parent": "Key That Is Subpart Of A Parent Key", + "with_parent.dot.in.key": "Dot In Key With a Parent Key", "dot.in.key2.nested": { "dot.in.key2.nested": "Dot In Key Nested Tricky" }, diff --git a/test/spec/lang_get_spec.js b/test/spec/lang_get_spec.js index 6c0f531..dee1bcd 100644 --- a/test/spec/lang_get_spec.js +++ b/test/spec/lang_get_spec.js @@ -69,6 +69,10 @@ describe('The lang.get() method', function () { expect(lang.get('messages.dot.in.key')).toBe('Dot In Key'); }); + it('should prioritize the dot in key', function() { + expect(lang.get('messages.with_parent.dot.in.key')).toBe('Dot In Key With a Parent Key'); + }); + it('should return the expected message if the key is nested and has a dot', function() { expect(lang.get('messages.dotInKey.dot.in.key')).toBe('Dot In Key Nested Simple'); expect(lang.get('messages.dot.in.key2.nested.dot.in.key2.nested')).toBe('Dot In Key Nested Tricky');