Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(330)

Side by Side Diff: src/extensions/i18n/i18n-utils.js

Issue 23414008: Revert "Snapshot i18n Javascript code" and "Fix mjsunit/debug-script after r16298". (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/extensions/i18n/i18n-extension.cc ('k') | src/extensions/i18n/locale.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 // limitations under the License.
28
29 // ECMAScript 402 API implementation is broken into separate files for
30 // each service. The build system combines them together into one
31 // Intl namespace.
32
33 /**
34 * Adds bound method to the prototype of the given object.
35 */
36 function addBoundMethod(obj, methodName, implementation, length) {
37 function getter() {
38 if (!this || typeof this !== 'object' ||
39 this.__initializedIntlObject === undefined) {
40 throw new TypeError('Method ' + methodName + ' called on a ' +
41 'non-object or on a wrong type of object.');
42 }
43 var internalName = '__bound' + methodName + '__';
44 if (this[internalName] === undefined) {
45 var that = this;
46 var boundMethod;
47 if (length === undefined || length === 2) {
48 boundMethod = function(x, y) {
49 if (%_IsConstructCall()) {
50 throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
51 }
52 return implementation(that, x, y);
53 }
54 } else if (length === 1) {
55 boundMethod = function(x) {
56 if (%_IsConstructCall()) {
57 throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
58 }
59 return implementation(that, x);
60 }
61 } else {
62 boundMethod = function() {
63 if (%_IsConstructCall()) {
64 throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
65 }
66 // DateTimeFormat.format needs to be 0 arg method, but can stil
67 // receive optional dateValue param. If one was provided, pass it
68 // along.
69 if (arguments.length > 0) {
70 return implementation(that, arguments[0]);
71 } else {
72 return implementation(that);
73 }
74 }
75 }
76 %FunctionSetName(boundMethod, internalName);
77 %FunctionRemovePrototype(boundMethod);
78 %SetNativeFlag(boundMethod);
79 this[internalName] = boundMethod;
80 }
81 return this[internalName];
82 }
83
84 %FunctionSetName(getter, methodName);
85 %FunctionRemovePrototype(getter);
86 %SetNativeFlag(getter);
87
88 Object.defineProperty(obj.prototype, methodName, {
89 get: getter,
90 enumerable: false,
91 configurable: true
92 });
93 }
94
95
96 /**
97 * Returns an intersection of locales and service supported locales.
98 * Parameter locales is treated as a priority list.
99 */
100 function supportedLocalesOf(service, locales, options) {
101 if (service.match(SERVICE_RE) === null) {
102 throw new Error('Internal error, wrong service type: ' + service);
103 }
104
105 // Provide defaults if matcher was not specified.
106 if (options === undefined) {
107 options = {};
108 } else {
109 options = toObject(options);
110 }
111
112 var matcher = options.localeMatcher;
113 if (matcher !== undefined) {
114 matcher = String(matcher);
115 if (matcher !== 'lookup' && matcher !== 'best fit') {
116 throw new RangeError('Illegal value for localeMatcher:' + matcher);
117 }
118 } else {
119 matcher = 'best fit';
120 }
121
122 var requestedLocales = initializeLocaleList(locales);
123
124 // Cache these, they don't ever change per service.
125 if (AVAILABLE_LOCALES[service] === undefined) {
126 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
127 }
128
129 // Use either best fit or lookup algorithm to match locales.
130 if (matcher === 'best fit') {
131 return initializeLocaleList(bestFitSupportedLocalesOf(
132 requestedLocales, AVAILABLE_LOCALES[service]));
133 }
134
135 return initializeLocaleList(lookupSupportedLocalesOf(
136 requestedLocales, AVAILABLE_LOCALES[service]));
137 }
138
139
140 /**
141 * Returns the subset of the provided BCP 47 language priority list for which
142 * this service has a matching locale when using the BCP 47 Lookup algorithm.
143 * Locales appear in the same order in the returned list as in the input list.
144 */
145 function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
146 var matchedLocales = [];
147 for (var i = 0; i < requestedLocales.length; ++i) {
148 // Remove -u- extension.
149 var locale = requestedLocales[i].replace(UNICODE_EXTENSION_RE, '');
150 do {
151 if (availableLocales[locale] !== undefined) {
152 // Push requested locale not the resolved one.
153 matchedLocales.push(requestedLocales[i]);
154 break;
155 }
156 // Truncate locale if possible, if not break.
157 var pos = locale.lastIndexOf('-');
158 if (pos === -1) {
159 break;
160 }
161 locale = locale.substring(0, pos);
162 } while (true);
163 }
164
165 return matchedLocales;
166 }
167
168
169 /**
170 * Returns the subset of the provided BCP 47 language priority list for which
171 * this service has a matching locale when using the implementation
172 * dependent algorithm.
173 * Locales appear in the same order in the returned list as in the input list.
174 */
175 function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
176 // TODO(cira): implement better best fit algorithm.
177 return lookupSupportedLocalesOf(requestedLocales, availableLocales);
178 }
179
180
181 /**
182 * Returns a getOption function that extracts property value for given
183 * options object. If property is missing it returns defaultValue. If value
184 * is out of range for that property it throws RangeError.
185 */
186 function getGetOption(options, caller) {
187 if (options === undefined) {
188 throw new Error('Internal ' + caller + ' error. ' +
189 'Default options are missing.');
190 }
191
192 var getOption = function getOption(property, type, values, defaultValue) {
193 if (options[property] !== undefined) {
194 var value = options[property];
195 switch (type) {
196 case 'boolean':
197 value = Boolean(value);
198 break;
199 case 'string':
200 value = String(value);
201 break;
202 case 'number':
203 value = Number(value);
204 break;
205 default:
206 throw new Error('Internal error. Wrong value type.');
207 }
208 if (values !== undefined && values.indexOf(value) === -1) {
209 throw new RangeError('Value ' + value + ' out of range for ' + caller +
210 ' options property ' + property);
211 }
212
213 return value;
214 }
215
216 return defaultValue;
217 }
218
219 return getOption;
220 }
221
222
223 /**
224 * Compares a BCP 47 language priority list requestedLocales against the locales
225 * in availableLocales and determines the best available language to meet the
226 * request. Two algorithms are available to match the locales: the Lookup
227 * algorithm described in RFC 4647 section 3.4, and an implementation dependent
228 * best-fit algorithm. Independent of the locale matching algorithm, options
229 * specified through Unicode locale extension sequences are negotiated
230 * separately, taking the caller's relevant extension keys and locale data as
231 * well as client-provided options into consideration. Returns an object with
232 * a locale property whose value is the language tag of the selected locale,
233 * and properties for each key in relevantExtensionKeys providing the selected
234 * value for that key.
235 */
236 function resolveLocale(service, requestedLocales, options) {
237 requestedLocales = initializeLocaleList(requestedLocales);
238
239 var getOption = getGetOption(options, service);
240 var matcher = getOption('localeMatcher', 'string',
241 ['lookup', 'best fit'], 'best fit');
242 var resolved;
243 if (matcher === 'lookup') {
244 resolved = lookupMatcher(service, requestedLocales);
245 } else {
246 resolved = bestFitMatcher(service, requestedLocales);
247 }
248
249 return resolved;
250 }
251
252
253 /**
254 * Returns best matched supported locale and extension info using basic
255 * lookup algorithm.
256 */
257 function lookupMatcher(service, requestedLocales) {
258 if (service.match(SERVICE_RE) === null) {
259 throw new Error('Internal error, wrong service type: ' + service);
260 }
261
262 // Cache these, they don't ever change per service.
263 if (AVAILABLE_LOCALES[service] === undefined) {
264 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
265 }
266
267 for (var i = 0; i < requestedLocales.length; ++i) {
268 // Remove all extensions.
269 var locale = requestedLocales[i].replace(ANY_EXTENSION_RE, '');
270 do {
271 if (AVAILABLE_LOCALES[service][locale] !== undefined) {
272 // Return the resolved locale and extension.
273 var extensionMatch = requestedLocales[i].match(UNICODE_EXTENSION_RE);
274 var extension = (extensionMatch === null) ? '' : extensionMatch[0];
275 return {'locale': locale, 'extension': extension, 'position': i};
276 }
277 // Truncate locale if possible.
278 var pos = locale.lastIndexOf('-');
279 if (pos === -1) {
280 break;
281 }
282 locale = locale.substring(0, pos);
283 } while (true);
284 }
285
286 // Didn't find a match, return default.
287 if (DEFAULT_ICU_LOCALE === undefined) {
288 DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
289 }
290
291 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
292 }
293
294
295 /**
296 * Returns best matched supported locale and extension info using
297 * implementation dependend algorithm.
298 */
299 function bestFitMatcher(service, requestedLocales) {
300 // TODO(cira): implement better best fit algorithm.
301 return lookupMatcher(service, requestedLocales);
302 }
303
304
305 /**
306 * Parses Unicode extension into key - value map.
307 * Returns empty object if the extension string is invalid.
308 * We are not concerned with the validity of the values at this point.
309 */
310 function parseExtension(extension) {
311 var extensionSplit = extension.split('-');
312
313 // Assume ['', 'u', ...] input, but don't throw.
314 if (extensionSplit.length <= 2 ||
315 (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
316 return {};
317 }
318
319 // Key is {2}alphanum, value is {3,8}alphanum.
320 // Some keys may not have explicit values (booleans).
321 var extensionMap = {};
322 var previousKey = undefined;
323 for (var i = 2; i < extensionSplit.length; ++i) {
324 var length = extensionSplit[i].length;
325 var element = extensionSplit[i];
326 if (length === 2) {
327 extensionMap[element] = undefined;
328 previousKey = element;
329 } else if (length >= 3 && length <=8 && previousKey !== undefined) {
330 extensionMap[previousKey] = element;
331 previousKey = undefined;
332 } else {
333 // There is a value that's too long, or that doesn't have a key.
334 return {};
335 }
336 }
337
338 return extensionMap;
339 }
340
341
342 /**
343 * Converts parameter to an Object if possible.
344 */
345 function toObject(value) {
346 if (value === undefined || value === null) {
347 throw new TypeError('Value cannot be converted to an Object.');
348 }
349
350 return Object(value);
351 }
352
353
354 /**
355 * Populates internalOptions object with boolean key-value pairs
356 * from extensionMap and options.
357 * Returns filtered extension (number and date format constructors use
358 * Unicode extensions for passing parameters to ICU).
359 * It's used for extension-option pairs only, e.g. kn-normalization, but not
360 * for 'sensitivity' since it doesn't have extension equivalent.
361 * Extensions like nu and ca don't have options equivalent, so we place
362 * undefined in the map.property to denote that.
363 */
364 function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
365 var extension = '';
366
367 var updateExtension = function updateExtension(key, value) {
368 return '-' + key + '-' + String(value);
369 }
370
371 var updateProperty = function updateProperty(property, type, value) {
372 if (type === 'boolean' && (typeof value === 'string')) {
373 value = (value === 'true') ? true : false;
374 }
375
376 if (property !== undefined) {
377 defineWEProperty(outOptions, property, value);
378 }
379 }
380
381 for (var key in keyValues) {
382 if (keyValues.hasOwnProperty(key)) {
383 var value = undefined;
384 var map = keyValues[key];
385 if (map.property !== undefined) {
386 // This may return true if user specifies numeric: 'false', since
387 // Boolean('nonempty') === true.
388 value = getOption(map.property, map.type, map.values);
389 }
390 if (value !== undefined) {
391 updateProperty(map.property, map.type, value);
392 extension += updateExtension(key, value);
393 continue;
394 }
395 // User options didn't have it, check Unicode extension.
396 // Here we want to convert strings 'true', 'false' into proper Boolean
397 // values (not a user error).
398 if (extensionMap.hasOwnProperty(key)) {
399 value = extensionMap[key];
400 if (value !== undefined) {
401 updateProperty(map.property, map.type, value);
402 extension += updateExtension(key, value);
403 } else if (map.type === 'boolean') {
404 // Boolean keys are allowed not to have values in Unicode extension.
405 // Those default to true.
406 updateProperty(map.property, map.type, true);
407 extension += updateExtension(key, true);
408 }
409 }
410 }
411 }
412
413 return extension === ''? '' : '-u' + extension;
414 }
415
416
417 /**
418 * Converts all OwnProperties into
419 * configurable: false, writable: false, enumerable: true.
420 */
421 function freezeArray(array) {
422 array.forEach(function(element, index) {
423 Object.defineProperty(array, index, {value: element,
424 configurable: false,
425 writable: false,
426 enumerable: true});
427 });
428
429 Object.defineProperty(array, 'length', {value: array.length,
430 writable: false});
431
432 return array;
433 }
434
435
436 /**
437 * It's sometimes desireable to leave user requested locale instead of ICU
438 * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
439 * one, if that was what user requested).
440 * This function returns user specified tag if its maximized form matches ICU
441 * resolved locale. If not we return ICU result.
442 */
443 function getOptimalLanguageTag(original, resolved) {
444 // Returns Array<Object>, where each object has maximized and base properties.
445 // Maximized: zh -> zh-Hans-CN
446 // Base: zh-CN-u-ca-gregory -> zh-CN
447 // Take care of grandfathered or simple cases.
448 if (original === resolved) {
449 return original;
450 }
451
452 var locales = %GetLanguageTagVariants([original, resolved]);
453 if (locales[0].maximized !== locales[1].maximized) {
454 return resolved;
455 }
456
457 // Preserve extensions of resolved locale, but swap base tags with original.
458 var resolvedBase = new RegExp('^' + locales[1].base);
459 return resolved.replace(resolvedBase, locales[0].base);
460 }
461
462
463 /**
464 * Returns an Object that contains all of supported locales for a given
465 * service.
466 * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
467 * that is supported. This is required by the spec.
468 */
469 function getAvailableLocalesOf(service) {
470 var available = %AvailableLocalesOf(service);
471
472 for (var i in available) {
473 if (available.hasOwnProperty(i)) {
474 var parts = i.match(/^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
475 if (parts !== null) {
476 // Build xx-ZZ. We don't care about the actual value,
477 // as long it's not undefined.
478 available[parts[1] + '-' + parts[3]] = null;
479 }
480 }
481 }
482
483 return available;
484 }
485
486
487 /**
488 * Defines a property and sets writable and enumerable to true.
489 * Configurable is false by default.
490 */
491 function defineWEProperty(object, property, value) {
492 Object.defineProperty(object, property,
493 {value: value, writable: true, enumerable: true});
494 }
495
496
497 /**
498 * Adds property to an object if the value is not undefined.
499 * Sets configurable descriptor to false.
500 */
501 function addWEPropertyIfDefined(object, property, value) {
502 if (value !== undefined) {
503 defineWEProperty(object, property, value);
504 }
505 }
506
507
508 /**
509 * Defines a property and sets writable, enumerable and configurable to true.
510 */
511 function defineWECProperty(object, property, value) {
512 Object.defineProperty(object, property,
513 {value: value,
514 writable: true,
515 enumerable: true,
516 configurable: true});
517 }
518
519
520 /**
521 * Adds property to an object if the value is not undefined.
522 * Sets all descriptors to true.
523 */
524 function addWECPropertyIfDefined(object, property, value) {
525 if (value !== undefined) {
526 defineWECProperty(object, property, value);
527 }
528 }
529
530
531 /**
532 * Returns titlecased word, aMeRricA -> America.
533 */
534 function toTitleCaseWord(word) {
535 return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase();
536 }
OLDNEW
« no previous file with comments | « src/extensions/i18n/i18n-extension.cc ('k') | src/extensions/i18n/locale.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698