1// https://github.com/rubenv/pofile 2// Version 1.1.4 3 4require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 5 6},{}],"pofile":[function(require,module,exports){ 7function trim(string) { 8 return string.replace(/^\s+|\s+$/g, ''); 9} 10 11var PO = function () { 12 this.comments = []; 13 this.extractedComments = []; 14 this.headers = {}; 15 this.headerOrder = []; 16 this.items = []; 17}; 18 19PO.prototype.save = function (filename, callback) { 20 require('fs').writeFile(filename, this.toString(), callback); 21}; 22 23PO.prototype.toString = function () { 24 var lines = []; 25 26 if (this.comments) { 27 this.comments.forEach(function (comment) { 28 lines.push(('# ' + comment).trim()); 29 }); 30 } 31 if (this.extractedComments) { 32 this.extractedComments.forEach(function (comment) { 33 lines.push(('#. ' + comment).trim()); 34 }); 35 } 36 37 lines.push('msgid ""'); 38 lines.push('msgstr ""'); 39 40 var self = this; 41 var headerOrder = []; 42 43 this.headerOrder.forEach(function (key) { 44 if (key in self.headers) { 45 headerOrder.push(key); 46 } 47 }); 48 49 var keys = Object.keys(this.headers); 50 51 keys.forEach(function (key) { 52 if (headerOrder.indexOf(key) === -1) { 53 headerOrder.push(key); 54 } 55 }); 56 57 headerOrder.forEach(function (key) { 58 lines.push('"' + key + ': ' + self.headers[key] + '\\n"'); 59 }); 60 61 lines.push(''); 62 63 this.items.forEach(function (item) { 64 lines.push(item.toString()); 65 lines.push(''); 66 }); 67 68 return lines.join('\n'); 69}; 70 71PO.load = function (filename, callback) { 72 require('fs').readFile(filename, 'utf-8', function (err, data) { 73 if (err) { 74 return callback(err); 75 } 76 var po = PO.parse(data); 77 callback(null, po); 78 }); 79}; 80 81PO.parse = function (data) { 82 //support both unix and windows newline formats. 83 data = data.replace(/\r\n/g, '\n'); 84 var po = new PO(); 85 var sections = data.split(/\n\n/); 86 var headers = []; 87 //everything until the first 'msgid ""' is considered header 88 while (sections[0] && (headers.length === 0 || headers[headers.length - 1].indexOf('msgid ""') < 0)) { 89 if (sections[0].match(/msgid\s+"[^"]/)) { 90 //found first real string, adding a dummy header item 91 headers.push('msgid ""'); 92 } else { 93 headers.push(sections.shift()); 94 } 95 } 96 headers = headers.join('\n'); 97 var lines = sections.join('\n').split(/\n/); 98 99 po.headers = { 100 'Project-Id-Version': '', 101 'Report-Msgid-Bugs-To': '', 102 'POT-Creation-Date': '', 103 'PO-Revision-Date': '', 104 'Last-Translator': '', 105 'Language': '', 106 'Language-Team': '', 107 'Content-Type': '', 108 'Content-Transfer-Encoding': '', 109 'Plural-Forms': '', 110 }; 111 po.headerOrder = []; 112 113 headers.split(/\n/).reduce(function (acc, line) { 114 if (acc.merge) { 115 //join lines, remove last resp. first " 116 line = acc.pop().slice(0, -1) + line.slice(1); 117 delete acc.merge; 118 } 119 if (/^".*"$/.test(line) && !/^".*\\n"$/.test(line)) { 120 acc.merge = true; 121 } 122 acc.push(line); 123 return acc; 124 }, []).forEach(function (header) { 125 if (header.match(/^#\./)) { 126 po.extractedComments.push(header.replace(/^#\.\s*/, '')); 127 } else if (header.match(/^#/)) { 128 po.comments.push(header.replace(/^#\s*/, '')); 129 } else if (header.match(/^"/)) { 130 header = header.trim().replace(/^"/, '').replace(/\\n"$/, ''); 131 var p = header.split(/:/); 132 var name = p.shift().trim(); 133 var value = p.join(':').trim(); 134 po.headers[name] = value; 135 po.headerOrder.push(name); 136 } 137 }); 138 139 var parsedPluralForms = PO.parsePluralForms(po.headers['Plural-Forms']); 140 var nplurals = parsedPluralForms.nplurals; 141 var item = new PO.Item({ nplurals: nplurals }); 142 var context = null; 143 var plural = 0; 144 var obsoleteCount = 0; 145 var noCommentLineCount = 0; 146 147 function finish() { 148 if (item.msgid.length > 0) { 149 if (obsoleteCount >= noCommentLineCount) { 150 item.obsolete = true; 151 } 152 obsoleteCount = 0; 153 noCommentLineCount = 0; 154 po.items.push(item); 155 item = new PO.Item({ nplurals: nplurals }); 156 } 157 } 158 159 function extract(string) {
160 string = trim(string); 161 string = string.replace(/^[^"]*"|"$/g, ''); 162 string = string.replace(/\\([abtnvfr'"\\?]|([0-7]{3})|x([0-9a-fA-F]{2}))/g, function (match, esc, oct, hex) { 163 if (oct) { 164 return String.fromCharCode(parseInt(oct, 8)); 165 } 166 if (hex) { 167 return String.fromCharCode(parseInt(hex, 16)); 168 } 169 switch (esc) { 170 case 'a': 171 return '\x07'; 172 case 'b': 173 return '\b'; 174 case 't': 175 return '\t'; 176 case 'n': 177 return '\n'; 178 case 'v': 179 return '\v'; 180 case 'f': 181 return '\f'; 182 case 'r': 183 return '\r'; 184 default: 185 return esc; 186 } 187 }); 188 return string; 189 } 190 191 while (lines.length > 0) { 192 var line = trim(lines.shift()); 193 var lineObsolete = false; 194 var add = false; 195 196 if (line.match(/^#\~/)) { // Obsolete item 197 //only remove the obsolte comment mark, here 198 //might be, this is a new item, so 199 //only remember, this line is marked obsolete, count after line is parsed 200 line = trim(line.substring(2)); 201 lineObsolete = true; 202 } 203 204 if (line.match(/^#:/)) { // Reference 205 finish(); 206 item.references.push(trim(line.replace(/^#:/, ''))); 207 } else if (line.match(/^#,/)) { // Flags 208 finish(); 209 var flags = trim(line.replace(/^#,/, '')).split(','); 210 for (var i = 0; i < flags.length; i++) { 211 item.flags[flags[i]] = true; 212 } 213 } else if (line.match(/^#($|\s+)/)) { // Translator comment 214 finish(); 215 item.comments.push(trim(line.replace(/^#($|\s+)/, ''))); 216 } else if (line.match(/^#\./)) { // Extracted comment 217 finish(); 218 item.extractedComments.push(trim(line.replace(/^#\./, ''))); 219 } else if (line.match(/^msgid_plural/)) { // Plural form 220 item.msgid_plural = extract(line); 221 context = 'msgid_plural'; 222 noCommentLineCount++; 223 } else if (line.match(/^msgid/)) { // Original 224 finish(); 225 item.msgid = extract(line); 226 context = 'msgid'; 227 noCommentLineCount++; 228 } else if (line.match(/^msgstr/)) { // Translation 229 var m = line.match(/^msgstr\[(\d+)\]/); 230 plural = m && m[1] ? parseInt(m[1]) : 0; 231 item.msgstr[plural] = extract(line); 232 context = 'msgstr'; 233 noCommentLineCount++; 234 } else if (line.match(/^msgctxt/)) { // Context 235 finish(); 236 item.msgctxt = extract(line); 237 context = 'msgctxt'; 238 noCommentLineCount++; 239 } else { // Probably multiline string or blank 240 if (line.length > 0) { 241 noCommentLineCount++; 242 if (context === 'msgstr') { 243 item.msgstr[plural] += extract(line); 244 } else if (context === 'msgid') { 245 item.msgid += extract(line); 246 } else if (context === 'msgid_plural') { 247 item.msgid_plural += extract(line); 248 } else if (context === 'msgctxt') { 249 item.msgctxt += extract(line); 250 } 251 } 252 } 253 254 if (lineObsolete) { 255 // Count obsolete lines for this item 256 obsoleteCount++; 257 } 258 } 259 finish(); 260 261 return po; 262}; 263 264PO.parsePluralForms = function (pluralFormsString) { 265 var results = (pluralFormsString || '') 266 .split(';') 267 .reduce(function (acc, keyValueString) { 268 var trimmedString = keyValueString.trim(); 269 var equalsIndex = trimmedString.indexOf('='); 270 var key = trimmedString.substring(0, equalsIndex).trim(); 271 var value = trimmedString.substring(equalsIndex + 1).trim(); 272 acc[key] = value; 273 return acc; 274 }, {}); 275 return { 276 nplurals: results.nplurals, 277 plural: results.plural 278 }; 279}; 280 281PO.Item = function (options) { 282 var nplurals = options && options.nplurals; 283 284 this.msgid = ''; 285 this.msgctxt = null; 286 this.references = []; 287 this.msgid_plural = null; 288 this.msgstr = []; 289 this.comments = []; // translator comments 290 this.extractedComments = []; 291 this.flags = {}; 292 this.obsolete = false;
293 var npluralsNumber = Number(nplurals); 294 this.nplurals = (isNaN(npluralsNumber)) ? 2 : npluralsNumber; 295}; 296 297PO.Item.prototype.toString = function () { 298 var lines = []; 299 var self = this; 300 301 // reverse what extract(string) method during PO.parse does 302 var _escape = function (string) { 303 // don't unescape \n, since string can never contain it 304 // since split('\n') is called on it 305 string = string.replace(/[\x07\b\t\v\f\r"\\]/g, function (match) { 306 switch (match) { 307 case '\x07': 308 return '\\a'; 309 case '\b': 310 return '\\b'; 311 case '\t': 312 return '\\t'; 313 case '\v': 314 return '\\v'; 315 case '\f': 316 return '\\f'; 317 case '\r': 318 return '\\r'; 319 default: 320 return '\\' + match; 321 } 322 }); 323 return string; 324 }; 325 326 var _process = function (keyword, text, i) { 327 var lines = []; 328 var parts = text.split(/\n/); 329 var index = typeof i !== 'undefined' ? '[' + i + ']' : ''; 330 if (parts.length > 1) { 331 lines.push(keyword + index + ' ""'); 332 parts.forEach(function (part) { 333 lines.push('"' + _escape(part) + '"'); 334 }); 335 } else { 336 lines.push(keyword + index + ' "' + _escape(text) + '"'); 337 } 338 return lines; 339 }; 340 341 //handle \n in single-line texts (can not be handled in _escape) 342 var _processLineBreak = function (keyword, text, index) { 343 var processed = _process(keyword, text, index); 344 for (var i = 1; i < processed.length - 1; i++) { 345 processed[i] = processed[i].slice(0, -1) + '\\n"'; 346 } 347 return processed; 348 }; 349 350 // https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html 351 // says order is translator-comments, extracted-comments, references, flags 352 353 this.comments.forEach(function (c) { 354 lines.push('# ' + c); 355 }); 356 357 this.extractedComments.forEach(function (c) { 358 lines.push('#. ' + c); 359 }); 360 361 this.references.forEach(function (ref) { 362 lines.push('#: ' + ref); 363 }); 364 365 var flags = Object.keys(this.flags).filter(function (flag) { 366 return !!this.flags[flag]; 367 }, this); 368 if (flags.length > 0) { 369 lines.push('#, ' + flags.join(',')); 370 } 371 var mkObsolete = this.obsolete ? '#~ ' : ''; 372 373 ['msgctxt', 'msgid', 'msgid_plural', 'msgstr'].forEach(function (keyword) { 374 var text = self[keyword]; 375 if (text != null) { 376 var hasTranslation = false; 377 if (Array.isArray(text)) { 378 hasTranslation = text.some(function (text) { 379 return text; 380 }); 381 } 382 383 if (Array.isArray(text) && text.length > 1) { 384 text.forEach(function (t, i) { 385 var processed = _processLineBreak(keyword, t, i); 386 lines = lines.concat(mkObsolete + processed.join('\n' + mkObsolete)); 387 }); 388 } else if (self.msgid_plural && keyword === 'msgstr' && !hasTranslation) { 389 for (var pluralIndex = 0; pluralIndex < self.nplurals; pluralIndex++) { 390 lines = lines.concat(mkObsolete + _process(keyword, '', pluralIndex)); 391 } 392 } else { 393 var index = (self.msgid_plural && Array.isArray(text)) ? 394 0 : 395 undefined; 396 text = Array.isArray(text) ? text.join() : text; 397 var processed = _processLineBreak(keyword, text, index); 398 lines = lines.concat(mkObsolete + processed.join('\n' + mkObsolete)); 399 } 400 } 401 }); 402 403 return lines.join('\n'); 404}; 405 406module.exports = PO; 407 408},{"fs":1}]},{},["pofile"]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.