diff --git a/.github/main.workflow b/.github/main.workflow
new file mode 100644
index 0000000..0620ad0
--- /dev/null
+++ b/.github/main.workflow
@@ -0,0 +1,17 @@
+workflow "New workflow" {
+ on = "push"
+ resolves = ["Run Tests"]
+}
+
+action "Install Dependencies" {
+ uses = "actions/npm@master"
+ runs = "npm"
+ args = "install"
+}
+
+action "Run Tests" {
+ uses = "actions/npm@master"
+ runs = "npm"
+ args = "test"
+ needs = ["Install Dependencies"]
+}
diff --git a/.github/next-version.svg b/.github/next-version.svg
new file mode 100644
index 0000000..644d625
--- /dev/null
+++ b/.github/next-version.svg
@@ -0,0 +1,24 @@
+
diff --git a/.travis.yml b/.travis.yml
index f790335..49b2706 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
language: node_js
node_js:
- - "0.10"
+ - "7"
install:
- npm install
script: npm run build
diff --git a/README.md b/README.md
index 3b3ffd1..9905598 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,10 @@
![NPM Montly Downloads](https://img.shields.io/npm/dm/lang.js.svg)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rmariuzzo/Lang.js/master/LICENSE)
+
+
+
+
## Installation
Different installation methods:
@@ -14,6 +18,16 @@ Different installation methods:
- Bower: `bower install lang.js`
- Manually: [Download latest release](https://github.com/rmariuzzo/Lang.js/releases/latest)
+
+
+
+
+[![Are you interested in the next version?](.github/next-version.svg)](https://github.com/rmariuzzo/Lang.js/tree/next)
+
+
+
+
+
## Documentation
### Initialization
@@ -205,7 +219,7 @@ var lang = new Lang({
'en.fruits': {
'apple': 'apple|apples'
},
- 'es.greetings': {
+ 'es.fruits': {
'apple': 'manzana|manzanas'
}
}
@@ -251,6 +265,9 @@ lang.choice('fruits.apple', 22);
This method act as an alias of [`choice()`](#choice).
+
+
+
## Development
@@ -260,6 +277,10 @@ This method act as an alias of [`choice()`](#choice).
**[Get help!](https://gitter.im/rmariuzzo/Lang.js)**
+
+
+
+
## Testing
To run the tests use the following commands:
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/package-lock.json b/package-lock.json
index d273656..f6b8a8b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "lang.js",
- "version": "1.1.13",
+ "version": "1.1.14",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 8b6c64d..63afd9f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "lang.js",
- "version": "1.1.13",
+ "version": "1.1.14",
"description": "Laravel's Lang in JavaScript!",
"main": "src/lang.js",
"types": "index.d.ts",
diff --git a/src/lang.js b/src/lang.js
index 6335d98..1386607 100644
--- a/src/lang.js
+++ b/src/lang.js
@@ -272,6 +272,42 @@
*/
Lang.prototype._getMessage = function(key, locale) {
locale = locale || this.getLocale();
+ let originalLocale = locale;
+
+ // Handle the scenario where the tranlation string is used as the key.
+ // (https://laravel.com/docs/6.x/localization#using-translation-strings-as-keys)
+ // In this case the Key should be present at the root of the locale.
+ if (typeof(this.messages[locale]) === 'undefined') {
+ // The given locale does not have keys at the root, use the fallback instead.
+ locale = this.getFallback();
+ }
+
+
+ // See if the key is defined.
+ if (typeof(this.messages[locale]) !== 'undefined') {
+ if (typeof(this.messages[locale][key]) !== 'undefined') {
+ return this.messages[locale][key];
+ }
+ }
+
+ //Try with the fallback as well. if we haven't looked there already.
+ if (locale === originalLocale) {
+ locale = this.getFallback();
+ if (typeof(this.messages[locale]) !== 'undefined') {
+ if (typeof(this.messages[locale][key]) !== 'undefined') {
+ return this.messages[locale][key];
+ }
+ }
+ }
+
+ // If we reach here, that means the traslation key did not exist in the provided locale
+ // nor the fallback locale. At this point proceed as normal and expect the rest of the code
+ // to find a valid translation or return the key itself as the translation
+ // (which also takes care of 'Translation strings as key')
+
+ // Reset to the original locale.
+ locale = originalLocale;
+
key = this._parseKey(key, locale);
// Ensure message source exists.
@@ -282,14 +318,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 +343,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..4e27f62 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"
},
@@ -214,5 +216,9 @@
},
"en.unique": {
"samePrefixKeys": "Your order contains :items items with :itemsPapayas papayas and :itemsMangoes mangoes"
+ },
+ "es": {
+ "Hello": "Hola",
+ "Payment received. Thanks!": "Pago recibido. Gracias!"
}
}
diff --git a/test/spec/lang_fallback_spec.js b/test/spec/lang_fallback_spec.js
index 6844fae..87a8eac 100644
--- a/test/spec/lang_fallback_spec.js
+++ b/test/spec/lang_fallback_spec.js
@@ -58,4 +58,23 @@ describe('The lang\'s fallback locale feature', function() {
lang.get('greetings.hello');
}).not.toThrow();
});
+
+ // JSON Translation string as keys
+ it('should return the fallback if tranlation is not availble.', function() {
+ var messages = {
+ 'en': {
+ 'Welcome': 'Welcome to the site.'
+ },
+ 'es': {
+ 'Hello': 'Hola',
+ }
+ };
+ lang = new Lang({
+ messages: messages
+ });
+ lang.setLocale('es');
+ lang.setFallback('en');
+
+ expect(lang.get('Welcome', [], 'es')).toBe('Welcome to the site.');
+ })
});
diff --git a/test/spec/lang_get_spec.js b/test/spec/lang_get_spec.js
index 6c0f531..c492d8b 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');
@@ -85,4 +89,13 @@ describe('The lang.get() method', function () {
itemsMangoes: 2
})).toBe('Your order contains 5 items with 3 papayas and 2 mangoes');
});
+
+ // JSON Translation string as key
+ it('should return the spanish translation when a key exists', function() {
+ expect(lang.get('Hello', [], 'es')).toBe('Hola');
+ });
+
+ it('should return the spanish translation when a key exists and key contains a period (.)', function() {
+ expect(lang.get('Payment received. Thanks!', [], 'es')).toBe('Pago recibido. Gracias!');
+ });
});