Multiple fixes (#713)

* - Update new create drive options
* - Add gender, keyword, languages attributes to user
This commit is contained in:
Ross Scroggs
2018-03-28 15:01:49 -07:00
committed by Jay Lee
parent ce2e379f30
commit 16636c34b5
3 changed files with 144 additions and 49 deletions

View File

@ -436,6 +436,7 @@ Named items
email|emails|otheremail|otheremails| email|emails|otheremail|otheremails|
externalids|externalid| externalids|externalid|
familyname|firstname|fullname|givenname|lastname|name| familyname|firstname|fullname|givenname|lastname|name|
gender|
id| id|
ims|im| ims|im|
includeinglobaladdresslist|gal| includeinglobaladdresslist|gal|
@ -444,6 +445,8 @@ Named items
ismailboxsetup| ismailboxsetup|
isenforcedin2sv|is2svenforced| isenforcedin2sv|is2svenforced|
isenrolledin2sv|is2svenrolled| isenrolledin2sv|is2svenrolled|
keyword|keywords|
language|languages|
lastlogintime| lastlogintime|
locations|location| locations|location|
noneditablealiases|aliases|nicknames| noneditablealiases|aliases|nicknames|
@ -463,11 +466,18 @@ Named items
familyname|lastname|givenname|firstname|email familyname|lastname|givenname|firstname|email
Named Lists Named Lists
Lists can be in the following formats
Items, separated by commas, without spaces or commas in the items themselves: item(,item)* Items in a list can be separated by commas or spaces; if an item itself contains a comma, a space or a single quote, special quoting must be used.
Items, separated by spaces, without spaces or commas in the items themselves: "item( item)*" Typically, you will enclose the entire list in double quotes and quote each item in the list as detailed below.
Items, separated by commas, with spaces or commas in the items themselves: "'it em'(,'it,em')*"
Items, separated by spaces, with spaces or commas in the items themselves: "'it em'( 'it,em')*" Items, separated by commas, without spaces, commas or single quotes in the items themselves
"item,item,item"
Items, separated by spaces, without spaces, commas or single quotes in the items themselves
"item item item"
Items, separated by commas, with spaces, commas or single quotes in the items themselves
"'it em','it,em',\"it'em\""
Items, separated by spaces, with spaces, commas or single quotes in the items themselves
"'it em' 'it,em' \"it'em\""
<ACLList> ::= "<ACLScope>(,<ACLScope>)*" <ACLList> ::= "<ACLScope>(,<ACLScope>)*"
<ASPIDList> ::= "<ASPID>(,<ASPID>)*" <ASPIDList> ::= "<ASPID>(,<ASPID>)*"
@ -507,29 +517,29 @@ Items, separated by spaces, with spaces or commas in the items themselves: "'it
Specify a collection of ChromeOS devices by directly specifying them Specify a collection of ChromeOS devices by directly specifying them
<CrOSTypeEntity> ::= <CrOSTypeEntity> ::=
(all cros)| (all cros)|
(cros <CrOSList>)| (cros <CrOSList>)|
Specify a collection of Users by directly specifying them or by specifiying items that will yield a list of users Specify a collection of Users by directly specifying them or by specifiying items that will yield a list of users
<UserTypeEntity> ::= <UserTypeEntity> ::=
(all users)| (all users)|
(user <UserItem>)| (user <UserItem>)|
(users <UserList>)| (users <UserList>)|
(group <GroupItem)| (group <GroupItem)|
(ou|org <OrgUnitPath)| (ou|org <OrgUnitPath)|
(ou_and_children|ou_and_child <OrgUnitPath>)| (ou_and_children|ou_and_child <OrgUnitPath>)|
(courseparticipants <CourseID>)| (courseparticipants <CourseID>)|
(students <CourseID>)| (students <CourseID>)|
(teachers <CourseID>)| (teachers <CourseID>)|
(file <FileName>)| (file <FileName>)|
(csvfile <FileName>:<FieldName>)| (csvfile <FileName>:<FieldName>)|
(license|licenses|licence|licences <SKUIDList>)| (license|licenses|licence|licences <SKUIDList>)|
(query <QueryUser>) (query <QueryUser>)
Item attributes Item attributes
<BuildingAttributes> ::= <BuildingAttributes> ::=
(buildingid <String>)|
(description <String>)| (description <String>)|
(floors <FloorNameList>)| (floors <FloorNameList>)|
(id <String>)|
(latitude <Float>)| (latitude <Float>)|
(longitude <Float>)| (longitude <Float>)|
(name <String>) (name <String>)
@ -621,7 +631,7 @@ Item attributes
<ResourceAttributes> ::= <ResourceAttributes> ::=
(buildingid <BuildingID>)| (buildingid <BuildingID>)|
(capacity <Number>)| (capacity <Number>)|
(category other|room|conference_room)| (category other|room|conference_room|unknown_category)|
(description <String>)| (description <String>)|
(features <FeatureNameList>)| (features <FeatureNameList>)|
(floor <FloorName>)| (floor <FloorName>)|
@ -646,8 +656,11 @@ Item attributes
(externalid clear|(account|customer|login_id|network|organization|<String> <String>))| (externalid clear|(account|customer|login_id|network|organization|<String> <String>))|
(firstname|givenname <String>)| (firstname|givenname <String>)|
(gal|includeinglobaladdresslist <Boolean>)| (gal|includeinglobaladdresslist <Boolean>)|
(gender clear|(female|male|unknown|(other <String>) [addressmeas <String>]))|
(im clear|(type work|home|other|(custom <String>) protocol aim|gtalk|icq|jabber|msn|net_meeting|qq|skype|yahoo|(custom_protocol <String>) <String> [notprimary|primary]))| (im clear|(type work|home|other|(custom <String>) protocol aim|gtalk|icq|jabber|msn|net_meeting|qq|skype|yahoo|(custom_protocol <String>) <String> [notprimary|primary]))|
(ipwhitelisted <Boolean>)| (ipwhitelisted <Boolean>)|
(keyword clear|(occupation|outlook|(custom <string>) <String>))|
(language clear|<LanguageList>)|
(lastname|familyname <String>)| (lastname|familyname <String>)|
(location clear|(type default|desk|<String> area <String> [building|buildingid <BuildingID>] [floor|floorname <FloorName>] [section|floorsection <String>] [desk|deskcode <String>] endlocation))| (location clear|(type default|desk|<String> area <String> [building|buildingid <BuildingID>] [floor|floorname <FloorName>] [section|floorsection <String>] [desk|deskcode <String>] endlocation))|
(note clear|([text_plain|text_html] <String>|(file <FileName> [charset <Charset>])))| (note clear|([text_plain|text_html] <String>|(file <FileName> [charset <Charset>])))|
@ -821,13 +834,6 @@ Prints no header row and deviceId for specified CrOS devices.
gam print cros ... basic|full gam print cros ... basic|full
Prints a header row and selected fields for specified CrOS devices. Prints a header row and selected fields for specified CrOS devices.
Print a header row and selected fields for specified CrOS devices; deviceId is the always the first column.
If you specify allfields|basic|full, all other column headers are sorted.
If you specify <CrOSFieldName>* | fields <CrOSFieldNameList>, the column headers appear in the order specified.
gam [<CrOSTypeEntity>] print cros [todrive [<ToDriveAttributes>]] [query <QueryCrOS>]|[select <CrOSTypeEntity>] [limittoou <OrgUnitItem>]
[orderby <CrOSOrderByFieldName> [ascending|descending]] [nolists|recentusers|timeranges|devicefiles] [listlimit <Number>] [start <Date>] [end <Date>]
[basic|full|allfields] <CrOSFieldName>* [fields <CrOSFieldNameList>]
The basic argument outputs these column headers: deviceId,annotatedAssetId,annotatedLocation,annotatedUser,lastSync,notes,serialNumber,status The basic argument outputs these column headers: deviceId,annotatedAssetId,annotatedLocation,annotatedUser,lastSync,notes,serialNumber,status
The allfields/full arguments output all column headers including three headers, recentUsers, activeTimeRanges and deviceFiles, The allfields/full arguments output all column headers including three headers, recentUsers, activeTimeRanges and deviceFiles,
that repeat with two/four/two subvalues each, yielding a large number of columns that make the output hard to process. that repeat with two/four/two subvalues each, yielding a large number of columns that make the output hard to process.
@ -1039,7 +1045,7 @@ gam <UserTypeEntity> show fileinfo <DriveFileID> [allfields|<DriveFieldName>*]
gam <UserTypeEntity> show filerevisions <DriveFileID> gam <UserTypeEntity> show filerevisions <DriveFileID>
gam <UserTypeEntity> show filetree [anyowner] (orderby <DriveOrderByFieldName> [ascending|descending])* gam <UserTypeEntity> show filetree [anyowner] (orderby <DriveOrderByFieldName> [ascending|descending])*
gam <UserTypeEntity> create|add drivefile [drivefilename <DriveFileName>] <DriveFileAddAttributes>* gam <UserTypeEntity> create|add drivefile [drivefilename <DriveFileName>] <DriveFileAddAttributes>* [csv] [todrive]
gam <UserTypeEntity> update drivefile (id <DriveFileID)|(drivefilename <DriveFileName>)|(query <QueryDriveFile) [copy] [newfilename <DriveFileName>] <DriveFileUpdateAttributes>* gam <UserTypeEntity> update drivefile (id <DriveFileID)|(drivefilename <DriveFileName>)|(query <QueryDriveFile) [copy] [newfilename <DriveFileName>] <DriveFileUpdateAttributes>*
gam <UserTypeEntity> get drivefile (id <DriveFileID>)|(drivefilename <DriveFileName>)|(query <QueryDriveFile>) [format <FileFormatList>] [targetfolder <FilePath>] [revision <Number>] gam <UserTypeEntity> get drivefile (id <DriveFileID>)|(drivefilename <DriveFileName>)|(query <QueryDriveFile>) [format <FileFormatList>] [targetfolder <FilePath>] [revision <Number>]
gam <UserTypeEntity> delete|del drivefile <DriveFileID>|<DriveFileURL>|(query:<QueryDriveFile>) [purge|untrash] gam <UserTypeEntity> delete|del drivefile <DriveFileID>|<DriveFileURL>|(query:<QueryDriveFile>) [purge|untrash]

View File

@ -4346,7 +4346,7 @@ def getDriveFileAttribute(i, body, parameters, myarg, update=False):
parameters[DFA_OCR] = True parameters[DFA_OCR] = True
i += 1 i += 1
elif myarg == u'ocrlanguage': elif myarg == u'ocrlanguage':
parameters[DFA_OCRLANGUAGE] = sys.argv[i+1] parameters[DFA_OCRLANGUAGE] = LANGUAGE_CODES_MAP.get(sys.argv[i+1].lower(), sys.argv[i+1])
i += 2 i += 2
elif myarg in DRIVEFILE_LABEL_CHOICES_MAP: elif myarg in DRIVEFILE_LABEL_CHOICES_MAP:
body.setdefault(u'labels', {}) body.setdefault(u'labels', {})
@ -4461,9 +4461,9 @@ def doUpdateDriveFile(users):
print u'Successfully copied %s to %s' % (fileId, result[u'id']) print u'Successfully copied %s to %s' % (fileId, result[u'id'])
def createDriveFile(users): def createDriveFile(users):
csv_output = None csv_output = to_drive = False
csv_rows = [] csv_rows = []
csv_titles = [u'title',u'id'] csv_titles = [u'User', u'title', u'id']
media_body = None media_body = None
body, parameters = initializeDriveFileAttributes() body, parameters = initializeDriveFileAttributes()
i = 5 i = 5
@ -4475,6 +4475,9 @@ def createDriveFile(users):
elif myarg == u'csv': elif myarg == u'csv':
csv_output = True csv_output = True
i += 1 i += 1
elif myarg == u'todrive':
to_drive = True
i += 1
else: else:
i = getDriveFileAttribute(i, body, parameters, myarg, False) i = getDriveFileAttribute(i, body, parameters, myarg, False)
for user in users: for user in users:
@ -4495,17 +4498,14 @@ def createDriveFile(users):
supportsTeamDrives=True) supportsTeamDrives=True)
titleInfo = u'{0}({1})'.format(result[u'title'], result[u'id']) titleInfo = u'{0}({1})'.format(result[u'title'], result[u'id'])
if csv_output: if csv_output:
csv_rows.append({ csv_rows.append({u'User': user, u'title': result[u'title'], u'id': result[u'id']})
u'title': result[u'title'],
u'id': result[u'id']
})
else: else:
if parameters[DFA_LOCALFILENAME]: if parameters[DFA_LOCALFILENAME]:
print u'Successfully uploaded %s to Drive File %s' % (parameters[DFA_LOCALFILENAME], titleInfo) print u'Successfully uploaded %s to Drive File %s' % (parameters[DFA_LOCALFILENAME], titleInfo)
else: else:
print u'Successfully created Drive %s %s' % ([u'Folder', u'File'][result[u'mimeType'] != MIMETYPE_GA_FOLDER], titleInfo) print u'Successfully created Drive %s %s' % ([u'Folder', u'File'][result[u'mimeType'] != MIMETYPE_GA_FOLDER], titleInfo)
if csv_output: if csv_output:
writeCSVfile(csv_rows, csv_titles, u'Files', False) writeCSVfile(csv_rows, csv_titles, u'Files', to_drive)
def downloadDriveFile(users): def downloadDriveFile(users):
i = 5 i = 5
@ -5475,7 +5475,7 @@ def doDeleteLabel(users):
bcount = 0 bcount = 0
j = 0 j = 0
del_me_count = len(del_labels) del_me_count = len(del_labels)
dbatch = gmail.new_batch_http_request() dbatch = gmail.new_batch_http_request()
for del_me in del_labels: for del_me in del_labels:
j += 1 j += 1
print u' deleting label %s (%s/%s)' % (del_me[u'name'], j, del_me_count) print u' deleting label %s (%s/%s)' % (del_me[u'name'], j, del_me_count)
@ -6423,7 +6423,7 @@ def doGetUserSchema():
_showSchema(schema) _showSchema(schema)
def getUserAttributes(i, cd, updateCmd=False): def getUserAttributes(i, cd, updateCmd=False):
def getEntryType(i, entry, entryTypes, setTypeCustom=True): def getEntryType(i, entry, entryTypes, setTypeCustom=True, customKeyword=u'custom', customTypeKeyword=u'customType'):
""" Get attribute entry type """ Get attribute entry type
entryTypes is list of pre-defined types, a|b|c entryTypes is list of pre-defined types, a|b|c
Allow a|b|c|<String>, a|b|c|custom <String> Allow a|b|c|<String>, a|b|c|custom <String>
@ -6435,26 +6435,25 @@ def getUserAttributes(i, cd, updateCmd=False):
""" """
utype = sys.argv[i] utype = sys.argv[i]
ltype = utype.lower() ltype = utype.lower()
if ltype == u'custom': if ltype == customKeyword:
i += 1 i += 1
utype = sys.argv[i] utype = sys.argv[i]
ltype = utype.lower() ltype = utype.lower()
if ltype in entryTypes: if ltype in entryTypes:
entry[u'type'] = ltype entry[u'type'] = ltype
entry.pop(u'customType', None) entry.pop(customTypeKeyword, None)
else: else:
entry[u'customType'] = utype entry[customTypeKeyword] = utype
if setTypeCustom: if setTypeCustom:
entry[u'type'] = u'custom' entry[u'type'] = customKeyword
else: else:
entry.pop(u'type', None) entry.pop(u'type', None)
return i+1 return i+1
def checkClearBodyList(i, body, itemName): def checkClearBodyList(i, body, itemName):
if sys.argv[i].lower() == u'clear': if sys.argv[i].lower() == u'clear':
if itemName in body: body.pop(itemName, None)
del body[itemName] body[itemName] = None
body.setdefault(itemName, None)
return True return True
return False return False
@ -6553,6 +6552,28 @@ def getUserAttributes(i, cd, updateCmd=False):
if body[u'orgUnitPath'][0] != u'/': if body[u'orgUnitPath'][0] != u'/':
body[u'orgUnitPath'] = u'/%s' % body[u'orgUnitPath'] body[u'orgUnitPath'] = u'/%s' % body[u'orgUnitPath']
i += 2 i += 2
elif myarg in [u'language', u'languages']:
i += 1
if checkClearBodyList(i, body, u'languages'):
i += 1
continue
for language in sys.argv[i].replace(u',', u' ').split():
if language.lower() in LANGUAGE_CODES_MAP:
appendItemToBodyList(body, u'languages', {u'languageCode': LANGUAGE_CODES_MAP[language.lower()]})
else:
appendItemToBodyList(body, u'languages', {u'customLanguage': language})
i += 1
elif myarg == u'gender':
i += 1
if checkClearBodyList(i, body, u'gender'):
i += 1
continue
gender = {}
i = getEntryType(i, gender, USER_GENDER_TYPES, customKeyword=u'other', customTypeKeyword=u'customGender')
if (i < len(sys.argv)) and (sys.argv[i].lower() == u'addressmeas'):
gender[u'addressMeAs'] = getString(i+1, u'String')
i += 2
body[u'gender'] = gender
elif myarg in [u'address', u'addresses']: elif myarg in [u'address', u'addresses']:
i += 1 i += 1
if checkClearBodyList(i, body, u'addresses'): if checkClearBodyList(i, body, u'addresses'):
@ -6846,6 +6867,16 @@ def getUserAttributes(i, cd, updateCmd=False):
else: else:
systemErrorExit(3, '%s is not a valid argument for user posix details. Make sure user posix details end with an endposix argument') systemErrorExit(3, '%s is not a valid argument for user posix details. Make sure user posix details end with an endposix argument')
appendItemToBodyList(body, u'posixAccounts', posix, checkSystemId=True) appendItemToBodyList(body, u'posixAccounts', posix, checkSystemId=True)
elif myarg in [u'keyword', u'keywords']:
i += 1
if checkClearBodyList(i, body, u'keywords'):
i += 1
continue
keyword = {}
i = getEntryType(i, keyword, USER_KEYWORD_TYPES, customKeyword=u'custom', customTypeKeyword=u'customType')
keyword[u'value'] = sys.argv[i]
i += 1
appendItemToBodyList(body, u'keywords', keyword)
elif myarg == u'clearschema': elif myarg == u'clearschema':
if not updateCmd: if not updateCmd:
systemErrorExit(2, '%s is not a valid create user argument.' % sys.argv[i]) systemErrorExit(2, '%s is not a valid create user argument.' % sys.argv[i])
@ -7650,6 +7681,8 @@ def getGroupAttrValue(myarg, value, gs_object, gs_body, function):
elif params[u'type'] == u'string': elif params[u'type'] == u'string':
if attrib == u'description': if attrib == u'description':
value = value.replace(u'\\n', u'\n') value = value.replace(u'\\n', u'\n')
elif attrib == u'primaryLanguage':
value = LANGUAGE_CODES_MAP.get(value.lower(), value)
elif params[u'description'].find(value.upper()) != -1: # ugly hack because API wants some values uppercased. elif params[u'description'].find(value.upper()) != -1: # ugly hack because API wants some values uppercased.
value = value.upper() value = value.upper()
elif value.lower() in true_values: elif value.lower() in true_values:
@ -8683,6 +8716,15 @@ def doGetUserInfo(user_email=None):
print utils.convertUTF8(u'First Name: %s' % user[u'name'][u'givenName']) print utils.convertUTF8(u'First Name: %s' % user[u'name'][u'givenName'])
if u'name' in user and u'familyName' in user[u'name']: if u'name' in user and u'familyName' in user[u'name']:
print utils.convertUTF8(u'Last Name: %s' % user[u'name'][u'familyName']) print utils.convertUTF8(u'Last Name: %s' % user[u'name'][u'familyName'])
if u'languages' in user:
up = u'languageCode'
languages = [row[up] for row in user[u'languages'] if up in row]
if languages:
print u'Languages: %s' % u','.join(languages)
up = u'customLanguage'
languages = [row[up] for row in user[u'languages'] if up in row]
if languages:
print u'Custom Languages: %s' % u','.join(languages)
if u'isAdmin' in user: if u'isAdmin' in user:
print u'Is a Super Admin: %s' % user[u'isAdmin'] print u'Is a Super Admin: %s' % user[u'isAdmin']
if u'isDelegatedAdmin' in user: if u'isDelegatedAdmin' in user:
@ -8733,6 +8775,22 @@ def doGetUserInfo(user_email=None):
else: else:
print utils.convertUTF8(utils.indentMultiLineText(u' value: {0}'.format(notes), n=2)) print utils.convertUTF8(utils.indentMultiLineText(u' value: {0}'.format(notes), n=2))
print u'' print u''
if u'gender' in user:
print u'Gender'
gender = user[u'gender']
for key in gender:
if key == u'customGender' and not gender[key]:
continue
print utils.convertUTF8(u' %s: %s' % (key, gender[key]))
print u''
if u'keywords' in user:
print u'Keywords:'
for keyword in user[u'keywords']:
for key in keyword:
if key == u'customType' and not keyword[key]:
continue
print utils.convertUTF8(u' %s: %s' % (key, keyword[key]))
print u''
if u'ims' in user: if u'ims' in user:
print u'IMs:' print u'IMs:'
for im in user[u'ims']: for im in user[u'ims']:
@ -9891,6 +9949,7 @@ USER_ARGUMENT_TO_PROPERTY_MAP = {
u'firstname': [u'name.givenName',], u'firstname': [u'name.givenName',],
u'fullname': [u'name.fullName',], u'fullname': [u'name.fullName',],
u'gal': [u'includeInGlobalAddressList',], u'gal': [u'includeInGlobalAddressList',],
u'gender': [u'gender.type', u'gender.customGender', u'gender.addressMeAs',],
u'givenname': [u'name.givenName',], u'givenname': [u'name.givenName',],
u'id': [u'id',], u'id': [u'id',],
u'im': [u'ims',], u'im': [u'ims',],
@ -9904,6 +9963,10 @@ USER_ARGUMENT_TO_PROPERTY_MAP = {
u'is2svenforced': [u'isEnforcedIn2Sv',], u'is2svenforced': [u'isEnforcedIn2Sv',],
u'is2svenrolled': [u'isEnrolledIn2Sv',], u'is2svenrolled': [u'isEnrolledIn2Sv',],
u'ismailboxsetup': [u'isMailboxSetup',], u'ismailboxsetup': [u'isMailboxSetup',],
u'keyword': [u'keywords',],
u'keywords': [u'keywords',],
u'language': [u'languages',],
u'languages': [u'languages',],
u'lastlogintime': [u'lastLoginTime',], u'lastlogintime': [u'lastLoginTime',],
u'lastname': [u'name.familyName',], u'lastname': [u'name.familyName',],
u'location': [u'locations',], u'location': [u'locations',],

View File

@ -784,9 +784,10 @@ GAPI_MEMBERS_RETRY_REASONS = [GAPI_SYSTEM_ERROR]
USER_ADDRESS_TYPES = [u'home', u'work', u'other'] USER_ADDRESS_TYPES = [u'home', u'work', u'other']
USER_EMAIL_TYPES = [u'home', u'work', u'other'] USER_EMAIL_TYPES = [u'home', u'work', u'other']
USER_EXTERNALID_TYPES = [u'account', u'customer', u'login_id', u'network', USER_EXTERNALID_TYPES = [u'account', u'customer', u'login_id', u'network', u'organization']
u'organization'] USER_GENDER_TYPES = [u'female', u'male', u'unknown']
USER_IM_TYPES = [u'home', u'work', u'other'] USER_IM_TYPES = [u'home', u'work', u'other']
USER_KEYWORD_TYPES = [u'occupation', u'outlook']
USER_LOCATION_TYPES = [u'default', u'desk'] USER_LOCATION_TYPES = [u'default', u'desk']
USER_ORGANIZATION_TYPES = [u'domain_only', u'school', u'unknown', u'work'] USER_ORGANIZATION_TYPES = [u'domain_only', u'school', u'unknown', u'work']
USER_PHONE_TYPES = [u'assistant', u'callback', u'car', u'company_main', USER_PHONE_TYPES = [u'assistant', u'callback', u'car', u'company_main',
@ -952,3 +953,28 @@ WEBCOLOR_MAP = {
u'yellow': u'#ffff00', u'yellow': u'#ffff00',
u'yellowgreen': u'#9acd32', u'yellowgreen': u'#9acd32',
} }
# Valid language codes
LANGUAGE_CODES_MAP = {
u'ach': u'ach', u'af': u'af', u'ag': u'ga', u'ak': u'ak', u'am': u'am', u'ar': u'ar', u'az': u'az', #Luo, Afrikaans, Irish, Akan, Amharic, Arabica, Azerbaijani
u'be': u'be', u'bem': u'bem', u'bg': u'bg', u'bn': u'bn', u'br': u'br', u'bs': u'bs', u'ca': u'ca', #Belarusian, Bemba, Bulgarian, Bengali, Breton, Bosnian, Catalan
u'chr': u'chr', u'ckb': u'ckb', u'co': u'co', u'crs': u'crs', u'cs': u'cs', u'cy': u'cy', u'da': u'da', #Cherokee, Kurdish (Sorani), Corsican, Seychellois Creole, Czech, Welsh, Danish
u'de': u'de', u'ee': u'ee', u'el': u'el', u'en': u'en', u'en-gb': u'en-GB', u'en-us': u'en-US', u'eo': u'eo', #German, Ewe, Greek, English, English (UK), English (US), Esperanto
u'es': u'es', u'es-419': u'es-419', u'et': u'et', u'eu': u'eu', u'fa': u'fa', u'fi': u'fi', u'fo': u'fo', #Spanish, Spanish (Latin American), Estonian, Basque, Persian, Finnish, Faroese
u'fr': u'fr', u'fr-ca': u'fr-ca', u'fy': u'fy', u'ga': u'ga', u'gaa': u'gaa', u'gd': u'gd', u'gl': u'gl', #French, French (Canada), Frisian, Irish, Ga, Scots Gaelic, Galician
u'gn': u'gn', u'gu': u'gu', u'ha': u'ha', u'haw': u'haw', u'he': u'he', u'hi': u'hi', u'hr': u'hr', #Guarani, Gujarati, Hausa, Hawaiian, Hebrew, Hindi, Croatian
u'ht': u'ht', u'hu': u'hu', u'hy': u'hy', u'ia': u'ia', u'id': u'id', u'ig': u'ig', u'in': u'in', #Haitian Creole, Hungarian, Armenian, Interlingua, Indonesian, Igbo, in
u'is': u'is', u'it': u'it', u'iw': u'iw', u'ja': u'ja', u'jw': u'jw', u'ka': u'ka', u'kg': u'kg', #Icelandic, Italian, Hebrew, Japanese, Javanese, Georgian, Kongo
u'kk': u'kk', u'km': u'km', u'kn': u'kn', u'ko': u'ko', u'kri': u'kri', u'ku': u'ku', u'ky': u'ky', #Kazakh, Khmer, Kannada, Korean, Krio (Sierra Leone), Kurdish, Kyrgyz
u'la': u'la', u'lg': u'lg', u'ln': u'ln', u'lo': u'lo', u'loz': u'loz', u'lt': u'lt', u'lua': u'lua', #Latin, Luganda, Lingala, Laothian, Lozi, Lithuanian, Tshiluba
u'lv': u'lv', u'mfe': u'mfe', u'mg': u'mg', u'mi': u'mi', u'mk': u'mk', u'ml': u'ml', u'mn': u'mn', #Latvian, Mauritian Creole, Malagasy, Maori, Macedonian, Malayalam, Mongolian
u'mo': u'mo', u'mr': u'mr', u'ms': u'ms', u'mt': u'mt', u'my': u'my', u'ne': u'ne', u'nl': u'nl', #Moldavian, Marathi, Malay, Maltese, Burmese, Nepali, Dutch
u'nn': u'nn', u'no': u'no', u'nso': u'nso', u'ny': u'ny', u'nyn': u'nyn', u'oc': u'oc', u'om': u'om', #Norwegian (Nynorsk), Norwegian, Northern Sotho, Chichewa, Runyakitara, Occitan, Oromo
u'or': u'or', u'pa': u'pa', u'pcm': u'pcm', u'pl': u'pl', u'ps': u'ps', u'pt-br': u'pt-BR', u'pt-pt': u'pt-PT', #Oriya, Punjabi, Nigerian Pidgin, Polish, Pashto, Portuguese (Brazil), Portuguese (Portugal)
u'qu': u'qu', u'rm': u'rm', u'rn': u'rn', u'ro': u'ro', u'ru': u'ru', u'rw': u'rw', u'sd': u'sd', #Quechua, Romansh, Kirundi, Romanian, Russian, Kinyarwanda, Sindhi
u'sh': u'sh', u'si': u'si', u'sk': u'sk', u'sl': u'sl', u'sn': u'sn', u'so': u'so', u'sq': u'sq', #Serbo-Croatian, Sinhalese, Slovak, Slovenian, Shona, Somali, Albanian
u'sr': u'sr', u'sr-me': u'sr-ME', u'st': u'st', u'su': u'su', u'sv': u'sv', u'sw': u'sw', u'ta': u'ta', #Serbian, Montenegrin, Sesotho, Sundanese, Swedish, Swahili, Tamil
u'te': u'te', u'tg': u'tg', u'th': u'th', u'ti': u'ti', u'tk': u'tk', u'tl': u'tl', u'tn': u'tn', #Telugu, Tajik, Thai, Tigrinya, Turkmen, Tagalog, Setswana
u'to': u'to', u'tr': u'tr', u'tt': u'tt', u'tum': u'tum', u'tw': u'tw', u'ug': u'ug', u'uk': u'uk', #Tonga, Turkish, Tatar, Tumbuka, Twi, Uighur, Ukrainian
u'ur': u'ur', u'uz': u'uz', u'vi': u'vi', u'wo': u'wo', u'xh': u'xh', u'yi': u'yi', u'yo': u'yo', #Urdu, Uzbek, Vietnamese, Wolof, Xhosa, Yiddish, Yoruba
u'zh-cn': u'zh-CN', u'zh-hk': u'zh-HK', u'zh-tw': u'zh-TW', u'zu': u'zu', #Chinese (Simplified), Chinese (Hong Kong/Traditional), Chinese (Taiwan/Traditional), Zulu
}