diff --git a/Readme.md b/Readme.md index 836fa9d..0360dd3 100644 --- a/Readme.md +++ b/Readme.md @@ -81,7 +81,7 @@ Default is `!!]`. pseudoloc.str('A test string with a %token%.') // [!!Á ţȇšŧ śťřīņğ ŵıţħ ą %token%.##] -#### Delimiter, StartDelimiter, EndDelimiter +#### Delimiter, StartDelimiter, EndDelimiter, Delimiters Specifies the token delimiter. Any characters between token delimiters will not be pseudolocalized. Tokens are used to replace data within localized strings. You can either specify a single delimiter or use startDelimiter and endDelimiter to specify the delimiters seperately. @@ -96,6 +96,39 @@ Default is `%`. pseudoloc.str('A test string with a {{token}}.') // [!!Á ţȇšŧ śťřīņğ ŵıţħ ą {{token}}.!!] +If you need to support multiple types of delimiters, you can pass an array of delimiters (single or pairs) to the `delimiters` option. + +The `delimiters` option takes an array of objects. Set properties on the objects as follows: + + * `{ start, end }`: specifies a pair of start and end delimiters, just like using `startDelimiter` and `endDelimiter`: + ``` + { start: '<', end: '>' } + ``` + + * `{ both }`: specifies a marker to use as both the start and end delimiters, just like using `delimiter` + ``` + { both: '$$' } + ``` + + * `{ full }`: specifies the entire pattern for the delimiter. This is useful for cases where the token doesn't have a start marker, name, and end marker, for example with printf-style placeholders `%s`, `%d`, etc. + ``` + { full: '%d' } + ``` + +Under the hood these strings are combined into a pattern that eventually is compiled into a RegExp. That can affect you in a couple of ways: + + 1. You can use regular expression matchers in your delimiters + 2. If your delimiter includes any characters that are special characters in regular expressions, they will need to be escaped + +For example, to match named sprintf-style placeholders (such as `%(name)s`), you need to escape the parentheses: + + // Note the double-backslash, which becomes a `\(` in the string + pseudoloc.option.startDelimiter = '%\\('; + // Note the square brackets, so it matches `)s` or `)d` + pseudoloc.option.endDelimiter = '\\)[sd]'; + pseudoloc.str('A test string with a %(token)s.'); + // [!!Á ţȇšŧ śťřīņğ ŵıţħ ą %(token)s.!!] + #### Extend Extends the width of the string by the specified percentage. Useful if you will be localizing into languages such as German which can be 30% longer than English. diff --git a/pseudoloc.js b/pseudoloc.js index 5b685c7..ea5747d 100644 --- a/pseudoloc.js +++ b/pseudoloc.js @@ -73,7 +73,23 @@ pseudoloc = function() { return pStr; }; pseudoloc.str = function(str) { - var opts = pseudoloc.option, startdelim = opts.startDelimiter || opts.delimiter, enddelim = opts.endDelimiter || opts.delimiter, re = new RegExp(startdelim + "\\s*[\\w\\.\\s*]+\\s*" + enddelim, "g"), m, tokens = [], i = 0, tokenIdx = 0, result = "", c, pc; + function makeTokenRegex(delims, tokenNameDelim) { + var tokenMatchers = delims.reduce(function(result, delim) { + if (delim.hasOwnProperty("both")) { + result.push(delim.both + tokenNameDelim + delim.both); + } else if (delim.hasOwnProperty("start") && delim.hasOwnProperty("end")) { + result.push(delim.start + tokenNameDelim + delim.end); + } else if (delim.hasOwnProperty("full")) { + result.push(delim.full); + } + return result; + }, []); + return new RegExp(tokenMatchers.join("|"), "g"); + } + var opts = pseudoloc.option, tokenNameDelim = "\\s*[\\w\\.\\s*]+\\s*", startdelim = opts.startDelimiter || opts.delimiter, enddelim = opts.endDelimiter || opts.delimiter, delims = opts.delimiters || [ { + start: startdelim, + end: enddelim + } ], re = makeTokenRegex(delims, tokenNameDelim), m, tokens = [], i = 0, tokenIdx = 0, result = "", c, pc; while (m = re.exec(str)) { tokens.push(m); } diff --git a/pseudoloc.min.js b/pseudoloc.min.js index f6f220e..25d0722 100644 --- a/pseudoloc.min.js +++ b/pseudoloc.min.js @@ -1 +1 @@ -pseudoloc=function(){var S={version:"1.1.0",option:{},reset:function(){S.option={prepend:"[!!",append:"!!]",delimiter:"%",startDelimiter:"",endDelimiter:"",extend:0,override:void 0}}};return S.reset(),S.table={A:String.fromCharCode(192,193,194,195,196,197,256,258,260,506,512,514),a:String.fromCharCode(224,225,226,227,228,229,257,259,261,507,513,515),B:String.fromCharCode(223,385,579,665),b:String.fromCharCode(384,386,387,388,389,595),C:String.fromCharCode(262,264,266,268),c:String.fromCharCode(263,265,267,269),D:String.fromCharCode(270,272,393,394),d:String.fromCharCode(271,273),E:String.fromCharCode(274,276,278,280,282,516,518),e:String.fromCharCode(275,277,279,281,283,517,519),F:String.fromCharCode(401),f:String.fromCharCode(402),G:String.fromCharCode(284,286,288,290),g:String.fromCharCode(285,287,289,291),H:String.fromCharCode(292,294),h:String.fromCharCode(293,295),I:String.fromCharCode(296,298,300,302,304),i:String.fromCharCode(297,299,301,303,305),J:String.fromCharCode(309),j:String.fromCharCode(308),K:String.fromCharCode(310,408),k:String.fromCharCode(311,312,409),L:String.fromCharCode(313,315,317,319,321),l:String.fromCharCode(314,316,318,320,322),N:String.fromCharCode(323,325,327,330,413),n:String.fromCharCode(324,326,328,329,331,414),O:String.fromCharCode(332,334,336,416),o:String.fromCharCode(333,335,337,417),P:String.fromCharCode(420),p:String.fromCharCode(421,447),Q:String.fromCharCode(490,492),q:String.fromCharCode(491,493,587),R:String.fromCharCode(340,342,344,422),r:String.fromCharCode(341,343,345),S:String.fromCharCode(346,348,350,352),s:String.fromCharCode(347,349,351,353),T:String.fromCharCode(354,356,358),t:String.fromCharCode(355,357,359),U:String.fromCharCode(360,362,364,366,368,370),u:String.fromCharCode(361,363,365,367,369,371),W:String.fromCharCode(372),w:String.fromCharCode(373),Y:String.fromCharCode(374,376,435,562,590),y:String.fromCharCode(375,436,563,591),Z:String.fromCharCode(377,379,381,437),z:String.fromCharCode(378,380,382,438)},S.pad=function(r,o){for(var e=Math.floor(r.length*o),C=r;e--;){C+=["ö","🐔","ఛ","ฒ"," ","そ","開","अ","㤌","కె"][Math.floor(10*Math.random())]}return C},S.str=function(r){for(var o,e,C,t=S.option,n=t.startDelimiter||t.delimiter,i=t.endDelimiter||t.delimiter,d=new RegExp(n+"\\s*[\\w\\.\\s*]+\\s*"+i,"g"),a=[],f=0,h=0,g="";o=d.exec(r);)a.push(o);for(var m=a[h++]||{index:-1};f' }, + { start: '<', end: '\\/>' }, + { start: '{{', end: '}}' } + ]; + var s1 = pseudoloc.str('%%value%%'); + s1.indexOf('%%value%%').should.not.eql(-1); + + var s2 = pseudoloc.str('%d files'); + s2.indexOf('%d').should.not.eql(-1); + s2.indexOf('files').should.eql(-1); + + var s3 = pseudoloc.str('%s files'); + s3.indexOf('%s').should.not.eql(-1); + s3.indexOf('files').should.eql(-1); + + var s4 = pseudoloc.str('Hello %(userName)s!'); + s4.indexOf('%(userName)s').should.not.eql(-1); + s4.indexOf('Hello').should.eql(-1); + + var s5 = pseudoloc.str('%(count)d tacos!'); + s5.indexOf('%(count)d').should.not.eql(-1); + s5.indexOf('tacos').should.eql(-1); + + var s6 = pseudoloc.str('this is bold text'); + s6.indexOf('').should.not.eql(-1); + s6.indexOf('').should.not.eql(-1); + s6.indexOf('bold').should.eql(-1); + + var s7 = pseudoloc.str('this is stuff'); + s7.indexOf('').should.not.eql(-1); + s7.indexOf('stuff').should.eql(-1); + + var s8 = pseudoloc.str('remove %(user)s from on %(date)s'); + s8.indexOf('%(user)s').should.not.eql(-1); + s8.indexOf('').should.not.eql(-1); + s8.indexOf('').should.not.eql(-1); + s8.indexOf('').should.not.eql(-1); + s8.indexOf('').should.not.eql(-1); + s8.indexOf('%(date)s').should.not.eql(-1); + s8.indexOf('').should.not.eql(-1); + s8.indexOf('remove').should.eql(-1); + s8.indexOf('from').should.eql(-1); + s8.indexOf('on').should.eql(-1); + }); it('should pad the string by the specified pad amount', function() { pseudoloc.option.extend = 0.2; @@ -93,4 +143,4 @@ describe('pseudoloc.str', function() { s1.should.eql('_____________________'); }); -}); \ No newline at end of file +});