diff --git a/README.md b/README.md index eb61598..d7759c5 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,19 @@ translator.addReplacement('role', user.getRole()); translator.translate('admin.%role%'); ``` +## Filters (eg. markdown) + +If you want some special formatting in your translations (for example markdown syntax or others) you can add filter +which will automatically transform all successfully translated messages. + +``` +translator.addFilter(function(message) { + return message.split('').reverse().join(''); +}); + +console.log(translator.translate('homepage.title')); // output is reversed title in homepage dictionary +``` + ## List of translations Sometimes you may want to display list of texts but don't want to create translations with these names: item1, item2, diff --git a/lib/Translator.js b/lib/Translator.js index 58c5700..12439c3 100644 --- a/lib/Translator.js +++ b/lib/Translator.js @@ -31,6 +31,8 @@ Translator.prototype.replacements = null; + Translator.prototype.filters = null; + Translator.prototype.data = null; Translator.prototype.cache = null; @@ -39,6 +41,7 @@ var config, data, language, stack, _config; this.plurals = {}; this.replacements = {}; + this.filters = []; this.data = {}; if (!pathOrLoader) { throw new Error('You have to set path to base directory or to config file or loader.'); @@ -136,6 +139,28 @@ return this; }; + Translator.prototype.addFilter = function(fn) { + this.filters.push(fn); + return this; + }; + + Translator.prototype.applyFilters = function(translation) { + var filter, i, t, _i, _j, _len, _len1, _ref; + if (Object.prototype.toString.call(translation) === '[object Array]') { + for (i = _i = 0, _len = translation.length; _i < _len; i = ++_i) { + t = translation[i]; + translation[i] = this.applyFilters(t); + } + return translation; + } + _ref = this.filters; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + filter = _ref[_j]; + translation = filter(translation); + } + return translation; + }; + Translator.prototype.loadCategory = function(_path, name, language) { var categoryName, conds, data, file; if (language == null) { @@ -230,7 +255,7 @@ }; Translator.prototype.translate = function(message, count, args) { - var language, match, num, params, translation; + var found, language, match, num, params, translation; if (count == null) { count = null; } @@ -242,6 +267,7 @@ count = params[1]; args = params[2]; language = this.language; + found = false; if (typeof message !== 'string') { return message; } @@ -259,7 +285,7 @@ message = match[2]; } if (language === null) { - throw new Error('You have to set language'); + throw new Error('You have to set language.'); } num = null; if ((match = message.match(/(.+)\[(\d+)\]$/)) !== null) { @@ -268,6 +294,7 @@ } message = this.applyReplacements(message, args); translation = this.findTranslation(message, language); + found = this.hasTranslation(message, language); if (num !== null) { if (!this.isList(translation)) { throw new Error('Translation ' + message + ' is not a list.'); @@ -282,6 +309,9 @@ } } message = this.prepareTranslation(message, args); + if (found) { + message = this.applyFilters(message); + } return message; }; diff --git a/src/Translator.coffee b/src/Translator.coffee index 1c862d9..7285cf8 100644 --- a/src/Translator.coffee +++ b/src/Translator.coffee @@ -23,6 +23,8 @@ class Translator replacements: null + filters: null + data: null cache: null @@ -31,6 +33,7 @@ class Translator constructor: (pathOrLoader) -> @plurals = {} @replacements = {} + @filters = [] @data = {} if !pathOrLoader @@ -119,6 +122,24 @@ class Translator return @ + addFilter: (fn) -> + @filters.push(fn) + return @ + + + applyFilters: (translation) -> + if Object.prototype.toString.call(translation) == '[object Array]' + for t, i in translation + translation[i] = @applyFilters(t) + + return translation + + for filter in @filters + translation = filter(translation) + + return translation + + loadCategory: (_path, name, language = @language) -> categoryName = _path + '/' + name if typeof @data[categoryName] == 'undefined' @@ -191,6 +212,7 @@ class Translator count = params[1] args = params[2] language = @language + found = false if typeof message != 'string' then return message @@ -206,7 +228,7 @@ class Translator message = match[2] if language == null - throw new Error 'You have to set language' + throw new Error 'You have to set language.' num = null if (match = message.match(/(.+)\[(\d+)\]$/)) != null @@ -216,6 +238,8 @@ class Translator message = @applyReplacements(message, args) translation = @findTranslation(message, language) + found = @hasTranslation(message, language) + if num != null if !@isList(translation) throw new Error 'Translation ' + message + ' is not a list.' @@ -230,6 +254,9 @@ class Translator message = @prepareTranslation(message, args) + if found + message = @applyFilters(message) + return message diff --git a/test/browser/application.js b/test/browser/application.js index ee03980..fd634e0 100644 --- a/test/browser/application.js +++ b/test/browser/application.js @@ -268,6 +268,8 @@ Translator.prototype.replacements = null; + Translator.prototype.filters = null; + Translator.prototype.data = null; Translator.prototype.cache = null; @@ -276,6 +278,7 @@ var config, data, language, stack, _config; this.plurals = {}; this.replacements = {}; + this.filters = []; this.data = {}; if (!pathOrLoader) { throw new Error('You have to set path to base directory or to config file or loader.'); @@ -373,6 +376,28 @@ return this; }; + Translator.prototype.addFilter = function(fn) { + this.filters.push(fn); + return this; + }; + + Translator.prototype.applyFilters = function(translation) { + var filter, i, t, _i, _j, _len, _len1, _ref; + if (Object.prototype.toString.call(translation) === '[object Array]') { + for (i = _i = 0, _len = translation.length; _i < _len; i = ++_i) { + t = translation[i]; + translation[i] = this.applyFilters(t); + } + return translation; + } + _ref = this.filters; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + filter = _ref[_j]; + translation = filter(translation); + } + return translation; + }; + Translator.prototype.loadCategory = function(_path, name, language) { var categoryName, conds, data, file; if (language == null) { @@ -467,7 +492,7 @@ }; Translator.prototype.translate = function(message, count, args) { - var language, match, num, params, translation; + var found, language, match, num, params, translation; if (count == null) { count = null; } @@ -479,6 +504,7 @@ count = params[1]; args = params[2]; language = this.language; + found = false; if (typeof message !== 'string') { return message; } @@ -496,7 +522,7 @@ message = match[2]; } if (language === null) { - throw new Error('You have to set language'); + throw new Error('You have to set language.'); } num = null; if ((match = message.match(/(.+)\[(\d+)\]$/)) !== null) { @@ -505,6 +531,7 @@ } message = this.applyReplacements(message, args); translation = this.findTranslation(message, language); + found = this.hasTranslation(message, language); if (num !== null) { if (!this.isList(translation)) { throw new Error('Translation ' + message + ' is not a list.'); @@ -519,6 +546,9 @@ } } message = this.prepareTranslation(message, args); + if (found) { + message = this.applyFilters(message); + } return message; }; diff --git a/test/node/lib/Translator.js b/test/node/lib/Translator.js index ed9ba43..74e6330 100644 --- a/test/node/lib/Translator.js +++ b/test/node/lib/Translator.js @@ -214,9 +214,33 @@ it('should return translated text from dictionary for different language', function() { return expect(translator.translate('cs|web.pages.homepage.simple.title')).to.be.equal('Titulek promo boxu'); }); - return it('should return original text if text is eclosed in \':\'', function() { + it('should return original text if text is eclosed in \':\'', function() { return expect(translator.translate(':cs|do.not.translate.me:')).to.be.equal('do.not.translate.me'); }); + it('should not apply filters to not translated messages', function() { + translator.addFilter(function(message) { + return message.split('').reverse().join(''); + }); + return expect(translator.translate('unknown.title')).to.be.equal('unknown.title'); + }); + it('should not apply filters to not translatable messages', function() { + translator.addFilter(function(message) { + return message.split('').reverse().join(''); + }); + return expect(translator.translate(':web.pages.homepage.simple.title:')).to.be.equal('web.pages.homepage.simple.title'); + }); + it('should apply filters to simple translations', function() { + translator.addFilter(function(message) { + return message.split('').reverse().join(''); + }); + return expect(translator.translate('web.pages.homepage.simple.title')).to.be.equal('xob omorp fo eltiT'); + }); + return it('should apply filters for lists of translations', function() { + translator.addFilter(function(message) { + return message.split('').reverse().join(''); + }); + return expect(translator.translate('web.pages.homepage.promo.fruits', 3)).to.be.eql(['sananab 3', 'snortic 3', 'segnaro 3']); + }); }); describe('#translatePairs()', function() { it('should throw an error if message to translate are not arrays', function() { diff --git a/test/node/src/Translator.coffee b/test/node/src/Translator.coffee index 733e903..1f01563 100644 --- a/test/node/src/Translator.coffee +++ b/test/node/src/Translator.coffee @@ -232,6 +232,34 @@ describe 'Translator', -> it 'should return original text if text is eclosed in \':\'', -> expect(translator.translate(':cs|do.not.translate.me:')).to.be.equal('do.not.translate.me') + it 'should not apply filters to not translated messages', -> + translator.addFilter( (message) -> + return message.split('').reverse().join('') + ) + expect(translator.translate('unknown.title')).to.be.equal('unknown.title') + + it 'should not apply filters to not translatable messages', -> + translator.addFilter( (message) -> + return message.split('').reverse().join('') + ) + expect(translator.translate(':web.pages.homepage.simple.title:')).to.be.equal('web.pages.homepage.simple.title') + + it 'should apply filters to simple translations', -> + translator.addFilter( (message) -> + return message.split('').reverse().join('') + ) + expect(translator.translate('web.pages.homepage.simple.title')).to.be.equal('xob omorp fo eltiT') + + it 'should apply filters for lists of translations', -> + translator.addFilter( (message) -> + return message.split('').reverse().join('') + ) + expect(translator.translate('web.pages.homepage.promo.fruits', 3)).to.be.eql([ + 'sananab 3' + 'snortic 3' + 'segnaro 3' + ]) + describe '#translatePairs()', -> it 'should throw an error if message to translate are not arrays', ->