2014-09-03 18:59:37 +00:00
/ * *
2015-07-27 18:53:16 +00:00
* CLDR JavaScript Library v0 . 4.1
2014-09-03 18:59:37 +00:00
* http : //jquery.com/
*
* Copyright 2013 Rafael Xavier de Souza
* Released under the MIT license
* http : //jquery.org/license
*
2015-07-27 18:53:16 +00:00
* Date : 2015 - 02 - 25 T13 : 51 Z
2014-09-03 18:59:37 +00:00
* /
/ * !
2015-07-27 18:53:16 +00:00
* CLDR JavaScript Library v0 . 4.1 2015 - 02 - 25 T13 : 51 Z MIT license © Rafael Xavier
2014-09-03 18:59:37 +00:00
* http : //git.io/h4lmVg
* /
( function ( root , factory ) {
if ( typeof define === "function" && define . amd ) {
// AMD.
define ( factory ) ;
} else if ( typeof module === "object" && typeof module . exports === "object" ) {
// Node. CommonJS.
module . exports = factory ( ) ;
} else {
// Global
root . Cldr = factory ( ) ;
}
} ( this , function ( ) {
var arrayIsArray = Array . isArray || function ( obj ) {
return Object . prototype . toString . call ( obj ) === "[object Array]" ;
} ;
var pathNormalize = function ( path , attributes ) {
if ( arrayIsArray ( path ) ) {
path = path . join ( "/" ) ;
}
if ( typeof path !== "string" ) {
throw new Error ( "invalid path \"" + path + "\"" ) ;
}
// 1: Ignore leading slash `/`
// 2: Ignore leading `cldr/`
path = path
. replace ( /^\// , "" ) /* 1 */
. replace ( /^cldr\// , "" ) ; /* 2 */
// Replace {attribute}'s
path = path . replace ( /{[a-zA-Z]+}/g , function ( name ) {
name = name . replace ( /^{([^}]*)}$/ , "$1" ) ;
return attributes [ name ] ;
} ) ;
return path . split ( "/" ) ;
} ;
var arraySome = function ( array , callback ) {
var i , length ;
if ( array . some ) {
return array . some ( callback ) ;
}
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
if ( callback ( array [ i ] , i , array ) ) {
return true ;
}
}
return false ;
} ;
/ * *
* Return the maximized language id as defined in
* http : //www.unicode.org/reports/tr35/#Likely_Subtags
* 1. Canonicalize .
* 1.1 Make sure the input locale is in canonical form : uses the right
* separator , and has the right casing .
* TODO Right casing ? What df ? It seems languages are lowercase , scripts are
* Capitalized , territory is uppercase . I am leaving this as an exercise to
* the user .
*
* 1.2 Replace any deprecated subtags with their canonical values using the
* < alias > data in supplemental metadata . Use the first value in the
* replacement list , if it exists . Language tag replacements may have multiple
* parts , such as "sh" ➞ "sr_Latn" or mo " ➞ " ro _MD " . In such a case , the
* original script and / or region are retained if there is one . Thus
* "sh_Arab_AQ" ➞ "sr_Arab_AQ" , not "sr_Latn_AQ" .
* TODO What < alias > data ?
*
* 1.3 If the tag is grandfathered ( see < variable id = "$grandfathered"
* type = "choice" > in the supplemental data ) , then return it .
* TODO grandfathered ?
*
* 1.4 Remove the script code 'Zzzz' and the region code 'ZZ' if they occur .
* 1.5 Get the components of the cleaned - up source tag ( languages , scripts ,
* and regions ) , plus any variants and extensions .
* 2. Lookup . Lookup each of the following in order , and stop on the first
* match :
* 2.1 languages _scripts _regions
* 2.2 languages _regions
* 2.3 languages _scripts
* 2.4 languages
* 2.5 und _scripts
* 3. Return
* 3.1 If there is no match , either return an error value , or the match for
* "und" ( in APIs where a valid language tag is required ) .
* 3.2 Otherwise there is a match = languagem _scriptm _regionm
* 3.3 Let xr = xs if xs is not empty , and xm otherwise .
* 3.4 Return the language tag composed of languager _ scriptr _ regionr +
* variants + extensions .
*
* @ subtags [ Array ] normalized language id subtags tuple ( see init . js ) .
* /
var coreLikelySubtags = function ( Cldr , cldr , subtags , options ) {
var match , matchFound ,
language = subtags [ 0 ] ,
script = subtags [ 1 ] ,
sep = Cldr . localeSep ,
territory = subtags [ 2 ] ;
options = options || { } ;
// Skip if (language, script, territory) is not empty [3.3]
if ( language !== "und" && script !== "Zzzz" && territory !== "ZZ" ) {
return [ language , script , territory ] ;
}
// Skip if no supplemental likelySubtags data is present
if ( typeof cldr . get ( "supplemental/likelySubtags" ) === "undefined" ) {
return ;
}
// [2]
matchFound = arraySome ( [
[ language , script , territory ] ,
[ language , territory ] ,
[ language , script ] ,
[ language ] ,
[ "und" , script ]
] , function ( test ) {
return match = ! ( /\b(Zzzz|ZZ)\b/ ) . test ( test . join ( sep ) ) /* [1.4] */ && cldr . get ( [ "supplemental/likelySubtags" , test . join ( sep ) ] ) ;
} ) ;
// [3]
if ( matchFound ) {
// [3.2 .. 3.4]
match = match . split ( sep ) ;
return [
language !== "und" ? language : match [ 0 ] ,
script !== "Zzzz" ? script : match [ 1 ] ,
territory !== "ZZ" ? territory : match [ 2 ]
] ;
} else if ( options . force ) {
// [3.1.2]
return cldr . get ( "supplemental/likelySubtags/und" ) . split ( sep ) ;
} else {
// [3.1.1]
return ;
}
} ;
/ * *
* Given a locale , remove any fields that Add Likely Subtags would add .
* http : //www.unicode.org/reports/tr35/#Likely_Subtags
* 1. First get max = AddLikelySubtags ( inputLocale ) . If an error is signaled ,
* return it .
* 2. Remove the variants from max .
* 3. Then for trial in { language , language _ region , language _ script } . If
* AddLikelySubtags ( trial ) = max , then return trial + variants .
* 4. If you do not get a match , return max + variants .
*
* @ maxLanguageId [ Array ] maxLanguageId tuple ( see init . js ) .
* /
var coreRemoveLikelySubtags = function ( Cldr , cldr , maxLanguageId ) {
var match , matchFound ,
language = maxLanguageId [ 0 ] ,
script = maxLanguageId [ 1 ] ,
territory = maxLanguageId [ 2 ] ;
// [3]
matchFound = arraySome ( [
[ [ language , "Zzzz" , "ZZ" ] , [ language ] ] ,
[ [ language , "Zzzz" , territory ] , [ language , territory ] ] ,
[ [ language , script , "ZZ" ] , [ language , script ] ]
] , function ( test ) {
var result = coreLikelySubtags ( Cldr , cldr , test [ 0 ] ) ;
match = test [ 1 ] ;
return result && result [ 0 ] === maxLanguageId [ 0 ] &&
result [ 1 ] === maxLanguageId [ 1 ] &&
result [ 2 ] === maxLanguageId [ 2 ] ;
} ) ;
// [4]
return matchFound ? match : maxLanguageId ;
} ;
2015-07-27 18:53:16 +00:00
/ * *
* subtags ( locale )
*
* @ locale [ String ]
* /
var coreSubtags = function ( locale ) {
var aux , unicodeLanguageId ,
subtags = [ ] ;
locale = locale . replace ( /_/ , "-" ) ;
// Unicode locale extensions.
aux = locale . split ( "-u-" ) ;
if ( aux [ 1 ] ) {
aux [ 1 ] = aux [ 1 ] . split ( "-t-" ) ;
locale = aux [ 0 ] + ( aux [ 1 ] [ 1 ] ? "-t-" + aux [ 1 ] [ 1 ] : "" ) ;
subtags [ 4 /* unicodeLocaleExtensions */ ] = aux [ 1 ] [ 0 ] ;
}
// TODO normalize transformed extensions. Currently, skipped.
// subtags[ x ] = locale.split( "-t-" )[ 1 ];
unicodeLanguageId = locale . split ( "-t-" ) [ 0 ] ;
// unicode_language_id = "root"
// | unicode_language_subtag
// (sep unicode_script_subtag)?
// (sep unicode_region_subtag)?
// (sep unicode_variant_subtag)* ;
//
// Although unicode_language_subtag = alpha{2,8}, I'm using alpha{2,3}. Because, there's no language on CLDR lengthier than 3.
aux = unicodeLanguageId . match ( /^(([a-z]{2,3})(-([A-Z][a-z]{3}))?(-([A-Z]{2}|[0-9]{3}))?)(-[a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3})*$|^(root)$/ ) ;
if ( aux === null ) {
return [ "und" , "Zzzz" , "ZZ" ] ;
}
subtags [ 0 /* language */ ] = aux [ 9 ] /* root */ || aux [ 2 ] || "und" ;
subtags [ 1 /* script */ ] = aux [ 4 ] || "Zzzz" ;
subtags [ 2 /* territory */ ] = aux [ 6 ] || "ZZ" ;
subtags [ 3 /* variant */ ] = aux [ 7 ] ;
// 0: language
// 1: script
// 2: territory (aka region)
// 3: variant
// 4: unicodeLocaleExtensions
return subtags ;
} ;
var arrayForEach = function ( array , callback ) {
var i , length ;
if ( array . forEach ) {
return array . forEach ( callback ) ;
}
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
callback ( array [ i ] , i , array ) ;
}
} ;
/ * *
* bundleLookup ( minLanguageId )
*
* @ Cldr [ Cldr class ]
*
* @ cldr [ Cldr instance ]
*
* @ minLanguageId [ String ] requested languageId after applied remove likely subtags .
* /
var bundleLookup = function ( Cldr , cldr , minLanguageId ) {
var availableBundleMap = Cldr . _availableBundleMap ,
availableBundleMapQueue = Cldr . _availableBundleMapQueue ;
if ( availableBundleMapQueue . length ) {
arrayForEach ( availableBundleMapQueue , function ( bundle ) {
var existing , maxBundle , minBundle , subtags ;
subtags = coreSubtags ( bundle ) ;
maxBundle = coreLikelySubtags ( Cldr , cldr , subtags , { force : true } ) || subtags ;
minBundle = coreRemoveLikelySubtags ( Cldr , cldr , maxBundle ) ;
minBundle = minBundle . join ( Cldr . localeSep ) ;
existing = availableBundleMapQueue [ minBundle ] ;
if ( existing && existing . length < bundle . length ) {
return ;
}
availableBundleMap [ minBundle ] = bundle ;
} ) ;
Cldr . _availableBundleMapQueue = [ ] ;
}
return availableBundleMap [ minLanguageId ] || null ;
} ;
var objectKeys = function ( object ) {
var i ,
result = [ ] ;
if ( Object . keys ) {
return Object . keys ( object ) ;
}
for ( i in object ) {
result . push ( i ) ;
}
return result ;
} ;
var createError = function ( code , attributes ) {
var error , message ;
message = code + ( attributes && JSON ? ": " + JSON . stringify ( attributes ) : "" ) ;
error = new Error ( message ) ;
error . code = code ;
// extend( error, attributes );
arrayForEach ( objectKeys ( attributes ) , function ( attribute ) {
error [ attribute ] = attributes [ attribute ] ;
} ) ;
return error ;
} ;
var validate = function ( code , check , attributes ) {
if ( ! check ) {
throw createError ( code , attributes ) ;
}
} ;
var validatePresence = function ( value , name ) {
validate ( "E_MISSING_PARAMETER" , typeof value !== "undefined" , {
name : name
} ) ;
} ;
var validateType = function ( value , name , check , expected ) {
validate ( "E_INVALID_PAR_TYPE" , check , {
expected : expected ,
name : name ,
value : value
} ) ;
} ;
var validateTypePath = function ( value , name ) {
validateType ( value , name , typeof value === "string" || arrayIsArray ( value ) , "String or Array" ) ;
} ;
/ * *
* Function inspired by jQuery Core , but reduced to our use case .
* /
var isPlainObject = function ( obj ) {
return obj !== null && "" + obj === "[object Object]" ;
} ;
var validateTypePlainObject = function ( value , name ) {
validateType ( value , name , typeof value === "undefined" || isPlainObject ( value ) , "Plain Object" ) ;
} ;
var validateTypeString = function ( value , name ) {
validateType ( value , name , typeof value === "string" , "a string" ) ;
} ;
2014-09-03 18:59:37 +00:00
// @path: normalized path
var resourceGet = function ( data , path ) {
var i ,
node = data ,
length = path . length ;
for ( i = 0 ; i < length - 1 ; i ++ ) {
node = node [ path [ i ] ] ;
if ( ! node ) {
return undefined ;
}
}
return node [ path [ i ] ] ;
} ;
2015-07-27 18:53:16 +00:00
/ * *
* setAvailableBundles ( Cldr , json )
*
* @ Cldr [ Cldr class ]
*
* @ json resolved / unresolved cldr data .
*
* Set available bundles queue based on passed json CLDR data . Considers a bundle as any String at / main / { bundle } .
* /
var coreSetAvailableBundles = function ( Cldr , json ) {
var bundle ,
availableBundleMapQueue = Cldr . _availableBundleMapQueue ,
main = resourceGet ( json , [ "main" ] ) ;
if ( main ) {
for ( bundle in main ) {
if ( main . hasOwnProperty ( bundle ) && bundle !== "root" ) {
availableBundleMapQueue . push ( bundle ) ;
}
}
}
2014-09-03 18:59:37 +00:00
} ;
2015-07-27 18:53:16 +00:00
var alwaysArray = function ( somethingOrArray ) {
return arrayIsArray ( somethingOrArray ) ? somethingOrArray : [ somethingOrArray ] ;
2014-09-03 18:59:37 +00:00
} ;
var jsonMerge = ( function ( ) {
// Returns new deeply merged JSON.
//
// Eg.
// merge( { a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } } )
// -> { a: { b: 3, c: 2, d: 4 } }
//
// @arguments JSON's
//
var merge = function ( ) {
var destination = { } ,
sources = [ ] . slice . call ( arguments , 0 ) ;
arrayForEach ( sources , function ( source ) {
var prop ;
for ( prop in source ) {
if ( prop in destination && arrayIsArray ( destination [ prop ] ) ) {
// Concat Arrays
destination [ prop ] = destination [ prop ] . concat ( source [ prop ] ) ;
} else if ( prop in destination && typeof destination [ prop ] === "object" ) {
// Merge Objects
destination [ prop ] = merge ( destination [ prop ] , source [ prop ] ) ;
} else {
// Set new values
destination [ prop ] = source [ prop ] ;
}
}
} ) ;
return destination ;
} ;
return merge ;
} ( ) ) ;
2015-07-27 18:53:16 +00:00
/ * *
* load ( Cldr , source , jsons )
*
* @ Cldr [ Cldr class ]
*
* @ source [ Object ]
*
* @ jsons [ arguments ]
* /
var coreLoad = function ( Cldr , source , jsons ) {
var i , j , json ;
validatePresence ( jsons [ 0 ] , "json" ) ;
// Support arbitrary parameters, e.g., `Cldr.load({...}, {...})`.
for ( i = 0 ; i < jsons . length ; i ++ ) {
// Support array parameters, e.g., `Cldr.load([{...}, {...}])`.
json = alwaysArray ( jsons [ i ] ) ;
for ( j = 0 ; j < json . length ; j ++ ) {
validateTypePlainObject ( json [ j ] , "json" ) ;
source = jsonMerge ( source , json [ j ] ) ;
coreSetAvailableBundles ( Cldr , json [ j ] ) ;
}
}
return source ;
} ;
var itemGetResolved = function ( Cldr , path , attributes ) {
// Resolve path
var normalizedPath = pathNormalize ( path , attributes ) ;
return resourceGet ( Cldr . _resolved , normalizedPath ) ;
} ;
2014-09-03 18:59:37 +00:00
/ * *
* new Cldr ( )
* /
var Cldr = function ( locale ) {
this . init ( locale ) ;
} ;
// Build optimization hack to avoid duplicating functions across modules.
Cldr . _alwaysArray = alwaysArray ;
2015-07-27 18:53:16 +00:00
Cldr . _coreLoad = coreLoad ;
2014-09-03 18:59:37 +00:00
Cldr . _createError = createError ;
Cldr . _itemGetResolved = itemGetResolved ;
Cldr . _jsonMerge = jsonMerge ;
Cldr . _pathNormalize = pathNormalize ;
Cldr . _resourceGet = resourceGet ;
Cldr . _validatePresence = validatePresence ;
Cldr . _validateType = validateType ;
Cldr . _validateTypePath = validateTypePath ;
Cldr . _validateTypePlainObject = validateTypePlainObject ;
2015-07-27 18:53:16 +00:00
Cldr . _availableBundleMap = { } ;
Cldr . _availableBundleMapQueue = [ ] ;
2014-09-03 18:59:37 +00:00
Cldr . _resolved = { } ;
// Allow user to override locale separator "-" (default) | "_". According to http://www.unicode.org/reports/tr35/#Unicode_language_identifier, both "-" and "_" are valid locale separators (eg. "en_GB", "en-GB"). According to http://unicode.org/cldr/trac/ticket/6786 its usage must be consistent throughout the data set.
Cldr . localeSep = "-" ;
2015-07-27 18:53:16 +00:00
/ * *
* Cldr . load ( json [ , json , ... ] )
*
* @ json [ JSON ] CLDR data or [ Array ] Array of @ json ' s .
*
* Load resolved cldr data .
* /
Cldr . load = function ( ) {
Cldr . _resolved = coreLoad ( Cldr , Cldr . _resolved , arguments ) ;
2014-09-03 18:59:37 +00:00
} ;
/ * *
* . init ( ) automatically run on instantiation / construction .
* /
Cldr . prototype . init = function ( locale ) {
2015-07-27 18:53:16 +00:00
var attributes , language , maxLanguageId , minLanguageId , script , subtags , territory , unicodeLocaleExtensions , variant ,
2014-09-03 18:59:37 +00:00
sep = Cldr . localeSep ;
validatePresence ( locale , "locale" ) ;
validateTypeString ( locale , "locale" ) ;
2015-07-27 18:53:16 +00:00
subtags = coreSubtags ( locale ) ;
unicodeLocaleExtensions = subtags [ 4 ] ;
variant = subtags [ 3 ] ;
2014-09-03 18:59:37 +00:00
// Normalize locale code.
// Get (or deduce) the "triple subtags": language, territory (also aliased as region), and script subtags.
// Get the variant subtags (calendar, collation, currency, etc).
// refs:
// - http://www.unicode.org/reports/tr35/#Field_Definitions
// - http://www.unicode.org/reports/tr35/#Language_and_Locale_IDs
// - http://www.unicode.org/reports/tr35/#Unicode_locale_identifier
// When a locale id does not specify a language, or territory (region), or script, they are obtained by Likely Subtags.
2015-07-27 18:53:16 +00:00
maxLanguageId = coreLikelySubtags ( Cldr , this , subtags , { force : true } ) || subtags ;
2014-09-03 18:59:37 +00:00
language = maxLanguageId [ 0 ] ;
script = maxLanguageId [ 1 ] ;
2015-07-27 18:53:16 +00:00
territory = maxLanguageId [ 2 ] ;
2014-09-03 18:59:37 +00:00
2015-07-27 18:53:16 +00:00
minLanguageId = coreRemoveLikelySubtags ( Cldr , this , maxLanguageId ) . join ( sep ) ;
2014-09-03 18:59:37 +00:00
// Set attributes
2015-07-27 18:53:16 +00:00
this . attributes = attributes = {
bundle : bundleLookup ( Cldr , this , minLanguageId ) ,
2014-09-03 18:59:37 +00:00
// Unicode Language Id
2015-07-27 18:53:16 +00:00
minlanguageId : minLanguageId ,
2014-09-03 18:59:37 +00:00
maxLanguageId : maxLanguageId . join ( sep ) ,
// Unicode Language Id Subtabs
language : language ,
script : script ,
territory : territory ,
region : territory , /* alias */
variant : variant
} ;
2015-07-27 18:53:16 +00:00
// Unicode locale extensions.
unicodeLocaleExtensions && ( "-" + unicodeLocaleExtensions ) . replace ( /-[a-z]{3,8}|(-[a-z]{2})-([a-z]{3,8})/g , function ( attribute , key , type ) {
if ( key ) {
// Extension is in the `keyword` form.
attributes [ "u" + key ] = type ;
} else {
// Extension is in the `attribute` form.
attributes [ "u" + attribute ] = true ;
}
} ) ;
this . locale = locale ;
2014-09-03 18:59:37 +00:00
} ;
/ * *
* . get ( )
* /
Cldr . prototype . get = function ( path ) {
validatePresence ( path , "path" ) ;
validateTypePath ( path , "path" ) ;
return itemGetResolved ( Cldr , path , this . attributes ) ;
} ;
/ * *
* . main ( )
* /
Cldr . prototype . main = function ( path ) {
validatePresence ( path , "path" ) ;
validateTypePath ( path , "path" ) ;
2015-07-27 18:53:16 +00:00
validate ( "E_MISSING_BUNDLE" , this . attributes . bundle !== null , {
locale : this . locale
} ) ;
2014-09-03 18:59:37 +00:00
path = alwaysArray ( path ) ;
2015-07-27 18:53:16 +00:00
return this . get ( [ "main/{bundle}" ] . concat ( path ) ) ;
2014-09-03 18:59:37 +00:00
} ;
return Cldr ;
2015-07-27 18:53:16 +00:00
2014-09-03 18:59:37 +00:00
} ) ) ;