PageSourceSearch

https://chiyodamusic.net/wp-content/plugins/pdf-poster/pdfjs/web/l10n.js

js chiyodamusic.net collected 2026-10-04 03:54:50 UTC 29,253 bytes, 1,033 lines download raw bytes

vendor: 23,087 bytes, lines 1-829
1/**
2 * Copyright (c) 2011-2013 Fabien Cazenave, Mozilla.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22/*
23  Additional modifications for PDF.js project:
24    - Disables language initialization on page loading;
25    - Removes consoleWarn and consoleLog and use console.log/warn directly.
26    - Removes window._ assignment.
27    - Remove compatibility code for OldIE.
28*/
29
30/*jshint browser: true, devel: true, es5: true, globalstrict: true */
31'use strict';
32
33document.webL10n = (function(window, document, undefined) {
34  var gL10nData = {};
35  var gTextData = '';
36  var gTextProp = 'textContent';
37  var gLanguage = '';
38  var gMacros = {};
39  var gReadyState = 'loading';
40
41
42  /**
43   * Synchronously loading l10n resources significantly minimizes flickering
44   * from displaying the app with non-localized strings and then updating the
45   * strings. Although this will block all script execution on this page, we
46   * expect that the l10n resources are available locally on flash-storage.
47   *
48   * As synchronous XHR is generally considered as a bad idea, we're still
49   * loading l10n resources asynchronously -- but we keep this in a setting,
50   * just in case... and applications using this library should hide their
51   * content until the `localized' event happens.
52   */
53
54  var gAsyncResourceLoading = true; // read-only
55
56
57  /**
58   * DOM helpers for the so-called "HTML API".
59   *
60   * These functions are written for modern browsers. For old versions of IE,
61   * they're overridden in the 'startup' section at the end of this file.
62   */
63
64  function getL10nResourceLinks() {
65    return document.querySelectorAll('link[type="application/l10n"]');
66  }
67
68  function getL10nDictionary() {
69    var script = document.querySelector('script[type="application/l10n"]');
70    // TODO: support multiple and external JSON dictionaries
71    return script ? JSON.parse(script.innerHTML) : null;
72  }
73
74  function getTranslatableChildren(element) {
75    return element ? element.querySelectorAll('*[data-l10n-id]') : [];
76  }
77
78  function getL10nAttributes(element) {
79    if (!element)
80      return {};
81
82    var l10nId = element.getAttribute('data-l10n-id');
83    var l10nArgs = element.getAttribute('data-l10n-args');
84    var args = {};
85    if (l10nArgs) {
86      try {
87        args = JSON.parse(l10nArgs);
88      } catch (e) {
89        console.warn('could not parse arguments for #' + l10nId);
90      }
91    }
92    return { id: l10nId, args: args };
93  }
94
95  function fireL10nReadyEvent(lang) {
96    var evtObject = document.createEvent('Event');
97    evtObject.initEvent('localized', true, false);
98    evtObject.language = lang;
99    document.dispatchEvent(evtObject);
100  }
101
102  function xhrLoadText(url, onSuccess, onFailure) {
103    onSuccess = onSuccess || function _onSuccess(data) {};
104    onFailure = onFailure || function _onFailure() {
105      console.warn(url + ' not found.');
106    };
107
108    var xhr = new XMLHttpRequest();
109    xhr.open('GET', url, gAsyncResourceLoading);
110    if (xhr.overrideMimeType) {
111      xhr.overrideMimeType('text/plain; charset=utf-8');
112    }
113    xhr.onreadystatechange = function() {
114      if (xhr.readyState == 4) {
115        if (xhr.status == 200 || xhr.status === 0) {
116          onSuccess(xhr.responseText);
117        } else {
118          onFailure();
119        }
120      }
121    };
122    xhr.onerror = onFailure;
123    xhr.ontimeout = onFailure;
124
125    // in Firefox OS with the app:// protocol, trying to XHR a non-existing
126    // URL will raise an exception here -- hence this ugly try...catch.
127    try {
128      xhr.send(null);
129    } catch (e) {
130      onFailure();
131    }
132  }
133
134
135  /**
136   * l10n resource parser:
137   *  - reads (async XHR) the l10n resource matching `lang';
138   *  - imports linked resources (synchronously) when specified;
139   *  - parses the text data (fills `gL10nData' and `gTextData');
140   *  - triggers success/failure callbacks when done.
141   *
142   * @param {string} href
143   *    URL of the l10n resource to parse.
144   *
145   * @param {string} lang
146   *    locale (language) to parse. Must be a lowercase string.
147   *
148   * @param {Function} successCallback
149   *    triggered when the l10n resource has been successully parsed.
150   *
151   * @param {Function} failureCallback
152   *    triggered when the an error has occured.
153   *
154   * @return {void}
155   *    uses the following global variables: gL10nData, gTextData, gTextProp.
156   */
157
158  function parseResource(href, lang, successCallback, failureCallback) {
159    var baseURL = href.replace(/[^\/]*$/, '') || './';
160
161    // handle escaped characters (backslashes) in a string
162    function evalString(text) {
163      if (text.lastIndexOf('\\') < 0)
164        return text;
165      return text.replace(/\\\\/g, '\\')
166                 .replace(/\\n/g, '\n')
167                 .replace(/\\r/g, '\r')
168                 .replace(/\\t/g, '\t')
169                 .replace(/\\b/g, '\b')
170                 .replace(/\\f/g, '\f')
171                 .replace(/\\{/g, '{')
172                 .replace(/\\}/g, '}')
173                 .replace(/\\"/g, '"')
174                 .replace(/\\'/g, "'");
175    }
176
177    // parse *.properties text data into an l10n dictionary
178    // If gAsyncResourceLoading is false, then the callback will be called
179    // synchronously. Otherwise it is called asynchronously.
180    function parseProperties(text, parsedPropertiesCallback) {
181      var dictionary = {};
182
183      // token expressions
184      var reBlank = /^\s*|\s*$/;
185      var reComment = /^\s*#|^\s*$/;
186      var reSection = /^\s*\[(.*)\]\s*$/;
187      var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
188      var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
189
190      // parse the *.properties file into an associative array
191      function parseRawLines(rawText, extendedSyntax, parsedRawLinesCallback) {
192        var entries = rawText.replace(reBlank, '').split(/[\r\n]+/);
193        var currentLang = '*';
194        var genericLang = lang.split('-', 1)[0];
195        var skipLang = false;
196        var match = '';
197
198        function nextEntry() {
199          // Use infinite loop instead of recursion to avoid reaching the
200          // maximum recursion limit for content with many lines.
201          while (true) {
202            if (!entries.length) {
203              parsedRawLinesCallback();
204              return;
205            }
206            var line = entries.shift();
207
208            // comment or blank line?
209            if (reComment.test(line))
210              continue;
211
212            // the extended syntax supports [lang] sections and @import rules
213            if (extendedSyntax) {
214              match = reSection.exec(line);
215              if (match) { // section start?
216                // RFC 4646, section 4.4, "All comparisons MUST be performed
217                // in a case-insensitive manner."
218
219                currentLang = match[1].toLowerCase();
220                skipLang = (currentLang !== '*') &&
221                    (currentLang !== lang) && (currentLang !== genericLang);
222                continue;
223              } else if (skipLang) {
224                continue;
225              }
226              match = reImport.exec(line);
227              if (match) { // @import rule?
228                loadImport(baseURL + match[1], nextEntry);
229                return;
230              }
231            }
232
233            // key-value pair
234            var tmp = line.match(reSplit);
235            if (tmp && tmp.length == 3) {
236              dictionary[tmp[1]] = evalString(tmp[2]);
237            }
238          }
239        }
240        nextEntry();
241      }
242
243      // import another *.properties file
244      function loadImport(url, callback) {
245        xhrLoadText(url, function(content) {
246          parseRawLines(content, false, callback); // don't allow recursive imports
247        }, null);
248      }
249
250      // fill the dictionary
251      parseRawLines(text, true, function() {
252        parsedPropertiesCallback(dictionary);
253      });
254    }
255
256    // load and parse l10n data (warning: global variables are used here)
257    xhrLoadText(href, function(response) {
258      gTextData += response; // mostly for debug
259
260      // parse *.properties text data into an l10n dictionary
261      parseProperties(response, function(data) {
262
263        // find attribute descriptions, if any
264        for (var key in data) {
265          var id, prop, index = key.lastIndexOf('.');
266          if (index > 0) { // an attribute has been specified
267            id = key.substring(0, index);
268            prop = key.substr(index + 1);
269          } else { // no attribute: assuming text content by default
270            id = key;
271            prop = gTextProp;
272          }
273          if (!gL10nData[id]) {
274            gL10nData[id] = {};
275          }
276          gL10nData[id][prop] = data[key];
277        }
278
279        // trigger callback
280        if (successCallback) {
281          successCallback();
282        }
283      });
284    }, failureCallback);
285  }
286
287  // load and parse all resources for the specified locale
288  function loadLocale(lang, callback) {
289    // RFC 4646, section 2.1 states that language tags have to be treated as
290    // case-insensitive. Convert to lowercase for case-insensitive comparisons.
291    if (lang) {
292      lang = lang.toLowerCase();
293    }
294
295    callback = callback || function _callback() {};
296
297    clear();
298    gLanguage = lang;
299
300    // check all <link type="application/l10n" href="..." /> nodes
301    // and load the resource files
302    var langLinks = getL10nResourceLinks();
303    var langCount = langLinks.length;
304    if (langCount === 0) {
305      // we might have a pre-compiled dictionary instead
306      var dict = getL10nDictionary();
307      if (dict && dict.locales && dict.default_locale) {
308        console.log('using the embedded JSON directory, early way out');
309        gL10nData = dict.locales[lang];
310        if (!gL10nData) {
311          var defaultLocale = dict.default_locale.toLowerCase();
312          for (var anyCaseLang in dict.locales) {
313            anyCaseLang = anyCaseLang.toLowerCase();
314            if (anyCaseLang === lang) {
315              gL10nData = dict.locales[lang];
316              break;
317            } else if (anyCaseLang === defaultLocale) {
318              gL10nData = dict.locales[defaultLocale];
319            }
320          }
321        }
322        callback();
323      } else {
324        console.log('no resource to load, early way out');
325      }
326      // early way out
327      fireL10nReadyEvent(lang);
328      gReadyState = 'complete';
329      return;
330    }
331
332    // start the callback when all resources are loaded
333    var onResourceLoaded = null;
334    var gResourceCount = 0;
335    onResourceLoaded = function() {
336      gResourceCount++;
337      if (gResourceCount >= langCount) {
338        callback();
339        fireL10nReadyEvent(lang);
340        gReadyState = 'complete';
341      }
342    };
343
344    // load all resource files
345    function L10nResourceLink(link) {
346      var href = link.href;
347      // Note: If |gAsyncResourceLoading| is false, then the following callbacks
348      // are synchronously called.
349      this.load = function(lang, callback) {
350        parseResource(href, lang, callback, function() {
351          console.warn(href + ' not found.');
352          // lang not found, used default resource instead
353          console.warn('"' + lang + '" resource not found');
354          gLanguage = '';
355          // Resource not loaded, but we still need to call the callback.
356          callback();
357        });
358      };
359    }
360
361    for (var i = 0; i < langCount; i++) {
362      var resource = new L10nResourceLink(langLinks[i]);
363      resource.load(lang, onResourceLoaded);
364    }
365  }
366
367  // clear all l10n data
368  function clear() {
369    gL10nData = {};
370    gTextData = '';
371    gLanguage = '';
372    // TODO: clear all non predefined macros.
373    // There's no such macro /yet/ but we're planning to have some...
374  }
375
376
377  /**
378   * Get rules for plural forms (shared with JetPack), see:
379   * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
380   * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p
381   *
382   * @param {string} lang
383   *    locale (language) used.
384   *
385   * @return {Function}
386   *    returns a function that gives the plural form name for a given integer:
387   *       var fun = getPluralRules('en');
388   *       fun(1)    -> 'one'
389   *       fun(0)    -> 'other'
390   *       fun(1000) -> 'other'.
391   */
392
393  function getPluralRules(lang) {
394    var locales2rules = {
395      'af': 3,
396      'ak': 4,
397      'am': 4,
398      'ar': 1,
399      'asa': 3,
400      'az': 0,
401      'be': 11,
402      'bem': 3,
403      'bez': 3,
404      'bg': 3,
405      'bh': 4,
406      'bm': 0,
407      'bn': 3,
408      'bo': 0,
409      'br': 20,
410      'brx': 3,
411      'bs': 11,
412      'ca': 3,
413      'cgg': 3,
414      'chr': 3,
415      'cs': 12,
416      'cy': 17,
417      'da': 3,
418      'de': 3,
419      'dv': 3,
420      'dz': 0,
421      'ee': 3,
422      'el': 3,
423      'en': 3,
424      'eo': 3,
425      'es': 3,
426      'et': 3,
427      'eu': 3,
428      'fa': 0,
429      'ff': 5,
430      'fi': 3,
431      'fil': 4,
432      'fo': 3,
433      'fr': 5,
434      'fur': 3,
435      'fy': 3,
436      'ga': 8,
437      'gd': 24,
438      'gl': 3,
439      'gsw': 3,
440      'gu': 3,
441      'guw': 4,
442      'gv': 23,
443      'ha': 3,
444      'haw': 3,
445      'he': 2,
446      'hi': 4,
447      'hr': 11,
448      'hu': 0,
449      'id': 0,
450      'ig': 0,
451      'ii': 0,
452      'is': 3,
453      'it': 3,
454      'iu': 7,
455      'ja': 0,
456      'jmc': 3,
457      'jv': 0,
458      'ka': 0,
459      'kab': 5,
460      'kaj': 3,
461      'kcg': 3,
462      'kde': 0,
463      'kea': 0,
464      'kk': 3,
465      'kl': 3,
466      'km': 0,
467      'kn': 0,
468      'ko': 0,
469      'ksb': 3,
470      'ksh': 21,
471      'ku': 3,
472      'kw': 7,
473      'lag': 18,
474      'lb': 3,
475      'lg': 3,
476      'ln': 4,
477      'lo': 0,
478      'lt': 10,
479      'lv': 6,
480      'mas': 3,
481      'mg': 4,
482      'mk': 16,
483      'ml': 3,
484      'mn': 3,
485      'mo': 9,
486      'mr': 3,
487      'ms': 0,
488      'mt': 15,
489      'my': 0,
490      'nah': 3,
491      'naq': 7,
492      'nb': 3,
493      'nd': 3,
494      'ne': 3,
495      'nl': 3,
496      'nn': 3,
497      'no': 3,
498      'nr': 3,
499      'nso': 4,
500      'ny': 3,
501      'nyn': 3,
502      'om': 3,
503      'or': 3,
504      'pa': 3,
505      'pap': 3,
506      'pl': 13,
507      'ps': 3,
508      'pt': 3,
509      'rm': 3,
510      'ro': 9,
511      'rof': 3,
512      'ru': 11,
513      'rwk': 3,
514      'sah': 0,
515      'saq': 3,
516      'se': 7,
517      'seh': 3,
518      'ses': 0,
519      'sg': 0,
520      'sh': 11,
521      'shi': 19,
522      'sk': 12,
523      'sl': 14,
524      'sma': 7,
525      'smi': 7,
526      'smj': 7,
527      'smn': 7,
528      'sms': 7,
529      'sn': 3,
530      'so': 3,
531      'sq': 3,
532      'sr': 11,
533      'ss': 3,
534      'ssy': 3,
535      'st': 3,
536      'sv': 3,
537      'sw': 3,
538      'syr': 3,
539      'ta': 3,
540      'te': 3,
541      'teo': 3,
542      'th': 0,
543      'ti': 4,
544      'tig': 3,
545      'tk': 3,
546      'tl': 4,
547      'tn': 3,
548      'to': 0,
549      'tr': 0,
550      'ts': 3,
551      'tzm': 22,
552      'uk': 11,
553      'ur': 3,
554      've': 3,
555      'vi': 0,
556      'vun': 3,
557      'wa': 4,
558      'wae': 3,
559      'wo': 0,
560      'xh': 3,
561      'xog': 3,
562      'yo': 0,
563      'zh': 0,
564      'zu': 3
565    };
566
567    // utility functions for plural rules methods
568    function isIn(n, list) {
569      return list.indexOf(n) !== -1;
570    }
571    function isBetween(n, start, end) {
572      return start <= n && n <= end;
573    }
574
575    // list of all plural rules methods:
576    // map an integer to the plural form name to use
577    var pluralRules = {
578      '0': function(n) {
579        return 'other';
580      },
581      '1': function(n) {
582        if ((isBetween((n % 100), 3, 10)))
583          return 'few';
584        if (n === 0)
585          return 'zero';
586        if ((isBetween((n % 100), 11, 99)))
587          return 'many';
588        if (n == 2)
589          return 'two';
590        if (n == 1)
591          return 'one';
592        return 'other';
593      },
594      '2': function(n) {
595        if (n !== 0 && (n % 10) === 0)
596          return 'many';
597        if (n == 2)
598          return 'two';
599        if (n == 1)
600          return 'one';
601        return 'other';
602      },
603      '3': function(n) {
604        if (n == 1)
605          return 'one';
606        return 'other';
607      },
608      '4': function(n) {
609        if ((isBetween(n, 0, 1)))
610          return 'one';
611        return 'other';
612      },
613      '5': function(n) {
614        if ((isBetween(n, 0, 2)) && n != 2)
615          return 'one';
616        return 'other';
617      },
618      '6': function(n) {
619        if (n === 0)
620          return 'zero';
621        if ((n % 10) == 1 && (n % 100) != 11)
622          return 'one';
623        return 'other';
624      },
625      '7': function(n) {
626        if (n == 2)
627          return 'two';
628        if (n == 1)
629          return 'one';
630        return 'other';
631      },
632      '8': function(n) {
633        if ((isBetween(n, 3, 6)))
634          return 'few';
635        if ((isBetween(n, 7, 10)))
636          return 'many';
637        if (n == 2)
638          return 'two';
639        if (n == 1)
640          return 'one';
641        return 'other';
642      },
643      '9': function(n) {
644        if (n === 0 || n != 1 && (isBetween((n % 100), 1, 19)))
645          return 'few';
646        if (n == 1)
647          return 'one';
648        return 'other';
649      },
650      '10': function(n) {
651        if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19)))
652          return 'few';
653        if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19)))
654          return 'one';
655        return 'other';
656      },
657      '11': function(n) {
658        if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
659          return 'few';
660        if ((n % 10) === 0 ||
661            (isBetween((n % 10), 5, 9)) ||
662            (isBetween((n % 100), 11, 14)))
663          return 'many';
664        if ((n % 10) == 1 && (n % 100) != 11)
665          return 'one';
666        return 'other';
667      },
668      '12': function(n) {
669        if ((isBetween(n, 2, 4)))
670          return 'few';
671        if (n == 1)
672          return 'one';
673        return 'other';
674      },
675      '13': function(n) {
676        if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
677          return 'few';
678        if (n != 1 && (isBetween((n % 10), 0, 1)) ||
679            (isBetween((n % 10), 5, 9)) ||
680            (isBetween((n % 100), 12, 14)))
681          return 'many';
682        if (n == 1)
683          return 'one';
684        return 'other';
685      },
686      '14': function(n) {
687        if ((isBetween((n % 100), 3, 4)))
688          return 'few';
689        if ((n % 100) == 2)
690          return 'two';
691        if ((n % 100) == 1)
692          return 'one';
693        return 'other';
694      },
695      '15': function(n) {
696        if (n === 0 || (isBetween((n % 100), 2, 10)))
697          return 'few';
698        if ((isBetween((n % 100), 11, 19)))
699          return 'many';
700        if (n == 1)
701          return 'one';
702        return 'other';
703      },
704      '16': function(n) {
705        if ((n % 10) == 1 && n != 11)
706          return 'one';
707        return 'other';
708      },
709      '17': function(n) {
710        if (n == 3)
711          return 'few';
712        if (n === 0)
713          return 'zero';
714        if (n == 6)
715          return 'many';
716        if (n == 2)
717          return 'two';
718        if (n == 1)
719          return 'one';
720        return 'other';
721      },
722      '18': function(n) {
723        if (n === 0)
724          return 'zero';
725        if ((isBetween(n, 0, 2)) && n !== 0 && n != 2)
726          return 'one';
727        return 'other';
728      },
729      '19': function(n) {
730        if ((isBetween(n, 2, 10)))
731          return 'few';
732        if ((isBetween(n, 0, 1)))
733          return 'one';
734        return 'other';
735      },
736      '20': function(n) {
737        if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(
738            isBetween((n % 100), 10, 19) ||
739            isBetween((n % 100), 70, 79) ||
740            isBetween((n % 100), 90, 99)
741            ))
742          return 'few';
743        if ((n % 1000000) === 0 && n !== 0)
744          return 'many';
745        if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92]))
746          return 'two';
747        if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91]))
748          return 'one';
749        return 'other';
750      },
751      '21': function(n) {
752        if (n === 0)
753          return 'zero';
754        if (n == 1)
755          return 'one';
756        return 'other';
757      },
758      '22': function(n) {
759        if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99)))
760          return 'one';
761        return 'other';
762      },
763      '23': function(n) {
764        if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0)
765          return 'one';
766        return 'other';
767      },
768      '24': function(n) {
769        if ((isBetween(n, 3, 10) || isBetween(n, 13, 19)))
770          return 'few';
771        if (isIn(n, [2, 12]))
772          return 'two';
773        if (isIn(n, [1, 11]))
774          return 'one';
775        return 'other';
776      }
777    };
778
779    // return a function that gives the plural form name for a given integer
780    var index = locales2rules[lang.replace(/-.*$/, '')];
781    if (!(index in pluralRules)) {
782      console.warn('plural form unknown for [' + lang + ']');
783      return function() { return 'other'; };
784    }
785    return pluralRules[index];
786  }
787
788  // pre-defined 'plural' macro
789  gMacros.plural = function(str, param, key, prop) {
790    var n = parseFloat(param);
791    if (isNaN(n))
792      return str;
793
794    // TODO: support other properties (l20n still doesn't...)
795    if (prop != gTextProp)
796      return str;
797
798    // initialize _pluralRules
799    if (!gMacros._pluralRules) {
800      gMacros._pluralRules = getPluralRules(gLanguage);
801    }
802    var index = '[' + gMacros._pluralRules(n) + ']';
803
804    // try to find a [zero|one|two] key if it's defined
805    if (n === 0 && (key + '[zero]') in gL10nData) {
806      str = gL10nData[key + '[zero]'][prop];
807    } else if (n == 1 && (key + '[one]') in gL10nData) {
808      str = gL10nData[key + '[one]'][prop];
809    } else if (n == 2 && (key + '[two]') in gL10nData) {
810      str = gL10nData[key + '[two]'][prop];
811    } else if ((key + index) in gL10nData) {
812      str = gL10nData[key + index][prop];
813    } else if ((key + '[other]') in gL10nData) {
814      str = gL10nData[key + '[other]'][prop];
815    }
816
817    return str;
818  };
819
820
821  /**
822   * l10n dictionary functions
823   */
824
825  // fetch an l10n object, warn if not found, apply `args' if possible
826  function getL10nData(key, args, fallback) {
827    var data = gL10nData[key];
828    if (!data) {
829      
vendor: 6,166 bytes, lines 829-1033
829console.warn('#' + key + ' is undefined.');
830      if (!fallback) {
831        return null;
832      }
833      data = fallback;
834    }
835
836    /** This is where l10n expressions should be processed.
837      * The plan is to support C-style expressions from the l20n project;
838      * until then, only two kinds of simple expressions are supported:
839      *   {[ index ]} and {{ arguments }}.
840      */
841    var rv = {};
842    for (var prop in data) {
843      var str = data[prop];
844      str = substIndexes(str, args, key, prop);
845      str = substArguments(str, args, key);
846      rv[prop] = str;
847    }
848    return rv;
849  }
850
851  // replace {[macros]} with their values
852  function substIndexes(str, args, key, prop) {
853    var reIndex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)\s*\]\}/;
854    var reMatch = reIndex.exec(str);
855    if (!reMatch || !reMatch.length)
856      return str;
857
858    // an index/macro has been found
859    // Note: at the moment, only one parameter is supported
860    var macroName = reMatch[1];
861    var paramName = reMatch[2];
862    var param;
863    if (args && paramName in args) {
864      param = args[paramName];
865    } else if (paramName in gL10nData) {
866      param = gL10nData[paramName];
867    }
868
869    // there's no macro parser yet: it has to be defined in gMacros
870    if (macroName in gMacros) {
871      var macro = gMacros[macroName];
872      str = macro(str, param, key, prop);
873    }
874    return str;
875  }
876
877  // replace {{arguments}} with their values
878  function substArguments(str, args, key) {
879    var reArgs = /\{\{\s*(.+?)\s*\}\}/g;
880    return str.replace(reArgs, function(matched_text, arg) {
881      if (args && arg in args) {
882        return args[arg];
883      }
884      if (arg in gL10nData) {
885        return gL10nData[arg];
886      }
887      console.log('argument {{' + arg + '}} for #' + key + ' is undefined.');
888      return matched_text;
889    });
890  }
891
892  // translate an HTML element
893  function translateElement(element) {
894    var l10n = getL10nAttributes(element);
895    if (!l10n.id)
896      return;
897
898    // get the related l10n object
899    var data = getL10nData(l10n.id, l10n.args);
900    if (!data) {
901      console.warn('#' + l10n.id + ' is undefined.');
902      return;
903    }
904
905    // translate element (TODO: security checks?)
906    if (data[gTextProp]) { // XXX
907      if (getChildElementCount(element) === 0) {
908        element[gTextProp] = data[gTextProp];
909      } else {
910        // this element has element children: replace the content of the first
911        // (non-empty) child textNode and clear other child textNodes
912        var children = element.childNodes;
913        var found = false;
914        for (var i = 0, l = children.length; i < l; i++) {
915          if (children[i].nodeType === 3 && /\S/.test(children[i].nodeValue)) {
916            if (found) {
917              children[i].nodeValue = '';
918            } else {
919              children[i].nodeValue = data[gTextProp];
920              found = true;
921            }
922          }
923        }
924        // if no (non-empty) textNode is found, insert a textNode before the
925        // first element child.
926        if (!found) {
927          var textNode = document.createTextNode(data[gTextProp]);
928          element.insertBefore(textNode, element.firstChild);
929        }
930      }
931      delete data[gTextProp];
932    }
933
934    for (var k in data) {
935      element[k] = data[k];
936    }
937  }
938
939  // webkit browsers don't currently support 'children' on SVG elements...
940  function getChildElementCount(element) {
941    if (element.children) {
942      return element.children.length;
943    }
944    if (typeof element.childElementCount !== 'undefined') {
945      return element.childElementCount;
946    }
947    var count = 0;
948    for (var i = 0; i < element.childNodes.length; i++) {
949      count += element.nodeType === 1 ? 1 : 0;
950    }
951    return count;
952  }
953
954  // translate an HTML subtree
955  function translateFragment(element) {
956    element = element || document.documentElement;
957
958    // check all translatable children (= w/ a `data-l10n-id' attribute)
959    var children = getTranslatableChildren(element);
960    var elementCount = children.length;
961    for (var i = 0; i < elementCount; i++) {
962      translateElement(children[i]);
963    }
964
965    // translate element itself if necessary
966    translateElement(element);
967  }
968
969  return {
970    // get a localized string
971    get: function(key, args, fallbackString) {
972      var index = key.lastIndexOf('.');
973      var prop = gTextProp;
974      if (index > 0) { // An attribute has been specified
975        prop = key.substr(index + 1);
976        key = key.substring(0, index);
977      }
978      var fallback;
979      if (fallbackString) {
980        fallback = {};
981        fallback[prop] = fallbackString;
982      }
983      var data = getL10nData(key, args, fallback);
984      if (data && prop in data) {
985        return data[prop];
986      }
987      return '{{' + key + '}}';
988    },
989
990    // debug
991    getData: function() { return gL10nData; },
992    getText: function() { return gTextData; },
993
994    // get|set the document language
995    getLanguage: function() { return gLanguage; },
996    setLanguage: function(lang, callback) {
997      loadLocale(lang, function() {
998        if (callback)
999          callback();
1000        translateFragment();
1001      });
1002    },
1003
1004    // get the direction (ltr|rtl) of the current language
1005    getDirection: function() {
1006      // http://www.w3.org/International/questions/qa-scripts
1007      // Arabic, Hebrew, Farsi, Pashto, Urdu
1008      var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
1009      var shortCode = gLanguage.split('-', 1)[0];
1010      return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr';
1011    },
1012
1013    // translate an element or document fragment
1014    translate: translateFragment,
1015
1016    // this can be used to prevent race conditions
1017    getReadyState: function() { return gReadyState; },
1018    ready: function(callback) {
1019      if (!callback) {
1020        return;
1021      } else if (gReadyState == 'complete' || gReadyState == 'interactive') {
1022        window.setTimeout(function() {
1023          callback();
1024        });
1025      } else if (document.addEventListener) {
1026        document.addEventListener('localized', function once() {
1027          document.removeEventListener('localized', once);
1028          callback();
1029        });
1030      }
1031    }
1032  };
1033}) (window, document);

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.