1 /*
  2  * LocaleInfo.js - Encode locale-specific defaults
  3  *
  4  * Copyright © 2012-2015, 2018, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 // !data localeinfo
 21 
 22 var ilib = require("./ilib.js");
 23 var Utils = require("./Utils.js");
 24 var Locale = require("./Locale.js");
 25 
 26 /**
 27  * @class
 28  * Create a new locale info instance. Locale info instances give information about
 29  * the default settings for a particular locale. These settings may be overridden
 30  * by various parts of the code, and should be used as a fall-back setting of last
 31  * resort. <p>
 32  *
 33  * The optional options object holds extra parameters if they are necessary. The
 34  * current list of supported options are:
 35  *
 36  * <ul>
 37  * <li><i>onLoad</i> - a callback function to call when the locale info object is fully
 38  * loaded. When the onLoad option is given, the localeinfo object will attempt to
 39  * load any missing locale data using the ilib loader callback.
 40  * When the constructor is done (even if the data is already preassembled), the
 41  * onLoad function is called with the current instance as a parameter, so this
 42  * callback can be used with preassembled or dynamic loading or a mix of the two.
 43  *
 44  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
 45  * asynchronously. If this option is given as "false", then the "onLoad"
 46  * callback must be given, as the instance returned from this constructor will
 47  * not be usable for a while.
 48  *
 49  * <li><i>loadParams</i> - an object containing parameters to pass to the
 50  * loader callback function when locale data is missing. The parameters are not
 51  * interpretted or modified in any way. They are simply passed along. The object
 52  * may contain any property/value pairs as long as the calling code is in
 53  * agreement with the loader callback function as to what those parameters mean.
 54  * </ul>
 55  *
 56  * If this copy of ilib is pre-assembled and all the data is already available,
 57  * or if the data was already previously loaded, then this constructor will call
 58  * the onLoad callback immediately when the initialization is done.
 59  * If the onLoad option is not given, this class will only attempt to load any
 60  * missing locale data synchronously.
 61  *
 62  *
 63  * @constructor
 64  * @see {ilib.setLoaderCallback} for information about registering a loader callback
 65  * function
 66  * @param {Locale|string=} locale the locale for which the info is sought, or undefined for
 67  * @param {Object=} options the locale for which the info is sought, or undefined for
 68  * the current locale
 69  */
 70 var LocaleInfo = function(locale, options) {
 71     var sync = true,
 72         loadParams = undefined;
 73 
 74     /**
 75       @private
 76       @type {{
 77         calendar:string,
 78         clock:string,
 79         currency:string,
 80         delimiter: {quotationStart:string,quotationEnd:string,alternateQuotationStart:string,alternateQuotationEnd:string},
 81         firstDayOfWeek:number,
 82         meridiems:string,
 83         numfmt:{
 84             currencyFormats:{common:string,commonNegative:string,iso:string,isoNegative:string},
 85             decimalChar:string,
 86             exponential:string,
 87             groupChar:string,
 88             negativenumFmt:string,
 89             negativepctFmt:string,
 90             pctChar:string,
 91             pctFmt:string,
 92             prigroupSize:number,
 93             roundingMode:string,
 94             script:string,
 95             secgroupSize:number,
 96             useNative:boolean
 97         },
 98         timezone:string,
 99         units:string,
100         weekendEnd:number,
101         weekendStart:number,
102         paperSizes:{regular:string}
103       }}
104     */
105     this.info = LocaleInfo.defaultInfo;
106 
107     switch (typeof(locale)) {
108         case "string":
109             this.locale = new Locale(locale);
110             break;
111         default:
112         case "undefined":
113             this.locale = new Locale();
114             break;
115         case "object":
116             this.locale = locale;
117             break;
118     }
119 
120     if (options) {
121         if (typeof(options.sync) !== 'undefined') {
122             sync = !!options.sync;
123         }
124 
125         if (typeof(options.loadParams) !== 'undefined') {
126             loadParams = options.loadParams;
127         }
128     }
129 
130     Utils.loadData({
131         object: "LocaleInfo",
132         locale: this.locale,
133         name: "localeinfo.json",
134         sync: sync,
135         loadParams: loadParams,
136         callback: ilib.bind(this, function (info) {
137             this.info = info || LocaleInfo.defaultInfo;
138             if (options && typeof(options.onLoad) === 'function') {
139                 options.onLoad(this);
140             }
141         })
142     });
143 };
144 
145 LocaleInfo.defaultInfo = ilib.data.localeinfo;
146 LocaleInfo.defaultInfo = LocaleInfo.defaultInfo || {
147     "calendar": "gregorian",
148     "clock": "24",
149     "currency": "USD",
150     "delimiter": {
151         "quotationStart": "“",
152         "quotationEnd": "”",
153         "alternateQuotationStart": "‘",
154         "alternateQuotationEnd": "’"
155     },
156     "firstDayOfWeek": 1,
157     "meridiems": "gregorian",
158     "numfmt": {
159         "script": "Latn",
160         "decimalChar": ".",
161         "groupChar": ",",
162         "pctChar": "%",
163         "exponential": "E",
164         "prigroupSize": 3,
165         "currencyFormats": {
166             "common": "{s} {n}",
167             "commonNegative": "-{s} {n}",
168             "iso": "{s} {n}",
169             "isoNegative": "({s} {n})"
170         },
171         "negativenumFmt": "-{n}",
172         "pctFmt": "{n}%",
173         "negativepctFmt": "-{n}%",
174         "roundingMode": "halfdown",
175         "secGroupSize": null,
176         "useNative": false
177     },
178     "paperSizes": {
179         "regular": "A4"
180     },
181     "timezone": "Etc/UTC",
182     "units": "metric",
183     "weekendEnd": 0,
184     "weekendStart": 6
185 };
186 
187 LocaleInfo.prototype = {
188     /**
189      * Return the name of the locale's language in English.
190      * @returns {string} the name of the locale's language in English
191      */
192     getLanguageName: function () {
193         return this.info["language.name"];
194     },
195 
196     /**
197      * Return the name of the locale's region in English. If the locale
198      * has no region, this returns undefined.
199      *
200      * @returns {string|undefined} the name of the locale's region in English
201      */
202     getRegionName: function () {
203         return this.info["region.name"];
204     },
205 
206     /**
207      * Return whether this locale commonly uses the 12- or the 24-hour clock.
208      *
209      * @returns {string} "12" if the locale commonly uses a 12-hour clock, or "24"
210      * if the locale commonly uses a 24-hour clock.
211      */
212     getClock: function() {
213         return this.info.clock;
214     },
215 
216     /**
217      * Return the locale that this info object was created with.
218      * @returns {Locale} The locale spec of the locale used to construct this info instance
219      */
220     getLocale: function () {
221         return this.locale;
222     },
223 
224     /**
225      * Return the name of the measuring system that is commonly used in the given locale.
226      * Valid values are "uscustomary", "imperial", and "metric".
227      *
228      * @returns {string} The name of the measuring system commonly used in the locale
229      */
230     getUnits: function () {
231         return this.info.units;
232     },
233 
234     /**
235      * Return the name of the calendar that is commonly used in the given locale.
236      *
237      * @returns {string} The name of the calendar commonly used in the locale
238      */
239     getCalendar: function () {
240         return this.info.calendar;
241     },
242 
243     /**
244      * Return the day of week that starts weeks in the current locale. Days are still
245      * numbered the standard way with 0 for Sunday through 6 for Saturday, but calendars
246      * should be displayed and weeks calculated with the day of week returned from this
247      * function as the first day of the week.
248      *
249      * @returns {number} the day of the week that starts weeks in the current locale.
250      */
251     getFirstDayOfWeek: function () {
252         return this.info.firstDayOfWeek;
253     },
254 
255     /**
256      * Return the day of week that starts weekend in the current locale. Days are still
257      * numbered the standard way with 0 for Sunday through 6 for Saturday.
258      *
259      * @returns {number} the day of the week that starts weeks in the current locale.
260      */
261     getWeekEndStart: function () {
262         return this.info.weekendStart;
263     },
264 
265     /**
266      * Return the day of week that starts weekend in the current locale. Days are still
267      * numbered the standard way with 0 for Sunday through 6 for Saturday.
268      *
269      * @returns {number} the day of the week that starts weeks in the current locale.
270      */
271     getWeekEndEnd: function () {
272         return this.info.weekendEnd;
273     },
274 
275     /**
276      * Return the default time zone for this locale. Many locales span across multiple
277      * time zones. In this case, the time zone with the largest population is chosen
278      * to represent the locale. This is obviously not that accurate, but then again,
279      * this method's return value should only be used as a default anyways.
280      * @returns {string} the default time zone for this locale.
281      */
282     getTimeZone: function () {
283         return this.info.timezone;
284     },
285 
286     /**
287      * Return the decimal separator for formatted numbers in this locale.
288      * @returns {string} the decimal separator char
289      */
290     getDecimalSeparator: function () {
291         return this.info.numfmt.decimalChar;
292     },
293 
294     /**
295      * Return the decimal separator for formatted numbers in this locale for native script.
296      * @returns {string} the decimal separator char
297      */
298     getNativeDecimalSeparator: function () {
299         return (this.info.native_numfmt && this.info.native_numfmt.decimalChar) || this.info.numfmt.decimalChar;
300     },
301 
302     /**
303      * Return the separator character used to separate groups of digits on the
304      * integer side of the decimal character.
305      * @returns {string} the grouping separator char
306      */
307     getGroupingSeparator: function () {
308         return this.info.numfmt.groupChar;
309     },
310 
311     /**
312      * Return the separator character used to separate groups of digits on the
313      * integer side of the decimal character for the native script if present other than the default script.
314      * @returns {string} the grouping separator char
315      */
316     getNativeGroupingSeparator: function () {
317         return (this.info.native_numfmt && this.info.native_numfmt.groupChar) || this.info.numfmt.groupChar;
318     },
319 
320     /**
321      * Return the minimum number of digits grouped together on the integer side
322      * for the first (primary) group.
323      * In western European cultures, groupings are in 1000s, so the number of digits
324      * is 3.
325      * @returns {number} the number of digits in a primary grouping, or 0 for no grouping
326      */
327     getPrimaryGroupingDigits: function () {
328         return (typeof(this.info.numfmt.prigroupSize) !== 'undefined' && this.info.numfmt.prigroupSize) || 0;
329     },
330 
331     /**
332      * Return the minimum number of digits grouped together on the integer side
333      * for the second or more (secondary) group.<p>
334      *
335      * In western European cultures, all groupings are by 1000s, so the secondary
336      * size should be 0 because there is no secondary size. In general, if this
337      * method returns 0, then all groupings are of the primary size.<p>
338      *
339      * For some other cultures, the first grouping (primary)
340      * is 3 and any subsequent groupings (secondary) are two. So, 100000 would be
341      * written as: "1,00,000".
342      *
343      * @returns {number} the number of digits in a secondary grouping, or 0 for no
344      * secondary grouping.
345      */
346     getSecondaryGroupingDigits: function () {
347         return this.info.numfmt.secgroupSize || 0;
348     },
349 
350     /**
351      * Return the format template used to format percentages in this locale.
352      * @returns {string} the format template for formatting percentages
353      */
354     getPercentageFormat: function () {
355         return this.info.numfmt.pctFmt;
356     },
357 
358     /**
359      * Return the format template used to format percentages in this locale
360      * with negative amounts.
361      * @returns {string} the format template for formatting percentages
362      */
363     getNegativePercentageFormat: function () {
364         return this.info.numfmt.negativepctFmt;
365     },
366 
367     /**
368      * Return the symbol used for percentages in this locale.
369      * @returns {string} the symbol used for percentages in this locale
370      */
371     getPercentageSymbol: function () {
372         return this.info.numfmt.pctChar || "%";
373     },
374 
375     /**
376      * Return the symbol used for exponential in this locale.
377      * @returns {string} the symbol used for exponential in this locale
378      */
379     getExponential: function () {
380         return this.info.numfmt.exponential;
381     },
382 
383     /**
384      * Return the symbol used for exponential in this locale for native script.
385      * @returns {string} the symbol used for exponential in this locale for native script
386      */
387     getNativeExponential: function () {
388         return (this.info.native_numfmt && this.info.native_numfmt.exponential) || this.info.numfmt.exponential;
389     },
390 
391     /**
392      * Return the symbol used for percentages in this locale for native script.
393      * @returns {string} the symbol used for percentages in this locale for native script
394      */
395     getNativePercentageSymbol: function () {
396         return (this.info.native_numfmt && this.info.native_numfmt.pctChar) || this.info.numfmt.pctChar || "%";
397 
398     },
399     /**
400      * Return the format template used to format negative numbers in this locale.
401      * @returns {string} the format template for formatting negative numbers
402      */
403     getNegativeNumberFormat: function () {
404         return this.info.numfmt.negativenumFmt;
405     },
406 
407     /**
408      * Return an object containing the format templates for formatting currencies
409      * in this locale. The object has a number of properties in it that each are
410      * a particular style of format. Normally, this contains a "common" and an "iso"
411      * style, but may contain others in the future.
412      * @returns {Object} an object containing the format templates for currencies
413      */
414     getCurrencyFormats: function () {
415         return this.info.numfmt.currencyFormats;
416     },
417 
418     /**
419      * Return the currency that is legal in the locale, or which is most commonly
420      * used in regular commerce.
421      * @returns {string} the ISO 4217 code for the currency of this locale
422      */
423     getCurrency: function () {
424         return this.info.currency;
425     },
426 
427     /**
428      * Return a string that describes the style of digits used by this locale.
429      * Possible return values are:
430      * <ul>
431      * <li><i>western</i> - uses the regular western 10-based digits 0 through 9
432      * <li><i>optional</i> - native 10-based digits exist, but in modern usage,
433      * this locale most often uses western digits
434      * <li><i>native</i> - native 10-based native digits exist and are used
435      * regularly by this locale
436      * <li><i>custom</i> - uses native digits by default that are not 10-based
437      * </ul>
438      * @returns {string} string that describes the style of digits used in this locale
439      */
440     getDigitsStyle: function () {
441         if (this.info.numfmt && this.info.numfmt.useNative) {
442             return "native";
443         }
444         if (typeof(this.info.native_numfmt) !== 'undefined') {
445             return "optional";
446         }
447         return "western";
448     },
449 
450     /**
451      * Return the digits of the default script if they are defined.
452      * If not defined, the default should be the regular "Arabic numerals"
453      * used in the Latin script. (0-9)
454      * @returns {string|undefined} the digits used in the default script
455      */
456     getDigits: function () {
457         return this.info.numfmt.digits;
458     },
459 
460     /**
461      * Return the digits of the native script if they are defined.
462      * @returns {string|undefined} the digits used in the default script
463      */
464     getNativeDigits: function () {
465         return (this.info.numfmt.useNative && this.info.numfmt.digits) || (this.info.native_numfmt && this.info.native_numfmt.digits);
466     },
467 
468     /**
469      * If this locale typically uses a different type of rounding for numeric
470      * formatting other than halfdown, especially for currency, then it can be
471      * specified in the localeinfo. If the locale uses the default, then this
472      * method returns undefined. The locale's rounding method overrides the
473      * rounding method for the currency itself, which can sometimes shared
474      * between various locales so it is less specific.
475      * @returns {string} the name of the rounding mode typically used in this
476      * locale, or "halfdown" if the locale does not override the default
477      */
478     getRoundingMode: function () {
479         return this.info.numfmt.roundingMode;
480     },
481 
482     /**
483      * Return the default script used to write text in the language of this
484      * locale. Text for most languages is written in only one script, but there
485      * are some languages where the text can be written in a number of scripts,
486      * depending on a variety of things such as the region, ethnicity, religion,
487      * etc. of the author. This method returns the default script for the
488      * locale, in which the language is most commonly written.<p>
489      *
490      * The script is returned as an ISO 15924 4-letter code.
491      *
492      * @returns {string} the ISO 15924 code for the default script used to write
493      * text in this locale
494      */
495     getDefaultScript: function() {
496         return (this.info.scripts) ? this.info.scripts[0] : "Latn";
497     },
498 
499     /**
500      * Return the script used for the current locale. If the current locale
501      * explicitly defines a script, then this script is returned. If not, then
502      * the default script for the locale is returned.
503      *
504      * @see LocaleInfo.getDefaultScript
505      * @returns {string} the ISO 15924 code for the script used to write
506      * text in this locale
507      */
508     getScript: function() {
509         return this.locale.getScript() || this.getDefaultScript();
510     },
511 
512     /**
513      * Return an array of script codes which are used to write text in the current
514      * language. Text for most languages is written in only one script, but there
515      * are some languages where the text can be written in a number of scripts,
516      * depending on a variety of things such as the region, ethnicity, religion,
517      * etc. of the author. This method returns an array of script codes in which
518      * the language is commonly written.
519      *
520      * @returns {Array.<string>} an array of ISO 15924 codes for the scripts used
521      * to write text in this language
522      */
523     getAllScripts: function() {
524         return this.info.scripts || ["Latn"];
525     },
526 
527     /**
528      * Return the default style of meridiems used in this locale. Meridiems are
529      * times of day like AM/PM. In a few locales with some calendars, for example
530      * Amharic/Ethiopia using the Ethiopic calendar, the times of day may be
531      * split into different segments than simple AM/PM as in the Gregorian
532      * calendar. Only a few locales are like that. For most locales, formatting
533      * a Gregorian date will use the regular Gregorian AM/PM meridiems.
534      *
535      * @returns {string} the default meridiems style used in this locale. Possible
536      * values are "gregorian", "chinese", and "ethiopic"
537      */
538     getMeridiemsStyle: function () {
539         return this.info.meridiems || "gregorian";
540     },
541     /**
542      * Return the default PaperSize information in this locale.
543      * @returns {string} default PaperSize in this locale
544      */
545     getPaperSize: function () {
546         return this.info.paperSizes.regular;
547     },
548     /**
549      * Return the default Delimiter QuotationStart information in this locale.
550      * @returns {string} default QuotationStart in this locale
551      */
552     getDelimiterQuotationStart: function () {
553         return this.info.delimiter.quotationStart;
554     },
555     /**
556      * Return the default Delimiter QuotationEnd information in this locale.
557      * @returns {string} default QuotationEnd in this locale
558      */
559     getDelimiterQuotationEnd: function () {
560         return this.info.delimiter.quotationEnd;
561     }
562 };
563 
564 module.exports = LocaleInfo;
565