Projet

Général

Profil

Paste
Télécharger (9,68 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / pdf_reader / js / l10n.js @ 87dbc3bf

1
/*  Copyright (c) 2011-2012 Fabien Cazenave, Mozilla.
2
  *
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
  * of this software and associated documentation files (the "Software"), to
5
  * deal in the Software without restriction, including without limitation the
6
  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
  * sell copies of the Software, and to permit persons to whom the Software is
8
  * furnished to do so, subject to the following conditions:
9
  *
10
  * The above copyright notice and this permission notice shall be included in
11
  * all copies or substantial portions of the Software.
12
  *
13
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
  * IN THE SOFTWARE.
20
  */
21
/*
22
  Additional modifications for PDF.js project:
23
    - Loading resources from <script type='application/l10n'>;
24
    - Disabling language initialization on page loading;
25
    - Add fallback argument to the translateString.
26
*/
27
'use strict';
28

    
29
(function(window) {
30
  var gL10nData = {};
31
  var gTextData = '';
32
  var gLanguage = '';
33

    
34
  // parser
35

    
36
  function evalString(text) {
37
    return text.replace(/\\\\/g, '\\')
38
               .replace(/\\n/g, '\n')
39
               .replace(/\\r/g, '\r')
40
               .replace(/\\t/g, '\t')
41
               .replace(/\\b/g, '\b')
42
               .replace(/\\f/g, '\f')
43
               .replace(/\\{/g, '{')
44
               .replace(/\\}/g, '}')
45
               .replace(/\\"/g, '"')
46
               .replace(/\\'/g, "'");
47
  }
48

    
49
  function parseProperties(text, lang) {
50
    var reBlank = /^\s*|\s*$/;
51
    var reComment = /^\s*#|^\s*$/;
52
    var reSection = /^\s*\[(.*)\]\s*$/;
53
    var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
54

    
55
    // parse the *.properties file into an associative array
56
    var currentLang = '*';
57
    var supportedLang = [];
58
    var skipLang = false;
59
    var data = [];
60
    var match = '';
61
    var entries = text.replace(reBlank, '').split(/[\r\n]+/);
62
    for (var i = 0; i < entries.length; i++) {
63
      var line = entries[i];
64

    
65
      // comment or blank line?
66
      if (reComment.test(line))
67
        continue;
68

    
69
      // section start?
70
      if (reSection.test(line)) {
71
        match = reSection.exec(line);
72
        currentLang = match[1];
73
        skipLang = (currentLang != lang) && (currentLang != '*') &&
74
          (currentLang != lang.substring(0, 2));
75
        continue;
76
      } else if (skipLang) {
77
        continue;
78
      }
79

    
80
      // @import rule?
81
      if (reImport.test(line)) {
82
        match = reImport.exec(line);
83
      }
84

    
85
      // key-value pair
86
      var tmp = line.split('=');
87
      if (tmp.length > 1)
88
        data[tmp[0]] = evalString(tmp[1]);
89
    }
90

    
91
    // find the attribute descriptions, if any
92
    for (var key in data) {
93
      var id, prop, index = key.lastIndexOf('.');
94
      if (index > 0) { // attribute
95
        id = key.substring(0, index);
96
        prop = key.substr(index + 1);
97
      } else { // textContent, could be innerHTML as well
98
        id = key;
99
        prop = 'textContent';
100
      }
101
      if (!gL10nData[id])
102
        gL10nData[id] = {};
103
      gL10nData[id][prop] = data[key];
104
    }
105
  }
106

    
107
  function parse(text, lang) {
108
    gTextData += text;
109
    // we only support *.properties files at the moment
110
    return parseProperties(text, lang);
111
  }
112

    
113
  // load and parse the specified resource file
114
  function loadResource(href, lang, onSuccess, onFailure) {
115
    var xhr = new XMLHttpRequest();
116
    xhr.open('GET', href, true);
117
    xhr.overrideMimeType('text/plain; charset=utf-8');
118
    xhr.onreadystatechange = function() {
119
      if (xhr.readyState == 4) {
120
        if (xhr.status == 200 || xhr.status == 0) {
121
          parse(xhr.responseText, lang);
122
          if (onSuccess)
123
            onSuccess();
124
        } else {
125
          if (onFailure)
126
            onFailure();
127
        }
128
      }
129
    };
130
    xhr.send(null);
131
  }
132

    
133
  // load and parse all resources for the specified locale
134
  function loadLocale(lang, callback) {
135
    clear();
136

    
137
    // check all <link type="application/l10n" href="..." /> nodes
138
    // and load the resource files
139
    var langLinks = document.querySelectorAll('link[type="application/l10n"]');
140
    var langLinksCount = langLinks.length;
141
    var langScripts = document.querySelectorAll('script[type="application/l10n"]');
142
    var langScriptCount = langScripts.length;
143
    var langCount = langLinksCount + langScriptCount;
144

    
145
    // start the callback when all resources are loaded
146
    var onResourceLoaded = null;
147
    var gResourceCount = 0;
148
    onResourceLoaded = function() {
149
      gResourceCount++;
150
      if (gResourceCount >= langCount) {
151
        // execute the [optional] callback
152
        if (callback)
153
          callback();
154
        // fire a 'localized' DOM event
155
        var evtObject = document.createEvent('Event');
156
        evtObject.initEvent('localized', false, false);
157
        evtObject.language = lang;
158
        window.dispatchEvent(evtObject);
159
      }
160
    }
161

    
162
    // load all resource files
163
    function l10nResourceLink(link) {
164
      var href = link.href;
165
      var type = link.type;
166
      this.load = function(lang, callback) {
167
        var applied = lang;
168
        loadResource(href, lang, callback, function() {
169
          console.warn(href + ' not found.');
170
          applied = '';
171
        });
172
        return applied; // return lang if found, an empty string if not found
173
      };
174
    }
175

    
176
    gLanguage = lang;
177
    for (var i = 0; i < langLinksCount; i++) {
178
      var resource = new l10nResourceLink(langLinks[i]);
179
      var rv = resource.load(lang, onResourceLoaded);
180
      if (rv != lang) // lang not found, used default resource instead
181
        gLanguage = '';
182
    }
183
    for (var i = 0; i < langScriptCount; i++) {
184
      var scriptText = langScripts[i].text;
185
      parse(scriptText, lang);
186
      onResourceLoaded();
187
    }
188
  }
189

    
190
  // fetch an l10n object, warn if not found
191
  function getL10nData(key) {
192
    var data = gL10nData[key];
193
    if (!data)
194
      console.warn('[l10n] #' + key + ' missing for [' + gLanguage + ']');
195
    return data;
196
  }
197

    
198
  // replace {{arguments}} with their values
199
  function substArguments(str, args) {
200
    var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/;
201
    var match = reArgs.exec(str);
202
    while (match) {
203
      if (!match || match.length < 2)
204
        return str; // argument key not found
205

    
206
      var arg = match[1];
207
      var sub = '';
208
      if (arg in args) {
209
        sub = args[arg];
210
      } else if (arg in gL10nData) {
211
        sub = gL10nData[arg].textContent;
212
      } else {
213
        console.warn('[l10n] could not find argument {{' + arg + '}}');
214
        return str;
215
      }
216

    
217
      str = str.substring(0, match.index) + sub +
218
            str.substr(match.index + match[0].length);
219
      match = reArgs.exec(str);
220
    }
221
    return str;
222
  }
223

    
224
  // translate a string
225
  function translateString(key, args, fallback) {
226
    var data = getL10nData(key);
227
    if (!data && fallback)
228
      data = {textContent: fallback};
229
    if (!data)
230
      return '{{' + key + '}}';
231
    return substArguments(data.textContent, args);
232
  }
233

    
234
  // translate an HTML element
235
  function translateElement(element) {
236
    if (!element || !element.dataset)
237
      return;
238

    
239
    // get the related l10n object
240
    var key = element.dataset.l10nId;
241
    var data = getL10nData(key);
242
    if (!data)
243
      return;
244

    
245
    // get arguments (if any)
246
    // TODO: more flexible parser?
247
    var args;
248
    if (element.dataset.l10nArgs) try {
249
      args = JSON.parse(element.dataset.l10nArgs);
250
    } catch (e) {
251
      console.warn('[l10n] could not parse arguments for #' + key + '');
252
    }
253

    
254
    // translate element
255
    // TODO: security check?
256
    for (var k in data)
257
      element[k] = substArguments(data[k], args);
258
  }
259

    
260
  // translate an HTML subtree
261
  function translateFragment(element) {
262
    element = element || document.querySelector('html');
263

    
264
    // check all translatable children (= w/ a `data-l10n-id' attribute)
265
    var children = element.querySelectorAll('*[data-l10n-id]');
266
    var elementCount = children.length;
267
    for (var i = 0; i < elementCount; i++)
268
      translateElement(children[i]);
269

    
270
    // translate element itself if necessary
271
    if (element.dataset.l10nId)
272
      translateElement(element);
273
  }
274

    
275
  // clear all l10n data
276
  function clear() {
277
    gL10nData = {};
278
    gTextData = '';
279
    gLanguage = '';
280
  }
281

    
282
  /*
283
  // load the default locale on startup
284
  window.addEventListener('DOMContentLoaded', function() {
285
    var lang = navigator.language;
286
    if (navigator.mozSettings) {
287
      var req = navigator.mozSettings.getLock().get('language.current');
288
      req.onsuccess = function() {
289
        loadLocale(req.result['language.current'] || lang, translateFragment);
290
      };
291
      req.onerror = function() {
292
        loadLocale(lang, translateFragment);
293
      };
294
    } else {
295
      loadLocale(lang, translateFragment);
296
    }
297
  });
298
  */
299

    
300
  // Public API
301
  document.mozL10n = {
302
    // get a localized string
303
    get: translateString,
304

    
305
    // get|set the document language and direction
306
    get language() {
307
      return {
308
        // get|set the document language (ISO-639-1)
309
        get code() { return gLanguage; },
310
        set code(lang) { loadLocale(lang, translateFragment); },
311

    
312
        // get the direction (ltr|rtl) of the current language
313
        get direction() {
314
          // http://www.w3.org/International/questions/qa-scripts
315
          // Arabic, Hebrew, Farsi, Pashto, Urdu
316
          var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
317
          return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
318
        }
319
      };
320
    }
321
  };
322
})(this);