root / drupal7 / sites / all / modules / pdf_reader / js / l10n.js @ a84ddea4
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);
|