Bluesky app fork with some witchin' additions 💫

Use on-device translation on mobile when available (#9930)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

authored by

DS Boyce
Samuel Newman
and committed by
GitHub
9bbcb472 7c9f05a2

+4620 -608
+1
package.json
··· 85 85 "@braintree/sanitize-url": "^6.0.2", 86 86 "@bsky.app/alf": "^0.1.7", 87 87 "@bsky.app/expo-image-crop-tool": "^0.5.0", 88 + "@bsky.app/expo-translate-text": "^0.2.4", 88 89 "@bsky.app/react-native-mmkv": "2.12.5", 89 90 "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", 90 91 "@emoji-mart/react": "^1.1.1",
+13
src/analytics/metrics/types.ts
··· 2 2 * Do not import runtime code into this file 3 3 */ 4 4 5 + import {type Platform} from 'react-native' 6 + 5 7 import {type NotificationReason} from '#/lib/hooks/useNotificationHandler' 6 8 import {type FeedDescriptor} from '#/state/queries/post-feed' 7 9 import {type LiveEventFeedMetricContext} from '#/features/liveEvents/types' ··· 678 680 sourceLanguages: string[] 679 681 targetLanguage: string 680 682 textLength: number 683 + } 684 + 'translate:result': { 685 + method: 'on-device' | 'google-translate' | 'fallback-alert' 686 + os: Platform['OS'] 687 + sourceLanguage: string | null 688 + targetLanguage: string 689 + } 690 + 'translate:override': { 691 + os: Platform['OS'] 692 + sourceLanguage: string 693 + targetLanguage: string 681 694 } 682 695 683 696 'verification:create': {}
+167
src/components/Post/Translated/index.tsx
··· 1 + import {useMemo} from 'react' 2 + import {Platform, View} from 'react-native' 3 + import {msg} from '@lingui/core/macro' 4 + import {useLingui} from '@lingui/react' 5 + import {Trans} from '@lingui/react/macro' 6 + 7 + import {codeToLanguageName, languageName} from '#/locale/helpers' 8 + import {LANGUAGES} from '#/locale/languages' 9 + import {useLanguagePrefs} from '#/state/preferences' 10 + import {atoms as a, useTheme} from '#/alf' 11 + import {Loader} from '#/components/Loader' 12 + import * as Select from '#/components/Select' 13 + import {Text} from '#/components/Typography' 14 + import {useAnalytics} from '#/analytics' 15 + import {useTranslateOnDevice} from '#/translation' 16 + 17 + export function TranslatedPost({ 18 + postText, 19 + hideLoading = false, 20 + }: { 21 + postText: string 22 + hideLoading: boolean 23 + }) { 24 + const {translationState} = useTranslateOnDevice() 25 + 26 + if (translationState.status === 'loading' && !hideLoading) { 27 + return <TranslationLoading /> 28 + } 29 + 30 + if (translationState.status === 'success') { 31 + return ( 32 + <TranslationResult 33 + postText={postText} 34 + sourceLanguage={translationState.sourceLanguage} 35 + translatedText={translationState.translatedText} 36 + /> 37 + ) 38 + } 39 + 40 + return null 41 + } 42 + 43 + function TranslationLoading() { 44 + const t = useTheme() 45 + 46 + return ( 47 + <View style={[a.flex_row, a.align_center, a.gap_sm, a.py_xs]}> 48 + <Loader size="sm" /> 49 + <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 50 + <Trans>Translating…</Trans> 51 + </Text> 52 + </View> 53 + ) 54 + } 55 + 56 + function TranslationResult({ 57 + postText, 58 + sourceLanguage, 59 + translatedText, 60 + }: { 61 + postText: string 62 + sourceLanguage: string | null 63 + translatedText: string 64 + }) { 65 + const t = useTheme() 66 + const {i18n} = useLingui() 67 + 68 + const langName = sourceLanguage 69 + ? codeToLanguageName(sourceLanguage, i18n.locale) 70 + : undefined 71 + 72 + return ( 73 + <View style={[a.py_xs, a.gap_xs, a.mt_sm]}> 74 + <Text style={[a.text_xs, t.atoms.text_contrast_medium]}> 75 + {langName ? ( 76 + <Trans>Translated from {langName}</Trans> 77 + ) : ( 78 + <Trans>Translated</Trans> 79 + )} 80 + {sourceLanguage != null && ( 81 + <> 82 + <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 83 + {' '} 84 + &middot; 85 + </Text>{' '} 86 + <TranslationLanguageSelect 87 + sourceLanguage={sourceLanguage} 88 + postText={postText} 89 + /> 90 + </> 91 + )} 92 + </Text> 93 + <Text emoji selectable style={[a.text_md, a.leading_snug]}> 94 + {translatedText} 95 + </Text> 96 + </View> 97 + ) 98 + } 99 + 100 + function TranslationLanguageSelect({ 101 + postText, 102 + sourceLanguage, 103 + }: { 104 + postText: string 105 + sourceLanguage: string 106 + }) { 107 + const ax = useAnalytics() 108 + const {_} = useLingui() 109 + const langPrefs = useLanguagePrefs() 110 + const {translate} = useTranslateOnDevice() 111 + 112 + const items = useMemo( 113 + () => 114 + LANGUAGES.filter( 115 + (lang, index, self) => 116 + !langPrefs.primaryLanguage.startsWith(lang.code2) && // Don't show the current language as it would be redundant 117 + index === self.findIndex(t => t.code2 === lang.code2), // Remove dupes (which will happen due to multiple code3 values mapping to the same code2) 118 + ) 119 + .sort( 120 + (a, b) => 121 + languageName(a, langPrefs.appLanguage).localeCompare( 122 + languageName(b, langPrefs.appLanguage), 123 + langPrefs.appLanguage, 124 + ), // Localized sort 125 + ) 126 + .map(l => ({ 127 + label: languageName(l, langPrefs.appLanguage), // The viewer may not be familiar with the source language, so localize the name 128 + value: l.code2, 129 + })), 130 + [langPrefs], 131 + ) 132 + 133 + const handleChangeTranslationLanguage = (sourceLangCode: string) => { 134 + ax.metric('translate:override', { 135 + os: Platform.OS, 136 + sourceLanguage: sourceLangCode, 137 + targetLanguage: langPrefs.primaryLanguage, 138 + }) 139 + void translate(postText, langPrefs.primaryLanguage, sourceLangCode) 140 + } 141 + 142 + return ( 143 + <Select.Root 144 + value={sourceLanguage} 145 + onValueChange={handleChangeTranslationLanguage}> 146 + <Select.Trigger hitSlop={10} label={_(msg`Change source language`)}> 147 + {({props}) => { 148 + return ( 149 + <Text {...props} style={[a.text_xs]}> 150 + <Trans>Edit</Trans> 151 + </Text> 152 + ) 153 + }} 154 + </Select.Trigger> 155 + <Select.Content 156 + label={_(msg`Select the source language`)} 157 + renderItem={({label, value}) => ( 158 + <Select.Item value={value} label={label}> 159 + <Select.ItemIndicator /> 160 + <Select.ItemText>{label}</Select.ItemText> 161 + </Select.Item> 162 + )} 163 + items={items} 164 + /> 165 + </Select.Root> 166 + ) 167 + }
+22 -16
src/components/PostControls/PostMenu/PostMenuItems.tsx
··· 219 219 const onToggleThreadMute = () => { 220 220 try { 221 221 if (isThreadMuted) { 222 - unmuteThread() 222 + void unmuteThread() 223 223 ax.metric('post:unmute', { 224 224 uri: postUri, 225 225 authorDid: postAuthor.did, ··· 228 228 }) 229 229 Toast.show(_(msg`You will now receive notifications for this thread`)) 230 230 } else { 231 - muteThread() 231 + void muteThread() 232 232 ax.metric('post:mute', { 233 233 uri: postUri, 234 234 authorDid: postAuthor.did, ··· 239 239 _(msg`You will no longer receive notifications for this thread`), 240 240 ) 241 241 } 242 - } catch (e: any) { 242 + } catch (err) { 243 + const e = err as Error 243 244 if (e?.name !== 'AbortError') { 244 245 logger.error('Failed to toggle thread mute', {message: e}) 245 246 Toast.show( ··· 253 254 const onCopyPostText = () => { 254 255 const str = richTextToString(richText, true) 255 256 256 - Clipboard.setStringAsync(str) 257 + void Clipboard.setStringAsync(str) 257 258 Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') 258 259 } 259 260 260 261 const onPressTranslate = () => { 261 - translate(record.text, langPrefs.primaryLanguage) 262 + void translate(record.text, langPrefs.primaryLanguage) 262 263 263 264 if ( 264 265 bsky.dangerousIsType<AppBskyFeedPost.Record>( ··· 343 344 ? _(msg`Quote post was successfully detached`) 344 345 : _(msg`Quote post was re-attached`), 345 346 ) 346 - } catch (e: any) { 347 + } catch (err) { 348 + const e = err as Error 347 349 Toast.show( 348 350 _(msg({message: 'Updating quote attachment failed', context: 'toast'})), 349 351 ) ··· 380 382 ? _(msg`Reply was successfully hidden`) 381 383 : _(msg({message: 'Reply visibility updated', context: 'toast'})), 382 384 ) 383 - } catch (e: any) { 385 + } catch (err) { 386 + const e = err as Error 384 387 if (e instanceof MaxHiddenRepliesError) { 385 388 Toast.show( 386 389 _( ··· 409 412 410 413 const onPressPin = () => { 411 414 ax.metric(isPinned ? 'post:unpin' : 'post:pin', {}) 412 - pinPostMutate({ 415 + void pinPostMutate({ 413 416 postUri, 414 417 postCid, 415 418 action: isPinned ? 'unpin' : 'pin', ··· 420 423 try { 421 424 await queueBlock() 422 425 Toast.show(_(msg({message: 'Account blocked', context: 'toast'}))) 423 - } catch (e: any) { 426 + } catch (err) { 427 + const e = err as Error 424 428 if (e?.name !== 'AbortError') { 425 429 logger.error('Failed to block account', {message: e}) 426 430 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') ··· 433 437 try { 434 438 await queueUnmute() 435 439 Toast.show(_(msg({message: 'Account unmuted', context: 'toast'}))) 436 - } catch (e: any) { 440 + } catch (err) { 441 + const e = err as Error 437 442 if (e?.name !== 'AbortError') { 438 443 logger.error('Failed to unmute account', {message: e}) 439 444 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') ··· 443 448 try { 444 449 await queueMute() 445 450 Toast.show(_(msg({message: 'Account muted', context: 'toast'}))) 446 - } catch (e: any) { 451 + } catch (err) { 452 + const e = err as Error 447 453 if (e?.name !== 'AbortError') { 448 454 logger.error('Failed to mute account', {message: e}) 449 455 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') ··· 456 462 const url = `https://docs.google.com/forms/d/e/1FAIpQLSd0QPqhNFksDQf1YyOos7r1ofCLvmrKAH1lU042TaS3GAZaWQ/viewform?entry.1756031717=${toShareUrl( 457 463 href, 458 464 )}` 459 - openLink(url) 465 + void openLink(url) 460 466 } 461 467 462 468 const onSignIn = () => requireSignIn(() => {}) ··· 687 693 ? _(msg`Unmute account`) 688 694 : _(msg`Mute account`) 689 695 } 690 - onPress={onMuteAuthor}> 696 + onPress={() => void onMuteAuthor()}> 691 697 <Menu.ItemText> 692 698 {postAuthor.viewer?.muted 693 699 ? _(msg`Unmute account`) ··· 796 802 description={_( 797 803 msg`This will remove your post from this quote post for all users, and replace it with a placeholder.`, 798 804 )} 799 - onConfirm={onToggleQuotePostAttachment} 805 + onConfirm={() => void onToggleQuotePostAttachment()} 800 806 confirmButtonCta={_(msg`Yes, detach`)} 801 807 /> 802 808 ··· 806 812 description={_( 807 813 msg`This reply will be sorted into a hidden section at the bottom of your thread and will mute notifications for subsequent replies - both for yourself and others.`, 808 814 )} 809 - onConfirm={onToggleReplyVisibility} 815 + onConfirm={() => void onToggleReplyVisibility()} 810 816 confirmButtonCta={_(msg`Yes, hide`)} 811 817 /> 812 818 ··· 816 822 description={_( 817 823 msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, 818 824 )} 819 - onConfirm={onBlockAuthor} 825 + onConfirm={() => void onBlockAuthor()} 820 826 confirmButtonCta={_(msg`Block`)} 821 827 confirmButtonColor="negative" 822 828 />
+2 -1
src/components/Select/index.tsx
··· 70 70 ) 71 71 } 72 72 73 - export function Trigger({children, label}: TriggerProps) { 73 + export function Trigger({children, hitSlop, label}: TriggerProps) { 74 74 const {control} = useSelectContext() 75 75 const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() 76 76 const { ··· 100 100 } else { 101 101 return ( 102 102 <Button 103 + hitSlop={hitSlop} 103 104 label={label} 104 105 onPress={control.open} 105 106 style={[a.flex_1, a.justify_between, a.pl_lg, a.pr_md]}
+2
src/components/Select/types.ts
··· 1 1 import { 2 2 type AccessibilityProps, 3 + type Insets, 3 4 type StyleProp, 4 5 type TextStyle, 5 6 type ViewStyle, ··· 60 61 61 62 export type TriggerProps = { 62 63 children: React.ReactNode | ((props: TriggerChildProps) => React.ReactNode) 64 + hitSlop?: number | Insets | null 63 65 label: string 64 66 } 65 67
+13 -4
src/components/dialogs/LanguageSelectDialog.tsx
··· 5 5 import {useLingui} from '@lingui/react' 6 6 import {Trans} from '@lingui/react/macro' 7 7 8 - import {languageName} from '#/locale/helpers' 9 8 import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages' 10 9 import {useLanguagePrefs} from '#/state/preferences/languages' 11 10 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' ··· 18 17 import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' 19 18 import {Text} from '#/components/Typography' 20 19 import {IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env' 20 + 21 + type FlatListItem = 22 + | { 23 + type: 'header' 24 + label: string 25 + } 26 + | { 27 + type: 'item' 28 + lang: Language 29 + } 21 30 22 31 export function LanguageSelectDialog({ 23 32 titleText, ··· 270 279 ]} 271 280 style={[IS_NATIVE && a.px_lg, IS_WEB && {paddingBottom: 120}]} 272 281 scrollIndicatorInsets={{top: headerHeight, bottom: footerHeight}} 273 - renderItem={({item, index}) => { 282 + renderItem={({item, index}: {item: FlatListItem; index: number}) => { 274 283 if (item.type === 'header') { 275 284 return ( 276 285 <Text ··· 295 304 <Toggle.Item 296 305 key={lang.code2} 297 306 name={lang.code2} 298 - label={languageName(lang, langPrefs.appLanguage)} 307 + label={lang.name} 299 308 style={[ 300 309 t.atoms.border_contrast_low, 301 310 !isLastItem && a.border_b, ··· 304 313 a.py_md, 305 314 ]}> 306 315 <Toggle.LabelText style={[a.flex_1]}> 307 - {languageName(lang, langPrefs.appLanguage)} 316 + {lang.name} 308 317 </Toggle.LabelText> 309 318 <Toggle.Checkbox /> 310 319 </Toggle.Item>
+12 -4
src/lib/hooks/useTranslate.ts
··· 1 1 import {useCallback} from 'react' 2 2 import * as IntentLauncher from 'expo-intent-launcher' 3 3 4 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 4 5 import {getTranslatorLink} from '#/locale/helpers' 5 6 import {IS_ANDROID} from '#/env' 6 - import {useOpenLink} from './useOpenLink' 7 7 8 + /** 9 + * Will always link out to Google Translate. If inline translation is desired, 10 + * use `useTranslateOnDevice` 11 + */ 8 12 export function useTranslate() { 9 13 const openLink = useOpenLink() 10 14 11 15 return useCallback( 12 - async (text: string, language: string) => { 13 - const translateUrl = getTranslatorLink(text, language) 16 + async (text: string, targetLangCode: string, sourceLanguage?: string) => { 17 + const translateUrl = getTranslatorLink( 18 + text, 19 + targetLangCode, 20 + sourceLanguage, 21 + ) 14 22 if (IS_ANDROID) { 15 23 try { 16 - // use getApplicationIconAsync to determine if the translate app is installed 24 + // use `getApplicationIconAsync` to determine if the translate app is installed 17 25 if ( 18 26 !(await IntentLauncher.getApplicationIconAsync( 19 27 'com.google.android.apps.translate',
+6 -2
src/locale/helpers.ts
··· 127 127 return bcp47Match.basicFilter(lang, targetLangs).length > 0 128 128 } 129 129 130 - export function getTranslatorLink(text: string, lang: string): string { 131 - return `https://translate.google.com/?sl=auto&tl=${lang}&text=${encodeURIComponent( 130 + export function getTranslatorLink( 131 + text: string, 132 + targetLangCode: string, 133 + sourceLanguage?: string, 134 + ): string { 135 + return `https://translate.google.com/?sl=${sourceLanguage ?? 'auto'}&tl=${targetLangCode}&text=${encodeURIComponent( 132 136 text, 133 137 )}` 134 138 }
+4011 -498
src/locale/languages.ts
··· 2 2 code3: string 3 3 code2: string 4 4 name: string 5 + en: string 6 + android: boolean 7 + ios: boolean 5 8 } 6 9 7 10 export enum AppLanguage { ··· 100 103 {code2: AppLanguage.zh_HK, name: '粵文 – Cantonese'}, 101 104 ] 102 105 106 + // Pre-generated list using Intl.DisplayNames to localize the language name. 107 + // https://developers.google.com/android/reference/com/google/mlkit/nl/translate/TranslateLanguage 108 + // https://developer.apple.com/documentation/foundation/nslocale/isolanguagecodes 103 109 export const LANGUAGES: Language[] = [ 104 - {code3: 'aar', code2: 'aa', name: 'Afar'}, 105 - {code3: 'abk', code2: 'ab', name: 'Abkhazian'}, 106 - {code3: 'ace', code2: '', name: 'Achinese'}, 107 - {code3: 'ach', code2: '', name: 'Acoli'}, 108 - {code3: 'ada', code2: '', name: 'Adangme'}, 109 - {code3: 'ady', code2: '', name: 'Adyghe; Adygei'}, 110 - {code3: 'afa', code2: '', name: 'Afro-Asiatic languages'}, 111 - {code3: 'afh', code2: '', name: 'Afrihili'}, 112 - {code3: 'afr', code2: 'af', name: 'Afrikaans'}, 113 - {code3: 'ain', code2: '', name: 'Ainu'}, 114 - {code3: 'aka', code2: 'ak', name: 'Akan'}, 115 - {code3: 'akk', code2: '', name: 'Akkadian'}, 116 - {code3: 'alb', code2: 'sq', name: 'Albanian'}, 117 - {code3: 'ale', code2: '', name: 'Aleut'}, 118 - {code3: 'alg', code2: '', name: 'Algonquian languages'}, 119 - {code3: 'alt', code2: '', name: 'Southern Altai'}, 120 - {code3: 'amh', code2: 'am', name: 'Amharic'}, 121 - {code3: 'ang', code2: '', name: 'English, Old (ca.450-1100)'}, 122 - {code3: 'anp', code2: '', name: 'Angika'}, 123 - {code3: 'apa', code2: '', name: 'Apache languages'}, 124 - {code3: 'ara', code2: 'ar', name: 'Arabic'}, 110 + { 111 + code3: 'aar', 112 + code2: 'aa', 113 + name: 'Afar', 114 + en: 'Afar', 115 + android: false, 116 + ios: false, 117 + }, 118 + { 119 + code3: 'abk', 120 + code2: 'ab', 121 + name: 'Abkhazian', 122 + en: 'Abkhazian', 123 + android: false, 124 + ios: false, 125 + }, 126 + { 127 + code3: 'ace', 128 + code2: '', 129 + name: 'Achinese', 130 + en: 'Achinese', 131 + android: false, 132 + ios: false, 133 + }, 134 + { 135 + code3: 'ach', 136 + code2: '', 137 + name: 'Acoli', 138 + en: 'Acoli', 139 + android: false, 140 + ios: false, 141 + }, 142 + { 143 + code3: 'ada', 144 + code2: '', 145 + name: 'Adangme', 146 + en: 'Adangme', 147 + android: false, 148 + ios: false, 149 + }, 150 + { 151 + code3: 'ady', 152 + code2: '', 153 + name: 'Adyghe; Adygei', 154 + en: 'Adyghe; Adygei', 155 + android: false, 156 + ios: false, 157 + }, 158 + { 159 + code3: 'afa', 160 + code2: '', 161 + name: 'Afro-Asiatic languages', 162 + en: 'Afro-Asiatic languages', 163 + android: false, 164 + ios: false, 165 + }, 166 + { 167 + code3: 'afh', 168 + code2: '', 169 + name: 'Afrihili', 170 + en: 'Afrihili', 171 + android: false, 172 + ios: false, 173 + }, 174 + { 175 + code3: 'afr', 176 + code2: 'af', 177 + name: 'Afrikaans', 178 + en: 'Afrikaans', 179 + android: false, 180 + ios: false, 181 + }, 182 + { 183 + code3: 'ain', 184 + code2: '', 185 + name: 'Ainu', 186 + en: 'Ainu', 187 + android: false, 188 + ios: false, 189 + }, 190 + { 191 + code3: 'aka', 192 + code2: 'ak', 193 + name: 'Akan', 194 + en: 'Akan', 195 + android: false, 196 + ios: false, 197 + }, 198 + { 199 + code3: 'akk', 200 + code2: '', 201 + name: 'Akkadian', 202 + en: 'Akkadian', 203 + android: false, 204 + ios: false, 205 + }, 206 + { 207 + code3: 'alb', 208 + code2: 'sq', 209 + name: 'Albanian', 210 + en: 'Albanian', 211 + android: true, 212 + ios: false, 213 + }, 214 + { 215 + code3: 'ale', 216 + code2: '', 217 + name: 'Aleut', 218 + en: 'Aleut', 219 + android: false, 220 + ios: false, 221 + }, 222 + { 223 + code3: 'alg', 224 + code2: '', 225 + name: 'Algonquian languages', 226 + en: 'Algonquian languages', 227 + android: false, 228 + ios: false, 229 + }, 230 + { 231 + code3: 'alt', 232 + code2: '', 233 + name: 'Southern Altai', 234 + en: 'Southern Altai', 235 + android: false, 236 + ios: false, 237 + }, 238 + { 239 + code3: 'amh', 240 + code2: 'am', 241 + name: 'አማርኛ', 242 + en: 'Amharic', 243 + android: false, 244 + ios: false, 245 + }, 246 + { 247 + code3: 'ang', 248 + code2: '', 249 + name: 'English, Old (ca.450-1100)', 250 + en: 'English, Old (ca.450-1100)', 251 + android: false, 252 + ios: false, 253 + }, 254 + { 255 + code3: 'anp', 256 + code2: '', 257 + name: 'Angika', 258 + en: 'Angika', 259 + android: false, 260 + ios: false, 261 + }, 262 + { 263 + code3: 'apa', 264 + code2: '', 265 + name: 'Apache languages', 266 + en: 'Apache languages', 267 + android: false, 268 + ios: false, 269 + }, 270 + { 271 + code3: 'ara', 272 + code2: 'ar', 273 + name: 'العربية', 274 + en: 'Arabic', 275 + android: true, 276 + ios: false, 277 + }, 125 278 { 126 279 code3: 'arc', 127 280 code2: '', 128 281 name: 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)', 282 + en: 'Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)', 283 + android: false, 284 + ios: false, 129 285 }, 130 - {code3: 'arg', code2: 'an', name: 'Aragonese'}, 131 - {code3: 'arm', code2: 'hy', name: 'Armenian'}, 132 - {code3: 'arn', code2: '', name: 'Mapudungun; Mapuche'}, 133 - {code3: 'arp', code2: '', name: 'Arapaho'}, 134 - {code3: 'art', code2: '', name: 'Artificial languages'}, 135 - {code3: 'arw', code2: '', name: 'Arawak'}, 136 - {code3: 'asm', code2: 'as', name: 'Assamese'}, 137 - {code3: 'ast', code2: '', name: 'Asturian; Bable; Leonese; Asturleonese'}, 138 - {code3: 'ath', code2: '', name: 'Athapascan languages'}, 139 - {code3: 'aus', code2: '', name: 'Australian languages'}, 140 - {code3: 'ava', code2: 'av', name: 'Avaric'}, 141 - {code3: 'ave', code2: 'ae', name: 'Avestan'}, 142 - {code3: 'awa', code2: '', name: 'Awadhi'}, 143 - {code3: 'aym', code2: 'ay', name: 'Aymara'}, 144 - {code3: 'aze', code2: 'az', name: 'Azerbaijani'}, 145 - {code3: 'bad', code2: '', name: 'Banda languages'}, 146 - {code3: 'bai', code2: '', name: 'Bamileke languages'}, 147 - {code3: 'bak', code2: 'ba', name: 'Bashkir'}, 148 - {code3: 'bal', code2: '', name: 'Baluchi'}, 149 - {code3: 'bam', code2: 'bm', name: 'Bambara'}, 150 - {code3: 'ban', code2: '', name: 'Balinese'}, 151 - {code3: 'baq', code2: 'eu', name: 'Basque'}, 152 - {code3: 'bas', code2: '', name: 'Basa'}, 153 - {code3: 'bat', code2: '', name: 'Baltic languages'}, 154 - {code3: 'bej', code2: '', name: 'Beja; Bedawiyet'}, 155 - {code3: 'bel', code2: 'be', name: 'Belarusian'}, 156 - {code3: 'bem', code2: '', name: 'Bemba'}, 157 - {code3: 'ben', code2: 'bn', name: 'Bengali'}, 158 - {code3: 'ber', code2: '', name: 'Berber languages'}, 159 - {code3: 'bho', code2: '', name: 'Bhojpuri'}, 160 - {code3: 'bih', code2: 'bh', name: 'Bihari languages'}, 161 - {code3: 'bik', code2: '', name: 'Bikol'}, 162 - {code3: 'bin', code2: '', name: 'Bini; Edo'}, 163 - {code3: 'bis', code2: 'bi', name: 'Bislama'}, 164 - {code3: 'bla', code2: '', name: 'Siksika'}, 165 - {code3: 'bnt', code2: '', name: 'Bantu languages'}, 166 - {code3: 'bod', code2: 'bo', name: 'Tibetan'}, 167 - {code3: 'bos', code2: 'bs', name: 'Bosnian'}, 168 - {code3: 'bra', code2: '', name: 'Braj'}, 169 - {code3: 'bre', code2: 'br', name: 'Breton'}, 170 - {code3: 'btk', code2: '', name: 'Batak languages'}, 171 - {code3: 'bua', code2: '', name: 'Buriat'}, 172 - {code3: 'bug', code2: '', name: 'Buginese'}, 173 - {code3: 'bul', code2: 'bg', name: 'Bulgarian'}, 174 - {code3: 'bur', code2: 'my', name: 'Burmese'}, 175 - {code3: 'byn', code2: '', name: 'Blin; Bilin'}, 176 - {code3: 'cad', code2: '', name: 'Caddo'}, 177 - {code3: 'cai', code2: '', name: 'Central American Indian languages'}, 178 - {code3: 'car', code2: '', name: 'Galibi Carib'}, 179 - {code3: 'cat', code2: 'ca', name: 'Catalan-Valencian'}, 180 - {code3: 'cau', code2: '', name: 'Caucasian languages'}, 181 - {code3: 'ceb', code2: '', name: 'Cebuano'}, 182 - {code3: 'cel', code2: '', name: 'Celtic languages'}, 183 - {code3: 'ces', code2: 'cs', name: 'Czech'}, 184 - {code3: 'cha', code2: 'ch', name: 'Chamorro'}, 185 - {code3: 'chb', code2: '', name: 'Chibcha'}, 186 - {code3: 'che', code2: 'ce', name: 'Chechen'}, 187 - {code3: 'chg', code2: '', name: 'Chagatai'}, 188 - {code3: 'chi', code2: 'zh', name: 'Chinese'}, 189 - {code3: 'chk', code2: '', name: 'Chuukese'}, 190 - {code3: 'chm', code2: '', name: 'Mari'}, 191 - {code3: 'chn', code2: '', name: 'Chinook jargon'}, 192 - {code3: 'cho', code2: '', name: 'Choctaw'}, 193 - {code3: 'chp', code2: '', name: 'Chipewyan; Dene Suline'}, 194 - {code3: 'chr', code2: '', name: 'Cherokee'}, 195 - {code3: 'chu', code2: 'cu', name: 'Church Slavic'}, 196 - {code3: 'chv', code2: 'cv', name: 'Chuvash'}, 197 - {code3: 'chy', code2: '', name: 'Cheyenne'}, 198 - {code3: 'cmc', code2: '', name: 'Chamic languages'}, 199 - {code3: 'cnr', code2: '', name: 'Montenegrin'}, 200 - {code3: 'cop', code2: '', name: 'Coptic'}, 201 - {code3: 'cor', code2: 'kw', name: 'Cornish'}, 202 - {code3: 'cos', code2: 'co', name: 'Corsican'}, 203 - {code3: 'cpe', code2: '', name: 'Creoles and pidgins, English based'}, 204 - {code3: 'cpf', code2: '', name: 'Creoles and pidgins, French-based'}, 205 - {code3: 'cpp', code2: '', name: 'Creoles and pidgins, Portuguese-based'}, 206 - {code3: 'cre', code2: 'cr', name: 'Cree'}, 207 - {code3: 'crh', code2: '', name: 'Crimean Tatar; Crimean Turkish'}, 208 - {code3: 'crp', code2: '', name: 'Creoles and pidgins'}, 209 - {code3: 'csb', code2: '', name: 'Kashubian'}, 210 - {code3: 'cus', code2: '', name: 'Cushitic languages'}, 211 - {code3: 'cym', code2: 'cy', name: 'Welsh'}, 212 - {code3: 'cze', code2: 'cs', name: 'Czech'}, 213 - {code3: 'dak', code2: '', name: 'Dakota'}, 214 - {code3: 'dan', code2: 'da', name: 'Danish'}, 215 - {code3: 'dar', code2: '', name: 'Dargwa'}, 216 - {code3: 'day', code2: '', name: 'Land Dayak languages'}, 217 - {code3: 'del', code2: '', name: 'Delaware'}, 218 - {code3: 'den', code2: '', name: 'Slave (Athapascan)'}, 219 - {code3: 'deu', code2: 'de', name: 'German'}, 220 - {code3: 'dgr', code2: '', name: 'Dogrib'}, 221 - {code3: 'din', code2: '', name: 'Dinka'}, 222 - {code3: 'div', code2: 'dv', name: 'Divehi; Dhivehi; Maldivian'}, 223 - {code3: 'doi', code2: '', name: 'Dogri'}, 224 - {code3: 'dra', code2: '', name: 'Dravidian languages'}, 225 - {code3: 'dsb', code2: '', name: 'Lower Sorbian'}, 226 - {code3: 'dua', code2: '', name: 'Duala'}, 227 - {code3: 'dum', code2: '', name: 'Dutch, Middle (ca.1050-1350)'}, 228 - {code3: 'dut', code2: 'nl', name: 'Dutch; Flemish'}, 229 - {code3: 'dyu', code2: '', name: 'Dyula'}, 230 - {code3: 'dzo', code2: 'dz', name: 'Dzongkha'}, 231 - {code3: 'efi', code2: '', name: 'Efik'}, 232 - {code3: 'egy', code2: '', name: 'Egyptian (Ancient)'}, 233 - {code3: 'eka', code2: '', name: 'Ekajuk'}, 234 - {code3: 'ell', code2: 'el', name: 'Greek'}, 235 - {code3: 'elx', code2: '', name: 'Elamite'}, 236 - {code3: 'eng', code2: 'en', name: 'English'}, 237 - {code3: 'enm', code2: '', name: 'English, Middle (1100-1500)'}, 238 - {code3: 'epo', code2: 'eo', name: 'Esperanto'}, 239 - {code3: 'est', code2: 'et', name: 'Estonian'}, 240 - {code3: 'eus', code2: 'eu', name: 'Basque'}, 241 - {code3: 'ewe', code2: 'ee', name: 'Ewe'}, 242 - {code3: 'ewo', code2: '', name: 'Ewondo'}, 243 - {code3: 'fan', code2: '', name: 'Fang'}, 244 - {code3: 'fao', code2: 'fo', name: 'Faroese'}, 245 - {code3: 'fas', code2: 'fa', name: 'Persian'}, 246 - {code3: 'fat', code2: '', name: 'Fanti'}, 247 - {code3: 'fij', code2: 'fj', name: 'Fijian'}, 248 - {code3: 'fil', code2: '', name: 'Filipino; Pilipino'}, 249 - {code3: 'fin', code2: 'fi', name: 'Finnish'}, 250 - {code3: 'fiu', code2: '', name: 'Finno-Ugrian languages'}, 251 - {code3: 'fon', code2: '', name: 'Fon'}, 252 - {code3: 'fra', code2: 'fr', name: 'French'}, 253 - {code3: 'fre', code2: 'fr', name: 'French'}, 254 - {code3: 'frm', code2: '', name: 'French, Middle (ca.1400-1600)'}, 255 - {code3: 'fro', code2: '', name: 'French, Old (842-ca.1400)'}, 256 - {code3: 'frr', code2: '', name: 'Northern Frisian'}, 257 - {code3: 'frs', code2: '', name: 'Eastern Frisian'}, 258 - {code3: 'fry', code2: 'fy', name: 'Western Frisian'}, 259 - {code3: 'ful', code2: 'ff', name: 'Fulah'}, 260 - {code3: 'fur', code2: '', name: 'Friulian'}, 261 - {code3: 'gaa', code2: '', name: 'Ga'}, 262 - {code3: 'gay', code2: '', name: 'Gayo'}, 263 - {code3: 'gba', code2: '', name: 'Gbaya'}, 264 - {code3: 'gem', code2: '', name: 'Germanic languages'}, 265 - {code3: 'geo', code2: 'ka', name: 'Georgian'}, 266 - {code3: 'ger', code2: 'de', name: 'German'}, 267 - {code3: 'gez', code2: '', name: 'Geez'}, 268 - {code3: 'gil', code2: '', name: 'Gilbertese'}, 269 - {code3: 'gla', code2: 'gd', name: 'Gaelic; Scottish Gaelic'}, 270 - {code3: 'gle', code2: 'ga', name: 'Irish'}, 271 - {code3: 'glg', code2: 'gl', name: 'Galician'}, 272 - {code3: 'glv', code2: 'gv', name: 'Manx'}, 273 - {code3: 'gmh', code2: '', name: 'German, Middle High (ca.1050-1500)'}, 274 - {code3: 'goh', code2: '', name: 'German, Old High (ca.750-1050)'}, 275 - {code3: 'gon', code2: '', name: 'Gondi'}, 276 - {code3: 'gor', code2: '', name: 'Gorontalo'}, 277 - {code3: 'got', code2: '', name: 'Gothic'}, 278 - {code3: 'grb', code2: '', name: 'Grebo'}, 279 - {code3: 'grc', code2: '', name: 'Ancient Greek'}, 280 - {code3: 'gre', code2: 'el', name: 'Greek'}, 281 - {code3: 'grn', code2: 'gn', name: 'Guarani'}, 282 - {code3: 'gsw', code2: '', name: 'Swiss German; Alemannic; Alsatian'}, 283 - {code3: 'guj', code2: 'gu', name: 'Gujarati'}, 284 - {code3: 'gwi', code2: '', name: "Gwich'in"}, 285 - {code3: 'hai', code2: '', name: 'Haida'}, 286 - {code3: 'hat', code2: 'ht', name: 'Haitian; Haitian Creole'}, 287 - {code3: 'hau', code2: 'ha', name: 'Hausa'}, 288 - {code3: 'haw', code2: '', name: 'Hawaiian'}, 289 - {code3: 'heb', code2: 'he', name: 'Hebrew'}, 290 - {code3: 'her', code2: 'hz', name: 'Herero'}, 291 - {code3: 'hil', code2: '', name: 'Hiligaynon'}, 286 + { 287 + code3: 'arg', 288 + code2: 'an', 289 + name: 'Aragonese', 290 + en: 'Aragonese', 291 + android: false, 292 + ios: false, 293 + }, 294 + { 295 + code3: 'arm', 296 + code2: 'hy', 297 + name: 'Armenian', 298 + en: 'Armenian', 299 + android: false, 300 + ios: false, 301 + }, 302 + { 303 + code3: 'arn', 304 + code2: '', 305 + name: 'Mapudungun; Mapuche', 306 + en: 'Mapudungun; Mapuche', 307 + android: false, 308 + ios: false, 309 + }, 310 + { 311 + code3: 'arp', 312 + code2: '', 313 + name: 'Arapaho', 314 + en: 'Arapaho', 315 + android: false, 316 + ios: false, 317 + }, 318 + { 319 + code3: 'art', 320 + code2: '', 321 + name: 'Artificial languages', 322 + en: 'Artificial languages', 323 + android: false, 324 + ios: false, 325 + }, 326 + { 327 + code3: 'arw', 328 + code2: '', 329 + name: 'Arawak', 330 + en: 'Arawak', 331 + android: false, 332 + ios: false, 333 + }, 334 + { 335 + code3: 'asm', 336 + code2: 'as', 337 + name: 'Assamese', 338 + en: 'Assamese', 339 + android: false, 340 + ios: false, 341 + }, 342 + { 343 + code3: 'ast', 344 + code2: '', 345 + name: 'Asturian', 346 + en: 'Asturian', 347 + android: false, 348 + ios: false, 349 + }, 350 + { 351 + code3: 'ath', 352 + code2: '', 353 + name: 'Athapascan languages', 354 + en: 'Athapascan languages', 355 + android: false, 356 + ios: false, 357 + }, 358 + { 359 + code3: 'aus', 360 + code2: '', 361 + name: 'Australian languages', 362 + en: 'Australian languages', 363 + android: false, 364 + ios: false, 365 + }, 366 + { 367 + code3: 'ava', 368 + code2: 'av', 369 + name: 'Avaric', 370 + en: 'Avaric', 371 + android: false, 372 + ios: false, 373 + }, 374 + { 375 + code3: 'ave', 376 + code2: 'ae', 377 + name: 'Avestan', 378 + en: 'Avestan', 379 + android: false, 380 + ios: false, 381 + }, 382 + { 383 + code3: 'awa', 384 + code2: '', 385 + name: 'Awadhi', 386 + en: 'Awadhi', 387 + android: false, 388 + ios: false, 389 + }, 390 + { 391 + code3: 'aym', 392 + code2: 'ay', 393 + name: 'Aymara', 394 + en: 'Aymara', 395 + android: false, 396 + ios: false, 397 + }, 398 + { 399 + code3: 'aze', 400 + code2: 'az', 401 + name: 'azərbaycan', 402 + en: 'Azerbaijani', 403 + android: false, 404 + ios: false, 405 + }, 406 + { 407 + code3: 'bad', 408 + code2: '', 409 + name: 'Banda languages', 410 + en: 'Banda languages', 411 + android: false, 412 + ios: false, 413 + }, 414 + { 415 + code3: 'bai', 416 + code2: '', 417 + name: 'Bamileke languages', 418 + en: 'Bamileke languages', 419 + android: false, 420 + ios: false, 421 + }, 422 + { 423 + code3: 'bak', 424 + code2: 'ba', 425 + name: 'Bashkir', 426 + en: 'Bashkir', 427 + android: false, 428 + ios: false, 429 + }, 430 + { 431 + code3: 'bal', 432 + code2: '', 433 + name: 'Baluchi', 434 + en: 'Baluchi', 435 + android: false, 436 + ios: false, 437 + }, 438 + { 439 + code3: 'bam', 440 + code2: 'bm', 441 + name: 'Bambara', 442 + en: 'Bambara', 443 + android: false, 444 + ios: false, 445 + }, 446 + { 447 + code3: 'ban', 448 + code2: '', 449 + name: 'Balinese', 450 + en: 'Balinese', 451 + android: false, 452 + ios: false, 453 + }, 454 + { 455 + code3: 'baq', 456 + code2: 'eu', 457 + name: 'Basque', 458 + en: 'Basque', 459 + android: false, 460 + ios: false, 461 + }, 462 + { 463 + code3: 'bas', 464 + code2: '', 465 + name: 'Basa', 466 + en: 'Basa', 467 + android: false, 468 + ios: false, 469 + }, 470 + { 471 + code3: 'bat', 472 + code2: '', 473 + name: 'Baltic languages', 474 + en: 'Baltic languages', 475 + android: false, 476 + ios: false, 477 + }, 478 + { 479 + code3: 'bej', 480 + code2: '', 481 + name: 'Beja; Bedawiyet', 482 + en: 'Beja; Bedawiyet', 483 + android: false, 484 + ios: false, 485 + }, 486 + { 487 + code3: 'bel', 488 + code2: 'be', 489 + name: 'Belarusian', 490 + en: 'Belarusian', 491 + android: true, 492 + ios: false, 493 + }, 494 + { 495 + code3: 'bem', 496 + code2: '', 497 + name: 'Bemba', 498 + en: 'Bemba', 499 + android: false, 500 + ios: false, 501 + }, 502 + { 503 + code3: 'ben', 504 + code2: 'bn', 505 + name: 'বাংলা', 506 + en: 'Bangla', 507 + android: true, 508 + ios: false, 509 + }, 510 + { 511 + code3: 'ber', 512 + code2: '', 513 + name: 'Berber languages', 514 + en: 'Berber languages', 515 + android: false, 516 + ios: false, 517 + }, 518 + { 519 + code3: 'bho', 520 + code2: '', 521 + name: 'Bhojpuri', 522 + en: 'Bhojpuri', 523 + android: false, 524 + ios: false, 525 + }, 526 + { 527 + code3: 'bih', 528 + code2: 'bh', 529 + name: 'Bhojpuri', 530 + en: 'Bhojpuri', 531 + android: false, 532 + ios: false, 533 + }, 534 + { 535 + code3: 'bik', 536 + code2: '', 537 + name: 'Bikol', 538 + en: 'Bikol', 539 + android: false, 540 + ios: false, 541 + }, 542 + { 543 + code3: 'bin', 544 + code2: '', 545 + name: 'Bini; Edo', 546 + en: 'Bini; Edo', 547 + android: false, 548 + ios: false, 549 + }, 550 + { 551 + code3: 'bis', 552 + code2: 'bi', 553 + name: 'Bislama', 554 + en: 'Bislama', 555 + android: false, 556 + ios: false, 557 + }, 558 + { 559 + code3: 'bla', 560 + code2: '', 561 + name: 'Siksika', 562 + en: 'Siksika', 563 + android: false, 564 + ios: false, 565 + }, 566 + { 567 + code3: 'bnt', 568 + code2: '', 569 + name: 'Bantu languages', 570 + en: 'Bantu languages', 571 + android: false, 572 + ios: false, 573 + }, 574 + { 575 + code3: 'bod', 576 + code2: 'bo', 577 + name: 'Tibetan', 578 + en: 'Tibetan', 579 + android: false, 580 + ios: false, 581 + }, 582 + { 583 + code3: 'bos', 584 + code2: 'bs', 585 + name: 'bosanski', 586 + en: 'Bosnian', 587 + android: false, 588 + ios: false, 589 + }, 590 + { 591 + code3: 'bra', 592 + code2: '', 593 + name: 'Braj', 594 + en: 'Braj', 595 + android: false, 596 + ios: false, 597 + }, 598 + { 599 + code3: 'bre', 600 + code2: 'br', 601 + name: 'Breton', 602 + en: 'Breton', 603 + android: false, 604 + ios: false, 605 + }, 606 + { 607 + code3: 'btk', 608 + code2: '', 609 + name: 'Batak languages', 610 + en: 'Batak languages', 611 + android: false, 612 + ios: false, 613 + }, 614 + { 615 + code3: 'bua', 616 + code2: '', 617 + name: 'Buriat', 618 + en: 'Buriat', 619 + android: false, 620 + ios: false, 621 + }, 622 + { 623 + code3: 'bug', 624 + code2: '', 625 + name: 'Buginese', 626 + en: 'Buginese', 627 + android: false, 628 + ios: false, 629 + }, 630 + { 631 + code3: 'bul', 632 + code2: 'bg', 633 + name: 'български', 634 + en: 'Bulgarian', 635 + android: true, 636 + ios: false, 637 + }, 638 + { 639 + code3: 'bur', 640 + code2: 'my', 641 + name: 'Burmese', 642 + en: 'Burmese', 643 + android: false, 644 + ios: false, 645 + }, 646 + { 647 + code3: 'byn', 648 + code2: '', 649 + name: 'Blin; Bilin', 650 + en: 'Blin; Bilin', 651 + android: false, 652 + ios: false, 653 + }, 654 + { 655 + code3: 'cad', 656 + code2: '', 657 + name: 'Caddo', 658 + en: 'Caddo', 659 + android: false, 660 + ios: false, 661 + }, 662 + { 663 + code3: 'cai', 664 + code2: '', 665 + name: 'Central American Indian languages', 666 + en: 'Central American Indian languages', 667 + android: false, 668 + ios: false, 669 + }, 670 + { 671 + code3: 'car', 672 + code2: '', 673 + name: 'Galibi Carib', 674 + en: 'Galibi Carib', 675 + android: false, 676 + ios: false, 677 + }, 678 + { 679 + code3: 'cat', 680 + code2: 'ca', 681 + name: 'català', 682 + en: 'Catalan', 683 + android: true, 684 + ios: false, 685 + }, 686 + { 687 + code3: 'cau', 688 + code2: '', 689 + name: 'Caucasian languages', 690 + en: 'Caucasian languages', 691 + android: false, 692 + ios: false, 693 + }, 694 + { 695 + code3: 'ceb', 696 + code2: '', 697 + name: 'Cebuano', 698 + en: 'Cebuano', 699 + android: false, 700 + ios: false, 701 + }, 702 + { 703 + code3: 'cel', 704 + code2: '', 705 + name: 'Celtic languages', 706 + en: 'Celtic languages', 707 + android: false, 708 + ios: false, 709 + }, 710 + { 711 + code3: 'ces', 712 + code2: 'cs', 713 + name: 'čeština', 714 + en: 'Czech', 715 + android: true, 716 + ios: false, 717 + }, 718 + { 719 + code3: 'cha', 720 + code2: 'ch', 721 + name: 'Chamorro', 722 + en: 'Chamorro', 723 + android: false, 724 + ios: false, 725 + }, 726 + { 727 + code3: 'chb', 728 + code2: '', 729 + name: 'Chibcha', 730 + en: 'Chibcha', 731 + android: false, 732 + ios: false, 733 + }, 734 + { 735 + code3: 'che', 736 + code2: 'ce', 737 + name: 'Chechen', 738 + en: 'Chechen', 739 + android: false, 740 + ios: false, 741 + }, 742 + { 743 + code3: 'chg', 744 + code2: '', 745 + name: 'Chagatai', 746 + en: 'Chagatai', 747 + android: false, 748 + ios: false, 749 + }, 750 + { 751 + code3: 'chi', 752 + code2: 'zh', 753 + name: '中文', 754 + en: 'Chinese', 755 + android: true, 756 + ios: false, 757 + }, 758 + { 759 + code3: 'chk', 760 + code2: '', 761 + name: 'Chuukese', 762 + en: 'Chuukese', 763 + android: false, 764 + ios: false, 765 + }, 766 + { 767 + code3: 'chm', 768 + code2: '', 769 + name: 'Mari', 770 + en: 'Mari', 771 + android: false, 772 + ios: false, 773 + }, 774 + { 775 + code3: 'chn', 776 + code2: '', 777 + name: 'Chinook jargon', 778 + en: 'Chinook jargon', 779 + android: false, 780 + ios: false, 781 + }, 782 + { 783 + code3: 'cho', 784 + code2: '', 785 + name: 'Choctaw', 786 + en: 'Choctaw', 787 + android: false, 788 + ios: false, 789 + }, 790 + { 791 + code3: 'chp', 792 + code2: '', 793 + name: 'Chipewyan; Dene Suline', 794 + en: 'Chipewyan; Dene Suline', 795 + android: false, 796 + ios: false, 797 + }, 798 + { 799 + code3: 'chr', 800 + code2: '', 801 + name: 'Cherokee', 802 + en: 'Cherokee', 803 + android: false, 804 + ios: false, 805 + }, 806 + { 807 + code3: 'chu', 808 + code2: 'cu', 809 + name: 'Church Slavic', 810 + en: 'Church Slavic', 811 + android: false, 812 + ios: false, 813 + }, 814 + { 815 + code3: 'chv', 816 + code2: 'cv', 817 + name: 'Chuvash', 818 + en: 'Chuvash', 819 + android: false, 820 + ios: false, 821 + }, 822 + { 823 + code3: 'chy', 824 + code2: '', 825 + name: 'Cheyenne', 826 + en: 'Cheyenne', 827 + android: false, 828 + ios: false, 829 + }, 830 + { 831 + code3: 'cmc', 832 + code2: '', 833 + name: 'Chamic languages', 834 + en: 'Chamic languages', 835 + android: false, 836 + ios: false, 837 + }, 838 + { 839 + code3: 'cnr', 840 + code2: '', 841 + name: 'srpski (Crna Gora)', 842 + en: 'Serbian (Montenegro)', 843 + android: false, 844 + ios: false, 845 + }, 846 + { 847 + code3: 'cop', 848 + code2: '', 849 + name: 'Coptic', 850 + en: 'Coptic', 851 + android: false, 852 + ios: false, 853 + }, 854 + { 855 + code3: 'cor', 856 + code2: 'kw', 857 + name: 'Cornish', 858 + en: 'Cornish', 859 + android: false, 860 + ios: false, 861 + }, 862 + { 863 + code3: 'cos', 864 + code2: 'co', 865 + name: 'Corsican', 866 + en: 'Corsican', 867 + android: false, 868 + ios: false, 869 + }, 870 + { 871 + code3: 'cpe', 872 + code2: '', 873 + name: 'Creoles and pidgins, English based', 874 + en: 'Creoles and pidgins, English based', 875 + android: false, 876 + ios: false, 877 + }, 878 + { 879 + code3: 'cpf', 880 + code2: '', 881 + name: 'Creoles and pidgins, French-based', 882 + en: 'Creoles and pidgins, French-based', 883 + android: false, 884 + ios: false, 885 + }, 886 + { 887 + code3: 'cpp', 888 + code2: '', 889 + name: 'Creoles and pidgins, Portuguese-based', 890 + en: 'Creoles and pidgins, Portuguese-based', 891 + android: false, 892 + ios: false, 893 + }, 894 + { 895 + code3: 'cre', 896 + code2: 'cr', 897 + name: 'Cree', 898 + en: 'Cree', 899 + android: false, 900 + ios: false, 901 + }, 902 + { 903 + code3: 'crh', 904 + code2: '', 905 + name: 'Crimean Tatar; Crimean Turkish', 906 + en: 'Crimean Tatar; Crimean Turkish', 907 + android: false, 908 + ios: false, 909 + }, 910 + { 911 + code3: 'crp', 912 + code2: '', 913 + name: 'Creoles and pidgins', 914 + en: 'Creoles and pidgins', 915 + android: false, 916 + ios: false, 917 + }, 918 + { 919 + code3: 'csb', 920 + code2: '', 921 + name: 'Kashubian', 922 + en: 'Kashubian', 923 + android: false, 924 + ios: false, 925 + }, 926 + { 927 + code3: 'cus', 928 + code2: '', 929 + name: 'Cushitic languages', 930 + en: 'Cushitic languages', 931 + android: false, 932 + ios: false, 933 + }, 934 + { 935 + code3: 'cym', 936 + code2: 'cy', 937 + name: 'Welsh', 938 + en: 'Welsh', 939 + android: true, 940 + ios: false, 941 + }, 942 + { 943 + code3: 'cze', 944 + code2: 'cs', 945 + name: 'čeština', 946 + en: 'Czech', 947 + android: true, 948 + ios: false, 949 + }, 950 + { 951 + code3: 'dak', 952 + code2: '', 953 + name: 'Dakota', 954 + en: 'Dakota', 955 + android: false, 956 + ios: false, 957 + }, 958 + { 959 + code3: 'dan', 960 + code2: 'da', 961 + name: 'dansk', 962 + en: 'Danish', 963 + android: true, 964 + ios: false, 965 + }, 966 + { 967 + code3: 'dar', 968 + code2: '', 969 + name: 'Dargwa', 970 + en: 'Dargwa', 971 + android: false, 972 + ios: false, 973 + }, 974 + { 975 + code3: 'day', 976 + code2: '', 977 + name: 'Land Dayak languages', 978 + en: 'Land Dayak languages', 979 + android: false, 980 + ios: false, 981 + }, 982 + { 983 + code3: 'del', 984 + code2: '', 985 + name: 'Delaware', 986 + en: 'Delaware', 987 + android: false, 988 + ios: false, 989 + }, 990 + { 991 + code3: 'den', 992 + code2: '', 993 + name: 'Slave (Athapascan)', 994 + en: 'Slave (Athapascan)', 995 + android: false, 996 + ios: false, 997 + }, 998 + { 999 + code3: 'deu', 1000 + code2: 'de', 1001 + name: 'Deutsch', 1002 + en: 'German', 1003 + android: true, 1004 + ios: true, 1005 + }, 1006 + { 1007 + code3: 'dgr', 1008 + code2: '', 1009 + name: 'Dogrib', 1010 + en: 'Dogrib', 1011 + android: false, 1012 + ios: false, 1013 + }, 1014 + { 1015 + code3: 'din', 1016 + code2: '', 1017 + name: 'Dinka', 1018 + en: 'Dinka', 1019 + android: false, 1020 + ios: false, 1021 + }, 1022 + { 1023 + code3: 'div', 1024 + code2: 'dv', 1025 + name: 'Divehi', 1026 + en: 'Divehi', 1027 + android: false, 1028 + ios: false, 1029 + }, 1030 + { 1031 + code3: 'doi', 1032 + code2: '', 1033 + name: 'Dogri', 1034 + en: 'Dogri', 1035 + android: false, 1036 + ios: false, 1037 + }, 1038 + { 1039 + code3: 'dra', 1040 + code2: '', 1041 + name: 'Dravidian languages', 1042 + en: 'Dravidian languages', 1043 + android: false, 1044 + ios: false, 1045 + }, 1046 + { 1047 + code3: 'dsb', 1048 + code2: '', 1049 + name: 'Lower Sorbian', 1050 + en: 'Lower Sorbian', 1051 + android: false, 1052 + ios: false, 1053 + }, 1054 + { 1055 + code3: 'dua', 1056 + code2: '', 1057 + name: 'Duala', 1058 + en: 'Duala', 1059 + android: false, 1060 + ios: false, 1061 + }, 1062 + { 1063 + code3: 'dum', 1064 + code2: '', 1065 + name: 'Dutch, Middle (ca.1050-1350)', 1066 + en: 'Dutch, Middle (ca.1050-1350)', 1067 + android: false, 1068 + ios: false, 1069 + }, 1070 + { 1071 + code3: 'dut', 1072 + code2: 'nl', 1073 + name: 'Nederlands', 1074 + en: 'Dutch', 1075 + android: true, 1076 + ios: true, 1077 + }, 1078 + { 1079 + code3: 'dyu', 1080 + code2: '', 1081 + name: 'Dyula', 1082 + en: 'Dyula', 1083 + android: false, 1084 + ios: false, 1085 + }, 1086 + { 1087 + code3: 'dzo', 1088 + code2: 'dz', 1089 + name: 'Dzongkha', 1090 + en: 'Dzongkha', 1091 + android: false, 1092 + ios: false, 1093 + }, 1094 + { 1095 + code3: 'efi', 1096 + code2: '', 1097 + name: 'Efik', 1098 + en: 'Efik', 1099 + android: false, 1100 + ios: false, 1101 + }, 1102 + { 1103 + code3: 'egy', 1104 + code2: '', 1105 + name: 'Egyptian (Ancient)', 1106 + en: 'Egyptian (Ancient)', 1107 + android: false, 1108 + ios: false, 1109 + }, 1110 + { 1111 + code3: 'eka', 1112 + code2: '', 1113 + name: 'Ekajuk', 1114 + en: 'Ekajuk', 1115 + android: false, 1116 + ios: false, 1117 + }, 1118 + { 1119 + code3: 'ell', 1120 + code2: 'el', 1121 + name: 'Ελληνικά', 1122 + en: 'Greek', 1123 + android: true, 1124 + ios: false, 1125 + }, 1126 + { 1127 + code3: 'elx', 1128 + code2: '', 1129 + name: 'Elamite', 1130 + en: 'Elamite', 1131 + android: false, 1132 + ios: false, 1133 + }, 1134 + { 1135 + code3: 'eng', 1136 + code2: 'en', 1137 + name: 'English', 1138 + en: 'English', 1139 + android: true, 1140 + ios: true, 1141 + }, 1142 + { 1143 + code3: 'enm', 1144 + code2: '', 1145 + name: 'English, Middle (1100-1500)', 1146 + en: 'English, Middle (1100-1500)', 1147 + android: false, 1148 + ios: false, 1149 + }, 1150 + { 1151 + code3: 'epo', 1152 + code2: 'eo', 1153 + name: 'Esperanto', 1154 + en: 'Esperanto', 1155 + android: true, 1156 + ios: false, 1157 + }, 1158 + { 1159 + code3: 'est', 1160 + code2: 'et', 1161 + name: 'eesti', 1162 + en: 'Estonian', 1163 + android: true, 1164 + ios: false, 1165 + }, 1166 + { 1167 + code3: 'eus', 1168 + code2: 'eu', 1169 + name: 'Basque', 1170 + en: 'Basque', 1171 + android: false, 1172 + ios: false, 1173 + }, 1174 + { 1175 + code3: 'ewe', 1176 + code2: 'ee', 1177 + name: 'Ewe', 1178 + en: 'Ewe', 1179 + android: false, 1180 + ios: false, 1181 + }, 1182 + { 1183 + code3: 'ewo', 1184 + code2: '', 1185 + name: 'Ewondo', 1186 + en: 'Ewondo', 1187 + android: false, 1188 + ios: false, 1189 + }, 1190 + { 1191 + code3: 'fan', 1192 + code2: '', 1193 + name: 'Fang', 1194 + en: 'Fang', 1195 + android: false, 1196 + ios: false, 1197 + }, 1198 + { 1199 + code3: 'fao', 1200 + code2: 'fo', 1201 + name: 'Faroese', 1202 + en: 'Faroese', 1203 + android: false, 1204 + ios: false, 1205 + }, 1206 + { 1207 + code3: 'fas', 1208 + code2: 'fa', 1209 + name: 'فارسی', 1210 + en: 'Persian', 1211 + android: true, 1212 + ios: false, 1213 + }, 1214 + { 1215 + code3: 'fat', 1216 + code2: '', 1217 + name: 'Akan', 1218 + en: 'Akan', 1219 + android: false, 1220 + ios: false, 1221 + }, 1222 + { 1223 + code3: 'fij', 1224 + code2: 'fj', 1225 + name: 'Fijian', 1226 + en: 'Fijian', 1227 + android: false, 1228 + ios: false, 1229 + }, 1230 + { 1231 + code3: 'fil', 1232 + code2: '', 1233 + name: 'Filipino', 1234 + en: 'Filipino', 1235 + android: false, 1236 + ios: false, 1237 + }, 1238 + { 1239 + code3: 'fin', 1240 + code2: 'fi', 1241 + name: 'suomi', 1242 + en: 'Finnish', 1243 + android: true, 1244 + ios: false, 1245 + }, 1246 + { 1247 + code3: 'fiu', 1248 + code2: '', 1249 + name: 'Finno-Ugrian languages', 1250 + en: 'Finno-Ugrian languages', 1251 + android: false, 1252 + ios: false, 1253 + }, 1254 + { 1255 + code3: 'fon', 1256 + code2: '', 1257 + name: 'Fon', 1258 + en: 'Fon', 1259 + android: false, 1260 + ios: false, 1261 + }, 1262 + { 1263 + code3: 'fra', 1264 + code2: 'fr', 1265 + name: 'français', 1266 + en: 'French', 1267 + android: true, 1268 + ios: true, 1269 + }, 1270 + { 1271 + code3: 'fre', 1272 + code2: 'fr', 1273 + name: 'français', 1274 + en: 'French', 1275 + android: true, 1276 + ios: true, 1277 + }, 1278 + { 1279 + code3: 'frm', 1280 + code2: '', 1281 + name: 'French, Middle (ca.1400-1600)', 1282 + en: 'French, Middle (ca.1400-1600)', 1283 + android: false, 1284 + ios: false, 1285 + }, 1286 + { 1287 + code3: 'fro', 1288 + code2: '', 1289 + name: 'French, Old (842-ca.1400)', 1290 + en: 'French, Old (842-ca.1400)', 1291 + android: false, 1292 + ios: false, 1293 + }, 1294 + { 1295 + code3: 'frr', 1296 + code2: '', 1297 + name: 'Northern Frisian', 1298 + en: 'Northern Frisian', 1299 + android: false, 1300 + ios: false, 1301 + }, 1302 + { 1303 + code3: 'frs', 1304 + code2: '', 1305 + name: 'Eastern Frisian', 1306 + en: 'Eastern Frisian', 1307 + android: false, 1308 + ios: false, 1309 + }, 1310 + { 1311 + code3: 'fry', 1312 + code2: 'fy', 1313 + name: 'Western Frisian', 1314 + en: 'Western Frisian', 1315 + android: false, 1316 + ios: false, 1317 + }, 1318 + { 1319 + code3: 'ful', 1320 + code2: 'ff', 1321 + name: 'Fulah', 1322 + en: 'Fulah', 1323 + android: false, 1324 + ios: false, 1325 + }, 1326 + { 1327 + code3: 'fur', 1328 + code2: '', 1329 + name: 'Friulian', 1330 + en: 'Friulian', 1331 + android: false, 1332 + ios: false, 1333 + }, 1334 + { 1335 + code3: 'gaa', 1336 + code2: '', 1337 + name: 'Ga', 1338 + en: 'Ga', 1339 + android: false, 1340 + ios: false, 1341 + }, 1342 + { 1343 + code3: 'gay', 1344 + code2: '', 1345 + name: 'Gayo', 1346 + en: 'Gayo', 1347 + android: false, 1348 + ios: false, 1349 + }, 1350 + { 1351 + code3: 'gba', 1352 + code2: '', 1353 + name: 'Gbaya', 1354 + en: 'Gbaya', 1355 + android: false, 1356 + ios: false, 1357 + }, 1358 + { 1359 + code3: 'gem', 1360 + code2: '', 1361 + name: 'Germanic languages', 1362 + en: 'Germanic languages', 1363 + android: false, 1364 + ios: false, 1365 + }, 1366 + { 1367 + code3: 'geo', 1368 + code2: 'ka', 1369 + name: 'Georgian', 1370 + en: 'Georgian', 1371 + android: true, 1372 + ios: false, 1373 + }, 1374 + { 1375 + code3: 'ger', 1376 + code2: 'de', 1377 + name: 'Deutsch', 1378 + en: 'German', 1379 + android: true, 1380 + ios: true, 1381 + }, 1382 + { 1383 + code3: 'gez', 1384 + code2: '', 1385 + name: 'Geez', 1386 + en: 'Geez', 1387 + android: false, 1388 + ios: false, 1389 + }, 1390 + { 1391 + code3: 'gil', 1392 + code2: '', 1393 + name: 'Gilbertese', 1394 + en: 'Gilbertese', 1395 + android: false, 1396 + ios: false, 1397 + }, 1398 + { 1399 + code3: 'gla', 1400 + code2: 'gd', 1401 + name: 'Scottish Gaelic', 1402 + en: 'Scottish Gaelic', 1403 + android: false, 1404 + ios: false, 1405 + }, 1406 + { 1407 + code3: 'gle', 1408 + code2: 'ga', 1409 + name: 'Irish', 1410 + en: 'Irish', 1411 + android: true, 1412 + ios: false, 1413 + }, 1414 + { 1415 + code3: 'glg', 1416 + code2: 'gl', 1417 + name: 'Galician', 1418 + en: 'Galician', 1419 + android: true, 1420 + ios: false, 1421 + }, 1422 + { 1423 + code3: 'glv', 1424 + code2: 'gv', 1425 + name: 'Manx', 1426 + en: 'Manx', 1427 + android: false, 1428 + ios: false, 1429 + }, 1430 + { 1431 + code3: 'gmh', 1432 + code2: '', 1433 + name: 'German, Middle High (ca.1050-1500)', 1434 + en: 'German, Middle High (ca.1050-1500)', 1435 + android: false, 1436 + ios: false, 1437 + }, 1438 + { 1439 + code3: 'goh', 1440 + code2: '', 1441 + name: 'German, Old High (ca.750-1050)', 1442 + en: 'German, Old High (ca.750-1050)', 1443 + android: false, 1444 + ios: false, 1445 + }, 1446 + { 1447 + code3: 'gon', 1448 + code2: '', 1449 + name: 'Gondi', 1450 + en: 'Gondi', 1451 + android: false, 1452 + ios: false, 1453 + }, 1454 + { 1455 + code3: 'gor', 1456 + code2: '', 1457 + name: 'Gorontalo', 1458 + en: 'Gorontalo', 1459 + android: false, 1460 + ios: false, 1461 + }, 1462 + { 1463 + code3: 'got', 1464 + code2: '', 1465 + name: 'Gothic', 1466 + en: 'Gothic', 1467 + android: false, 1468 + ios: false, 1469 + }, 1470 + { 1471 + code3: 'grb', 1472 + code2: '', 1473 + name: 'Grebo', 1474 + en: 'Grebo', 1475 + android: false, 1476 + ios: false, 1477 + }, 1478 + { 1479 + code3: 'grc', 1480 + code2: '', 1481 + name: 'Ancient Greek', 1482 + en: 'Ancient Greek', 1483 + android: false, 1484 + ios: false, 1485 + }, 1486 + { 1487 + code3: 'gre', 1488 + code2: 'el', 1489 + name: 'Ελληνικά', 1490 + en: 'Greek', 1491 + android: true, 1492 + ios: false, 1493 + }, 1494 + { 1495 + code3: 'grn', 1496 + code2: 'gn', 1497 + name: 'Guarani', 1498 + en: 'Guarani', 1499 + android: false, 1500 + ios: false, 1501 + }, 1502 + { 1503 + code3: 'gsw', 1504 + code2: '', 1505 + name: 'Swiss German; Alemannic; Alsatian', 1506 + en: 'Swiss German; Alemannic; Alsatian', 1507 + android: false, 1508 + ios: false, 1509 + }, 1510 + { 1511 + code3: 'guj', 1512 + code2: 'gu', 1513 + name: 'ગુજરાતી', 1514 + en: 'Gujarati', 1515 + android: true, 1516 + ios: false, 1517 + }, 1518 + { 1519 + code3: 'gwi', 1520 + code2: '', 1521 + name: "Gwich'in", 1522 + en: "Gwich'in", 1523 + android: false, 1524 + ios: false, 1525 + }, 1526 + { 1527 + code3: 'hai', 1528 + code2: '', 1529 + name: 'Haida', 1530 + en: 'Haida', 1531 + android: false, 1532 + ios: false, 1533 + }, 1534 + { 1535 + code3: 'hat', 1536 + code2: 'ht', 1537 + name: 'Haitian Creole', 1538 + en: 'Haitian Creole', 1539 + android: true, 1540 + ios: false, 1541 + }, 1542 + { 1543 + code3: 'hau', 1544 + code2: 'ha', 1545 + name: 'Hausa', 1546 + en: 'Hausa', 1547 + android: false, 1548 + ios: false, 1549 + }, 1550 + { 1551 + code3: 'haw', 1552 + code2: '', 1553 + name: 'Hawaiian', 1554 + en: 'Hawaiian', 1555 + android: false, 1556 + ios: false, 1557 + }, 1558 + { 1559 + code3: 'heb', 1560 + code2: 'he', 1561 + name: 'עברית', 1562 + en: 'Hebrew', 1563 + android: true, 1564 + ios: false, 1565 + }, 1566 + { 1567 + code3: 'her', 1568 + code2: 'hz', 1569 + name: 'Herero', 1570 + en: 'Herero', 1571 + android: false, 1572 + ios: false, 1573 + }, 1574 + { 1575 + code3: 'hil', 1576 + code2: '', 1577 + name: 'Hiligaynon', 1578 + en: 'Hiligaynon', 1579 + android: false, 1580 + ios: false, 1581 + }, 292 1582 { 293 1583 code3: 'him', 294 1584 code2: '', 295 1585 name: 'Himachali languages; Western Pahari languages', 1586 + en: 'Himachali languages; Western Pahari languages', 1587 + android: false, 1588 + ios: false, 296 1589 }, 297 - {code3: 'hin', code2: 'hi', name: 'Hindi'}, 298 - {code3: 'hit', code2: '', name: 'Hittite'}, 299 - {code3: 'hmn', code2: '', name: 'Hmong; Mong'}, 300 - {code3: 'hmo', code2: 'ho', name: 'Hiri Motu'}, 301 - {code3: 'hrv', code2: 'hr', name: 'Croatian'}, 302 - {code3: 'hsb', code2: '', name: 'Upper Sorbian'}, 303 - {code3: 'hun', code2: 'hu', name: 'Hungarian'}, 304 - {code3: 'hup', code2: '', name: 'Hupa'}, 305 - {code3: 'hye', code2: 'hy', name: 'Armenian'}, 306 - {code3: 'iba', code2: '', name: 'Iban'}, 307 - {code3: 'ibo', code2: 'ig', name: 'Igbo'}, 308 - {code3: 'ice', code2: 'is', name: 'Icelandic'}, 309 - {code3: 'ido', code2: 'io', name: 'Ido'}, 310 - {code3: 'iii', code2: 'ii', name: 'Sichuan Yi; Nuosu'}, 311 - {code3: 'ijo', code2: '', name: 'Ijo languages'}, 312 - {code3: 'iku', code2: 'iu', name: 'Inuktitut'}, 313 - {code3: 'ile', code2: 'ie', name: 'Interlingue'}, 314 - {code3: 'ilo', code2: '', name: 'Iloko'}, 315 - {code3: 'ina', code2: 'ia', name: 'Interlingua'}, 316 - {code3: 'inc', code2: '', name: 'Indic languages'}, 317 - {code3: 'ind', code2: 'id', name: 'Indonesian'}, 318 - {code3: 'ine', code2: '', name: 'Indo-European languages'}, 319 - {code3: 'inh', code2: '', name: 'Ingush'}, 320 - {code3: 'ipk', code2: 'ik', name: 'Inupiaq'}, 321 - {code3: 'ira', code2: '', name: 'Iranian languages'}, 322 - {code3: 'iro', code2: '', name: 'Iroquoian languages'}, 323 - {code3: 'isl', code2: 'is', name: 'Icelandic'}, 324 - {code3: 'ita', code2: 'it', name: 'Italian'}, 325 - {code3: 'jav', code2: 'jv', name: 'Javanese'}, 326 - {code3: 'jbo', code2: '', name: 'Lojban'}, 327 - {code3: 'jpn', code2: 'ja', name: 'Japanese'}, 328 - {code3: 'jpr', code2: '', name: 'Judeo-Persian'}, 329 - {code3: 'jrb', code2: '', name: 'Judeo-Arabic'}, 330 - {code3: 'kaa', code2: '', name: 'Kara-Kalpak'}, 331 - {code3: 'kab', code2: '', name: 'Kabyle'}, 332 - {code3: 'kac', code2: '', name: 'Kachin; Jingpho'}, 333 - {code3: 'kal', code2: 'kl', name: 'Kalaallisut'}, 334 - {code3: 'kam', code2: '', name: 'Kamba'}, 335 - {code3: 'kan', code2: 'kn', name: 'Kannada'}, 336 - {code3: 'kar', code2: '', name: 'Karen languages'}, 337 - {code3: 'kas', code2: 'ks', name: 'Kashmiri'}, 338 - {code3: 'kat', code2: 'ka', name: 'Georgian'}, 339 - {code3: 'kau', code2: 'kr', name: 'Kanuri'}, 340 - {code3: 'kaw', code2: '', name: 'Kawi'}, 341 - {code3: 'kaz', code2: 'kk', name: 'Kazakh'}, 342 - {code3: 'kbd', code2: '', name: 'Kabardian'}, 343 - {code3: 'kha', code2: '', name: 'Khasi'}, 344 - {code3: 'khi', code2: '', name: 'Khoisan languages'}, 345 - {code3: 'khm', code2: 'km', name: 'Central Khmer'}, 346 - {code3: 'kho', code2: '', name: 'Khotanese; Sakan'}, 347 - {code3: 'kik', code2: 'ki', name: 'Kikuyu; Gikuyu'}, 348 - {code3: 'kin', code2: 'rw', name: 'Kinyarwanda'}, 349 - {code3: 'kir', code2: 'ky', name: 'Kirghiz; Kyrgyz'}, 350 - {code3: 'kmb', code2: '', name: 'Kimbundu'}, 351 - {code3: 'kok', code2: '', name: 'Konkani'}, 352 - {code3: 'kom', code2: 'kv', name: 'Komi'}, 353 - {code3: 'kon', code2: 'kg', name: 'Kongo'}, 354 - {code3: 'kor', code2: 'ko', name: 'Korean'}, 355 - {code3: 'kos', code2: '', name: 'Kosraean'}, 356 - {code3: 'kpe', code2: '', name: 'Kpelle'}, 357 - {code3: 'krc', code2: '', name: 'Karachay-Balkar'}, 358 - {code3: 'krl', code2: '', name: 'Karelian'}, 359 - {code3: 'kro', code2: '', name: 'Kru languages'}, 360 - {code3: 'kru', code2: '', name: 'Kurukh'}, 361 - {code3: 'kua', code2: 'kj', name: 'Kuanyama; Kwanyama'}, 362 - {code3: 'kum', code2: '', name: 'Kumyk'}, 363 - {code3: 'kur', code2: 'ku', name: 'Kurdish'}, 364 - {code3: 'kut', code2: '', name: 'Kutenai'}, 365 - {code3: 'lad', code2: '', name: 'Ladino'}, 366 - {code3: 'lah', code2: '', name: 'Lahnda'}, 367 - {code3: 'lam', code2: '', name: 'Lamba'}, 368 - {code3: 'lao', code2: 'lo', name: 'Lao'}, 369 - {code3: 'lat', code2: 'la', name: 'Latin'}, 370 - {code3: 'lav', code2: 'lv', name: 'Latvian'}, 371 - {code3: 'lez', code2: '', name: 'Lezghian'}, 372 - {code3: 'lim', code2: 'li', name: 'Limburgish'}, 373 - {code3: 'lin', code2: 'ln', name: 'Lingala'}, 374 - {code3: 'lit', code2: 'lt', name: 'Lithuanian'}, 375 - {code3: 'lol', code2: '', name: 'Mongo'}, 376 - {code3: 'loz', code2: '', name: 'Lozi'}, 377 - {code3: 'ltz', code2: 'lb', name: 'Luxembourgish'}, 378 - {code3: 'lua', code2: '', name: 'Luba-Lulua'}, 379 - {code3: 'lub', code2: 'lu', name: 'Luba-Katanga'}, 380 - {code3: 'lug', code2: 'lg', name: 'Ganda'}, 381 - {code3: 'lui', code2: '', name: 'Luiseno'}, 382 - {code3: 'lun', code2: '', name: 'Lunda'}, 1590 + { 1591 + code3: 'hin', 1592 + code2: 'hi', 1593 + name: 'हिन्दी', 1594 + en: 'Hindi', 1595 + android: true, 1596 + ios: true, 1597 + }, 1598 + { 1599 + code3: 'hit', 1600 + code2: '', 1601 + name: 'Hittite', 1602 + en: 'Hittite', 1603 + android: false, 1604 + ios: false, 1605 + }, 1606 + { 1607 + code3: 'hmn', 1608 + code2: '', 1609 + name: 'Hmong', 1610 + en: 'Hmong', 1611 + android: false, 1612 + ios: false, 1613 + }, 1614 + { 1615 + code3: 'hmo', 1616 + code2: 'ho', 1617 + name: 'Hiri Motu', 1618 + en: 'Hiri Motu', 1619 + android: false, 1620 + ios: false, 1621 + }, 1622 + { 1623 + code3: 'hrv', 1624 + code2: 'hr', 1625 + name: 'hrvatski', 1626 + en: 'Croatian', 1627 + android: true, 1628 + ios: false, 1629 + }, 1630 + { 1631 + code3: 'hsb', 1632 + code2: '', 1633 + name: 'Upper Sorbian', 1634 + en: 'Upper Sorbian', 1635 + android: false, 1636 + ios: false, 1637 + }, 1638 + { 1639 + code3: 'hun', 1640 + code2: 'hu', 1641 + name: 'magyar', 1642 + en: 'Hungarian', 1643 + android: true, 1644 + ios: false, 1645 + }, 1646 + { 1647 + code3: 'hup', 1648 + code2: '', 1649 + name: 'Hupa', 1650 + en: 'Hupa', 1651 + android: false, 1652 + ios: false, 1653 + }, 1654 + { 1655 + code3: 'hye', 1656 + code2: 'hy', 1657 + name: 'Armenian', 1658 + en: 'Armenian', 1659 + android: false, 1660 + ios: false, 1661 + }, 1662 + { 1663 + code3: 'iba', 1664 + code2: '', 1665 + name: 'Iban', 1666 + en: 'Iban', 1667 + android: false, 1668 + ios: false, 1669 + }, 1670 + { 1671 + code3: 'ibo', 1672 + code2: 'ig', 1673 + name: 'Igbo', 1674 + en: 'Igbo', 1675 + android: false, 1676 + ios: false, 1677 + }, 1678 + { 1679 + code3: 'ice', 1680 + code2: 'is', 1681 + name: 'Icelandic', 1682 + en: 'Icelandic', 1683 + android: true, 1684 + ios: false, 1685 + }, 1686 + { 1687 + code3: 'ido', 1688 + code2: 'io', 1689 + name: 'Ido', 1690 + en: 'Ido', 1691 + android: false, 1692 + ios: false, 1693 + }, 1694 + { 1695 + code3: 'iii', 1696 + code2: 'ii', 1697 + name: 'Sichuan Yi; Nuosu', 1698 + en: 'Sichuan Yi; Nuosu', 1699 + android: false, 1700 + ios: false, 1701 + }, 1702 + { 1703 + code3: 'ijo', 1704 + code2: '', 1705 + name: 'Ijo languages', 1706 + en: 'Ijo languages', 1707 + android: false, 1708 + ios: false, 1709 + }, 1710 + { 1711 + code3: 'iku', 1712 + code2: 'iu', 1713 + name: 'Inuktitut', 1714 + en: 'Inuktitut', 1715 + android: false, 1716 + ios: false, 1717 + }, 1718 + { 1719 + code3: 'ile', 1720 + code2: 'ie', 1721 + name: 'Interlingue', 1722 + en: 'Interlingue', 1723 + android: false, 1724 + ios: false, 1725 + }, 1726 + { 1727 + code3: 'ilo', 1728 + code2: '', 1729 + name: 'Iloko', 1730 + en: 'Iloko', 1731 + android: false, 1732 + ios: false, 1733 + }, 1734 + { 1735 + code3: 'ina', 1736 + code2: 'ia', 1737 + name: 'Interlingua', 1738 + en: 'Interlingua', 1739 + android: false, 1740 + ios: false, 1741 + }, 1742 + { 1743 + code3: 'inc', 1744 + code2: '', 1745 + name: 'Indic languages', 1746 + en: 'Indic languages', 1747 + android: false, 1748 + ios: false, 1749 + }, 1750 + { 1751 + code3: 'ind', 1752 + code2: 'id', 1753 + name: 'Indonesia', 1754 + en: 'Indonesian', 1755 + android: true, 1756 + ios: false, 1757 + }, 1758 + { 1759 + code3: 'ine', 1760 + code2: '', 1761 + name: 'Indo-European languages', 1762 + en: 'Indo-European languages', 1763 + android: false, 1764 + ios: false, 1765 + }, 1766 + { 1767 + code3: 'inh', 1768 + code2: '', 1769 + name: 'Ingush', 1770 + en: 'Ingush', 1771 + android: false, 1772 + ios: false, 1773 + }, 1774 + { 1775 + code3: 'ipk', 1776 + code2: 'ik', 1777 + name: 'Inupiaq', 1778 + en: 'Inupiaq', 1779 + android: false, 1780 + ios: false, 1781 + }, 1782 + { 1783 + code3: 'ira', 1784 + code2: '', 1785 + name: 'Iranian languages', 1786 + en: 'Iranian languages', 1787 + android: false, 1788 + ios: false, 1789 + }, 1790 + { 1791 + code3: 'iro', 1792 + code2: '', 1793 + name: 'Iroquoian languages', 1794 + en: 'Iroquoian languages', 1795 + android: false, 1796 + ios: false, 1797 + }, 1798 + { 1799 + code3: 'isl', 1800 + code2: 'is', 1801 + name: 'Icelandic', 1802 + en: 'Icelandic', 1803 + android: true, 1804 + ios: false, 1805 + }, 1806 + { 1807 + code3: 'ita', 1808 + code2: 'it', 1809 + name: 'italiano', 1810 + en: 'Italian', 1811 + android: true, 1812 + ios: true, 1813 + }, 1814 + { 1815 + code3: 'jav', 1816 + code2: 'jv', 1817 + name: 'Javanese', 1818 + en: 'Javanese', 1819 + android: false, 1820 + ios: false, 1821 + }, 1822 + { 1823 + code3: 'jbo', 1824 + code2: '', 1825 + name: 'Lojban', 1826 + en: 'Lojban', 1827 + android: false, 1828 + ios: false, 1829 + }, 1830 + { 1831 + code3: 'jpn', 1832 + code2: 'ja', 1833 + name: '日本語', 1834 + en: 'Japanese', 1835 + android: true, 1836 + ios: true, 1837 + }, 1838 + { 1839 + code3: 'jpr', 1840 + code2: '', 1841 + name: 'Judeo-Persian', 1842 + en: 'Judeo-Persian', 1843 + android: false, 1844 + ios: false, 1845 + }, 1846 + { 1847 + code3: 'jrb', 1848 + code2: '', 1849 + name: 'Judeo-Arabic', 1850 + en: 'Judeo-Arabic', 1851 + android: false, 1852 + ios: false, 1853 + }, 1854 + { 1855 + code3: 'kaa', 1856 + code2: '', 1857 + name: 'Kara-Kalpak', 1858 + en: 'Kara-Kalpak', 1859 + android: false, 1860 + ios: false, 1861 + }, 1862 + { 1863 + code3: 'kab', 1864 + code2: '', 1865 + name: 'Kabyle', 1866 + en: 'Kabyle', 1867 + android: false, 1868 + ios: false, 1869 + }, 1870 + { 1871 + code3: 'kac', 1872 + code2: '', 1873 + name: 'Kachin; Jingpho', 1874 + en: 'Kachin; Jingpho', 1875 + android: false, 1876 + ios: false, 1877 + }, 1878 + { 1879 + code3: 'kal', 1880 + code2: 'kl', 1881 + name: 'Kalaallisut', 1882 + en: 'Kalaallisut', 1883 + android: false, 1884 + ios: false, 1885 + }, 1886 + { 1887 + code3: 'kam', 1888 + code2: '', 1889 + name: 'Kamba', 1890 + en: 'Kamba', 1891 + android: false, 1892 + ios: false, 1893 + }, 1894 + { 1895 + code3: 'kan', 1896 + code2: 'kn', 1897 + name: 'ಕನ್ನಡ', 1898 + en: 'Kannada', 1899 + android: true, 1900 + ios: false, 1901 + }, 1902 + { 1903 + code3: 'kar', 1904 + code2: '', 1905 + name: 'Karen languages', 1906 + en: 'Karen languages', 1907 + android: false, 1908 + ios: false, 1909 + }, 1910 + { 1911 + code3: 'kas', 1912 + code2: 'ks', 1913 + name: 'Kashmiri', 1914 + en: 'Kashmiri', 1915 + android: false, 1916 + ios: false, 1917 + }, 1918 + { 1919 + code3: 'kat', 1920 + code2: 'ka', 1921 + name: 'Georgian', 1922 + en: 'Georgian', 1923 + android: true, 1924 + ios: false, 1925 + }, 1926 + { 1927 + code3: 'kau', 1928 + code2: 'kr', 1929 + name: 'Kanuri', 1930 + en: 'Kanuri', 1931 + android: false, 1932 + ios: false, 1933 + }, 1934 + { 1935 + code3: 'kaw', 1936 + code2: '', 1937 + name: 'Kawi', 1938 + en: 'Kawi', 1939 + android: false, 1940 + ios: false, 1941 + }, 1942 + { 1943 + code3: 'kaz', 1944 + code2: 'kk', 1945 + name: 'Kazakh', 1946 + en: 'Kazakh', 1947 + android: false, 1948 + ios: false, 1949 + }, 1950 + { 1951 + code3: 'kbd', 1952 + code2: '', 1953 + name: 'Kabardian', 1954 + en: 'Kabardian', 1955 + android: false, 1956 + ios: false, 1957 + }, 1958 + { 1959 + code3: 'kha', 1960 + code2: '', 1961 + name: 'Khasi', 1962 + en: 'Khasi', 1963 + android: false, 1964 + ios: false, 1965 + }, 1966 + { 1967 + code3: 'khi', 1968 + code2: '', 1969 + name: 'Khoisan languages', 1970 + en: 'Khoisan languages', 1971 + android: false, 1972 + ios: false, 1973 + }, 1974 + { 1975 + code3: 'khm', 1976 + code2: 'km', 1977 + name: 'Khmer', 1978 + en: 'Khmer', 1979 + android: false, 1980 + ios: false, 1981 + }, 1982 + { 1983 + code3: 'kho', 1984 + code2: '', 1985 + name: 'Khotanese; Sakan', 1986 + en: 'Khotanese; Sakan', 1987 + android: false, 1988 + ios: false, 1989 + }, 1990 + { 1991 + code3: 'kik', 1992 + code2: 'ki', 1993 + name: 'Kikuyu; Gikuyu', 1994 + en: 'Kikuyu; Gikuyu', 1995 + android: false, 1996 + ios: false, 1997 + }, 1998 + { 1999 + code3: 'kin', 2000 + code2: 'rw', 2001 + name: 'Kinyarwanda', 2002 + en: 'Kinyarwanda', 2003 + android: false, 2004 + ios: false, 2005 + }, 2006 + { 2007 + code3: 'kir', 2008 + code2: 'ky', 2009 + name: 'Kyrgyz', 2010 + en: 'Kyrgyz', 2011 + android: false, 2012 + ios: false, 2013 + }, 2014 + { 2015 + code3: 'kmb', 2016 + code2: '', 2017 + name: 'Kimbundu', 2018 + en: 'Kimbundu', 2019 + android: false, 2020 + ios: false, 2021 + }, 2022 + { 2023 + code3: 'kok', 2024 + code2: '', 2025 + name: 'कोंकणी', 2026 + en: 'Konkani', 2027 + android: false, 2028 + ios: false, 2029 + }, 2030 + { 2031 + code3: 'kom', 2032 + code2: 'kv', 2033 + name: 'Komi', 2034 + en: 'Komi', 2035 + android: false, 2036 + ios: false, 2037 + }, 2038 + { 2039 + code3: 'kon', 2040 + code2: 'kg', 2041 + name: 'Kongo', 2042 + en: 'Kongo', 2043 + android: false, 2044 + ios: false, 2045 + }, 2046 + { 2047 + code3: 'kor', 2048 + code2: 'ko', 2049 + name: '한국어', 2050 + en: 'Korean', 2051 + android: true, 2052 + ios: true, 2053 + }, 2054 + { 2055 + code3: 'kos', 2056 + code2: '', 2057 + name: 'Kosraean', 2058 + en: 'Kosraean', 2059 + android: false, 2060 + ios: false, 2061 + }, 2062 + { 2063 + code3: 'kpe', 2064 + code2: '', 2065 + name: 'Kpelle', 2066 + en: 'Kpelle', 2067 + android: false, 2068 + ios: false, 2069 + }, 2070 + { 2071 + code3: 'krc', 2072 + code2: '', 2073 + name: 'Karachay-Balkar', 2074 + en: 'Karachay-Balkar', 2075 + android: false, 2076 + ios: false, 2077 + }, 2078 + { 2079 + code3: 'krl', 2080 + code2: '', 2081 + name: 'Karelian', 2082 + en: 'Karelian', 2083 + android: false, 2084 + ios: false, 2085 + }, 2086 + { 2087 + code3: 'kro', 2088 + code2: '', 2089 + name: 'Kru languages', 2090 + en: 'Kru languages', 2091 + android: false, 2092 + ios: false, 2093 + }, 2094 + { 2095 + code3: 'kru', 2096 + code2: '', 2097 + name: 'Kurukh', 2098 + en: 'Kurukh', 2099 + android: false, 2100 + ios: false, 2101 + }, 2102 + { 2103 + code3: 'kua', 2104 + code2: 'kj', 2105 + name: 'Kuanyama; Kwanyama', 2106 + en: 'Kuanyama; Kwanyama', 2107 + android: false, 2108 + ios: false, 2109 + }, 2110 + { 2111 + code3: 'kum', 2112 + code2: '', 2113 + name: 'Kumyk', 2114 + en: 'Kumyk', 2115 + android: false, 2116 + ios: false, 2117 + }, 2118 + { 2119 + code3: 'kur', 2120 + code2: 'ku', 2121 + name: 'Kurdish', 2122 + en: 'Kurdish', 2123 + android: false, 2124 + ios: false, 2125 + }, 2126 + { 2127 + code3: 'kut', 2128 + code2: '', 2129 + name: 'Kutenai', 2130 + en: 'Kutenai', 2131 + android: false, 2132 + ios: false, 2133 + }, 2134 + { 2135 + code3: 'lad', 2136 + code2: '', 2137 + name: 'Ladino', 2138 + en: 'Ladino', 2139 + android: false, 2140 + ios: false, 2141 + }, 2142 + { 2143 + code3: 'lah', 2144 + code2: '', 2145 + name: 'Lahnda', 2146 + en: 'Lahnda', 2147 + android: false, 2148 + ios: false, 2149 + }, 2150 + { 2151 + code3: 'lam', 2152 + code2: '', 2153 + name: 'Lamba', 2154 + en: 'Lamba', 2155 + android: false, 2156 + ios: false, 2157 + }, 2158 + { 2159 + code3: 'lao', 2160 + code2: 'lo', 2161 + name: 'Lao', 2162 + en: 'Lao', 2163 + android: false, 2164 + ios: false, 2165 + }, 2166 + { 2167 + code3: 'lat', 2168 + code2: 'la', 2169 + name: 'Latin', 2170 + en: 'Latin', 2171 + android: false, 2172 + ios: false, 2173 + }, 2174 + { 2175 + code3: 'lav', 2176 + code2: 'lv', 2177 + name: 'latviešu', 2178 + en: 'Latvian', 2179 + android: true, 2180 + ios: false, 2181 + }, 2182 + { 2183 + code3: 'lez', 2184 + code2: '', 2185 + name: 'Lezghian', 2186 + en: 'Lezghian', 2187 + android: false, 2188 + ios: false, 2189 + }, 2190 + { 2191 + code3: 'lim', 2192 + code2: 'li', 2193 + name: 'Limburgish', 2194 + en: 'Limburgish', 2195 + android: false, 2196 + ios: false, 2197 + }, 2198 + { 2199 + code3: 'lin', 2200 + code2: 'ln', 2201 + name: 'Lingala', 2202 + en: 'Lingala', 2203 + android: false, 2204 + ios: false, 2205 + }, 2206 + { 2207 + code3: 'lit', 2208 + code2: 'lt', 2209 + name: 'lietuvių', 2210 + en: 'Lithuanian', 2211 + android: true, 2212 + ios: false, 2213 + }, 2214 + { 2215 + code3: 'lol', 2216 + code2: '', 2217 + name: 'Mongo', 2218 + en: 'Mongo', 2219 + android: false, 2220 + ios: false, 2221 + }, 2222 + { 2223 + code3: 'loz', 2224 + code2: '', 2225 + name: 'Lozi', 2226 + en: 'Lozi', 2227 + android: false, 2228 + ios: false, 2229 + }, 2230 + { 2231 + code3: 'ltz', 2232 + code2: 'lb', 2233 + name: 'Luxembourgish', 2234 + en: 'Luxembourgish', 2235 + android: false, 2236 + ios: false, 2237 + }, 2238 + { 2239 + code3: 'lua', 2240 + code2: '', 2241 + name: 'Luba-Lulua', 2242 + en: 'Luba-Lulua', 2243 + android: false, 2244 + ios: false, 2245 + }, 2246 + { 2247 + code3: 'lub', 2248 + code2: 'lu', 2249 + name: 'Luba-Katanga', 2250 + en: 'Luba-Katanga', 2251 + android: false, 2252 + ios: false, 2253 + }, 2254 + { 2255 + code3: 'lug', 2256 + code2: 'lg', 2257 + name: 'Ganda', 2258 + en: 'Ganda', 2259 + android: false, 2260 + ios: false, 2261 + }, 2262 + { 2263 + code3: 'lui', 2264 + code2: '', 2265 + name: 'Luiseno', 2266 + en: 'Luiseno', 2267 + android: false, 2268 + ios: false, 2269 + }, 2270 + { 2271 + code3: 'lun', 2272 + code2: '', 2273 + name: 'Lunda', 2274 + en: 'Lunda', 2275 + android: false, 2276 + ios: false, 2277 + }, 383 2278 { 384 2279 code3: 'luo', 385 2280 code2: '', 386 2281 name: 'Luo (Kenya and Tanzania)', 2282 + en: 'Luo (Kenya and Tanzania)', 2283 + android: false, 2284 + ios: false, 387 2285 }, 388 - {code3: 'lus', code2: '', name: 'Lushai'}, 389 - {code3: 'mac', code2: 'mk', name: 'Macedonian'}, 390 - {code3: 'mad', code2: '', name: 'Madurese'}, 391 - {code3: 'mag', code2: '', name: 'Magahi'}, 392 - {code3: 'mah', code2: 'mh', name: 'Marshallese'}, 393 - {code3: 'mai', code2: '', name: 'Maithili'}, 394 - {code3: 'mak', code2: '', name: 'Makasar'}, 395 - {code3: 'mal', code2: 'ml', name: 'Malayalam'}, 396 - {code3: 'man', code2: '', name: 'Mandingo'}, 397 - {code3: 'mao', code2: 'mi', name: 'Maori'}, 398 - {code3: 'map', code2: '', name: 'Austronesian languages'}, 399 - {code3: 'mar', code2: 'mr', name: 'Marathi'}, 400 - {code3: 'mas', code2: '', name: 'Masai'}, 401 - {code3: 'may', code2: 'ms', name: 'Malay'}, 402 - {code3: 'mdf', code2: '', name: 'Moksha'}, 403 - {code3: 'mdr', code2: '', name: 'Mandar'}, 404 - {code3: 'men', code2: '', name: 'Mende'}, 405 - {code3: 'mga', code2: '', name: 'Irish, Middle (900-1200)'}, 406 - {code3: 'mic', code2: '', name: "Mi'kmaq; Micmac"}, 407 - {code3: 'min', code2: '', name: 'Minangkabau'}, 408 - {code3: 'mis', code2: '', name: 'Uncoded languages'}, 409 - {code3: 'mkd', code2: 'mk', name: 'Macedonian'}, 410 - {code3: 'mkh', code2: '', name: 'Mon-Khmer languages'}, 411 - {code3: 'mlg', code2: 'mg', name: 'Malagasy'}, 412 - {code3: 'mlt', code2: 'mt', name: 'Maltese'}, 413 - {code3: 'mnc', code2: '', name: 'Manchu'}, 414 - {code3: 'mni', code2: '', name: 'Manipuri'}, 415 - {code3: 'mno', code2: '', name: 'Manobo languages'}, 416 - {code3: 'moh', code2: '', name: 'Mohawk'}, 417 - {code3: 'mon', code2: 'mn', name: 'Mongolian'}, 418 - {code3: 'mos', code2: '', name: 'Mossi'}, 419 - {code3: 'mri', code2: 'mi', name: 'Maori'}, 420 - {code3: 'msa', code2: 'ms', name: 'Malay'}, 421 - {code3: 'mul', code2: '', name: 'Multiple languages'}, 422 - {code3: 'mun', code2: '', name: 'Munda languages'}, 423 - {code3: 'mus', code2: '', name: 'Creek'}, 424 - {code3: 'mwl', code2: '', name: 'Mirandese'}, 425 - {code3: 'mwr', code2: '', name: 'Marwari'}, 426 - {code3: 'mya', code2: 'my', name: 'Burmese'}, 427 - {code3: 'myn', code2: '', name: 'Mayan languages'}, 428 - {code3: 'myv', code2: '', name: 'Erzya'}, 429 - {code3: 'nah', code2: '', name: 'Nahuatl languages'}, 430 - {code3: 'nai', code2: '', name: 'North American Indian languages'}, 431 - {code3: 'nap', code2: '', name: 'Neapolitan'}, 432 - {code3: 'nau', code2: 'na', name: 'Nauru'}, 433 - {code3: 'nav', code2: 'nv', name: 'Navajo'}, 434 - {code3: 'nbl', code2: 'nr', name: 'South Ndebele'}, 435 - {code3: 'nde', code2: 'nd', name: 'North Ndebele'}, 436 - {code3: 'ndo', code2: 'ng', name: 'Ndonga'}, 2286 + { 2287 + code3: 'lus', 2288 + code2: '', 2289 + name: 'Mizo', 2290 + en: 'Mizo', 2291 + android: false, 2292 + ios: false, 2293 + }, 2294 + { 2295 + code3: 'mac', 2296 + code2: 'mk', 2297 + name: 'Macedonian', 2298 + en: 'Macedonian', 2299 + android: true, 2300 + ios: false, 2301 + }, 2302 + { 2303 + code3: 'mad', 2304 + code2: '', 2305 + name: 'Madurese', 2306 + en: 'Madurese', 2307 + android: false, 2308 + ios: false, 2309 + }, 2310 + { 2311 + code3: 'mag', 2312 + code2: '', 2313 + name: 'Magahi', 2314 + en: 'Magahi', 2315 + android: false, 2316 + ios: false, 2317 + }, 2318 + { 2319 + code3: 'mah', 2320 + code2: 'mh', 2321 + name: 'Marshallese', 2322 + en: 'Marshallese', 2323 + android: false, 2324 + ios: false, 2325 + }, 2326 + { 2327 + code3: 'mai', 2328 + code2: '', 2329 + name: 'Maithili', 2330 + en: 'Maithili', 2331 + android: false, 2332 + ios: false, 2333 + }, 2334 + { 2335 + code3: 'mak', 2336 + code2: '', 2337 + name: 'Makasar', 2338 + en: 'Makasar', 2339 + android: false, 2340 + ios: false, 2341 + }, 2342 + { 2343 + code3: 'mal', 2344 + code2: 'ml', 2345 + name: 'മലയാളം', 2346 + en: 'Malayalam', 2347 + android: false, 2348 + ios: false, 2349 + }, 2350 + { 2351 + code3: 'man', 2352 + code2: '', 2353 + name: 'Mandingo', 2354 + en: 'Mandingo', 2355 + android: false, 2356 + ios: false, 2357 + }, 2358 + { 2359 + code3: 'mao', 2360 + code2: 'mi', 2361 + name: 'Māori', 2362 + en: 'Māori', 2363 + android: false, 2364 + ios: false, 2365 + }, 2366 + { 2367 + code3: 'map', 2368 + code2: '', 2369 + name: 'Austronesian languages', 2370 + en: 'Austronesian languages', 2371 + android: false, 2372 + ios: false, 2373 + }, 2374 + { 2375 + code3: 'mar', 2376 + code2: 'mr', 2377 + name: 'मराठी', 2378 + en: 'Marathi', 2379 + android: true, 2380 + ios: false, 2381 + }, 2382 + { 2383 + code3: 'mas', 2384 + code2: '', 2385 + name: 'Masai', 2386 + en: 'Masai', 2387 + android: false, 2388 + ios: false, 2389 + }, 2390 + { 2391 + code3: 'may', 2392 + code2: 'ms', 2393 + name: 'Melayu', 2394 + en: 'Malay', 2395 + android: true, 2396 + ios: false, 2397 + }, 2398 + { 2399 + code3: 'mdf', 2400 + code2: '', 2401 + name: 'Moksha', 2402 + en: 'Moksha', 2403 + android: false, 2404 + ios: false, 2405 + }, 2406 + { 2407 + code3: 'mdr', 2408 + code2: '', 2409 + name: 'Mandar', 2410 + en: 'Mandar', 2411 + android: false, 2412 + ios: false, 2413 + }, 2414 + { 2415 + code3: 'men', 2416 + code2: '', 2417 + name: 'Mende', 2418 + en: 'Mende', 2419 + android: false, 2420 + ios: false, 2421 + }, 2422 + { 2423 + code3: 'mga', 2424 + code2: '', 2425 + name: 'Irish, Middle (900-1200)', 2426 + en: 'Irish, Middle (900-1200)', 2427 + android: false, 2428 + ios: false, 2429 + }, 2430 + { 2431 + code3: 'mic', 2432 + code2: '', 2433 + name: "Mi'kmaq; Micmac", 2434 + en: "Mi'kmaq; Micmac", 2435 + android: false, 2436 + ios: false, 2437 + }, 2438 + { 2439 + code3: 'min', 2440 + code2: '', 2441 + name: 'Minangkabau', 2442 + en: 'Minangkabau', 2443 + android: false, 2444 + ios: false, 2445 + }, 2446 + { 2447 + code3: 'mis', 2448 + code2: '', 2449 + name: 'Uncoded languages', 2450 + en: 'Uncoded languages', 2451 + android: false, 2452 + ios: false, 2453 + }, 2454 + { 2455 + code3: 'mkd', 2456 + code2: 'mk', 2457 + name: 'Macedonian', 2458 + en: 'Macedonian', 2459 + android: true, 2460 + ios: false, 2461 + }, 2462 + { 2463 + code3: 'mkh', 2464 + code2: '', 2465 + name: 'Mon-Khmer languages', 2466 + en: 'Mon-Khmer languages', 2467 + android: false, 2468 + ios: false, 2469 + }, 2470 + { 2471 + code3: 'mlg', 2472 + code2: 'mg', 2473 + name: 'Malagasy', 2474 + en: 'Malagasy', 2475 + android: false, 2476 + ios: false, 2477 + }, 2478 + { 2479 + code3: 'mlt', 2480 + code2: 'mt', 2481 + name: 'Maltese', 2482 + en: 'Maltese', 2483 + android: true, 2484 + ios: false, 2485 + }, 2486 + { 2487 + code3: 'mnc', 2488 + code2: '', 2489 + name: 'Manchu', 2490 + en: 'Manchu', 2491 + android: false, 2492 + ios: false, 2493 + }, 2494 + { 2495 + code3: 'mni', 2496 + code2: '', 2497 + name: 'Manipuri', 2498 + en: 'Manipuri', 2499 + android: false, 2500 + ios: false, 2501 + }, 2502 + { 2503 + code3: 'mno', 2504 + code2: '', 2505 + name: 'Manobo languages', 2506 + en: 'Manobo languages', 2507 + android: false, 2508 + ios: false, 2509 + }, 2510 + { 2511 + code3: 'moh', 2512 + code2: '', 2513 + name: 'Mohawk', 2514 + en: 'Mohawk', 2515 + android: false, 2516 + ios: false, 2517 + }, 2518 + { 2519 + code3: 'mon', 2520 + code2: 'mn', 2521 + name: 'Mongolian', 2522 + en: 'Mongolian', 2523 + android: false, 2524 + ios: false, 2525 + }, 2526 + { 2527 + code3: 'mos', 2528 + code2: '', 2529 + name: 'Mossi', 2530 + en: 'Mossi', 2531 + android: false, 2532 + ios: false, 2533 + }, 2534 + { 2535 + code3: 'mri', 2536 + code2: 'mi', 2537 + name: 'Māori', 2538 + en: 'Māori', 2539 + android: false, 2540 + ios: false, 2541 + }, 2542 + { 2543 + code3: 'msa', 2544 + code2: 'ms', 2545 + name: 'Melayu', 2546 + en: 'Malay', 2547 + android: true, 2548 + ios: false, 2549 + }, 2550 + { 2551 + code3: 'mul', 2552 + code2: '', 2553 + name: 'Multiple languages', 2554 + en: 'Multiple languages', 2555 + android: false, 2556 + ios: false, 2557 + }, 2558 + { 2559 + code3: 'mun', 2560 + code2: '', 2561 + name: 'Munda languages', 2562 + en: 'Munda languages', 2563 + android: false, 2564 + ios: false, 2565 + }, 2566 + { 2567 + code3: 'mus', 2568 + code2: '', 2569 + name: 'Creek', 2570 + en: 'Creek', 2571 + android: false, 2572 + ios: false, 2573 + }, 2574 + { 2575 + code3: 'mwl', 2576 + code2: '', 2577 + name: 'Mirandese', 2578 + en: 'Mirandese', 2579 + android: false, 2580 + ios: false, 2581 + }, 2582 + { 2583 + code3: 'mwr', 2584 + code2: '', 2585 + name: 'Marwari', 2586 + en: 'Marwari', 2587 + android: false, 2588 + ios: false, 2589 + }, 2590 + { 2591 + code3: 'mya', 2592 + code2: 'my', 2593 + name: 'Burmese', 2594 + en: 'Burmese', 2595 + android: false, 2596 + ios: false, 2597 + }, 2598 + { 2599 + code3: 'myn', 2600 + code2: '', 2601 + name: 'Mayan languages', 2602 + en: 'Mayan languages', 2603 + android: false, 2604 + ios: false, 2605 + }, 2606 + { 2607 + code3: 'myv', 2608 + code2: '', 2609 + name: 'Erzya', 2610 + en: 'Erzya', 2611 + android: false, 2612 + ios: false, 2613 + }, 2614 + { 2615 + code3: 'nah', 2616 + code2: '', 2617 + name: 'Nahuatl languages', 2618 + en: 'Nahuatl languages', 2619 + android: false, 2620 + ios: false, 2621 + }, 2622 + { 2623 + code3: 'nai', 2624 + code2: '', 2625 + name: 'North American Indian languages', 2626 + en: 'North American Indian languages', 2627 + android: false, 2628 + ios: false, 2629 + }, 2630 + { 2631 + code3: 'nap', 2632 + code2: '', 2633 + name: 'Neapolitan', 2634 + en: 'Neapolitan', 2635 + android: false, 2636 + ios: false, 2637 + }, 2638 + { 2639 + code3: 'nau', 2640 + code2: 'na', 2641 + name: 'Nauru', 2642 + en: 'Nauru', 2643 + android: false, 2644 + ios: false, 2645 + }, 2646 + { 2647 + code3: 'nav', 2648 + code2: 'nv', 2649 + name: 'Navajo', 2650 + en: 'Navajo', 2651 + android: false, 2652 + ios: false, 2653 + }, 2654 + { 2655 + code3: 'nbl', 2656 + code2: 'nr', 2657 + name: 'South Ndebele', 2658 + en: 'South Ndebele', 2659 + android: false, 2660 + ios: false, 2661 + }, 2662 + { 2663 + code3: 'nde', 2664 + code2: 'nd', 2665 + name: 'North Ndebele', 2666 + en: 'North Ndebele', 2667 + android: false, 2668 + ios: false, 2669 + }, 2670 + { 2671 + code3: 'ndo', 2672 + code2: 'ng', 2673 + name: 'Ndonga', 2674 + en: 'Ndonga', 2675 + android: false, 2676 + ios: false, 2677 + }, 437 2678 { 438 2679 code3: 'nds', 439 2680 code2: '', 440 2681 name: 'Low German; Low Saxon; German, Low; Saxon, Low', 2682 + en: 'Low German; Low Saxon; German, Low; Saxon, Low', 2683 + android: false, 2684 + ios: false, 441 2685 }, 442 - {code3: 'nep', code2: 'ne', name: 'Nepali'}, 443 - {code3: 'new', code2: '', name: 'Nepal Bhasa; Newari'}, 444 - {code3: 'nia', code2: '', name: 'Nias'}, 445 - {code3: 'nic', code2: '', name: 'Niger-Kordofanian languages'}, 446 - {code3: 'niu', code2: '', name: 'Niuean'}, 447 - {code3: 'nld', code2: 'nl', name: 'Dutch; Flemish'}, 448 - {code3: 'nno', code2: 'nn', name: 'Norwegian Nynorsk'}, 449 - {code3: 'nob', code2: 'nb', name: 'Norwegian Bokmål'}, 450 - {code3: 'nog', code2: '', name: 'Nogai'}, 451 - {code3: 'non', code2: '', name: 'Norse, Old'}, 452 - {code3: 'nor', code2: 'no', name: 'Norwegian'}, 453 - {code3: 'nqo', code2: '', name: "N'Ko"}, 454 - {code3: 'nso', code2: '', name: 'Pedi; Sepedi; Northern Sotho'}, 455 - {code3: 'nub', code2: '', name: 'Nubian languages'}, 2686 + { 2687 + code3: 'nep', 2688 + code2: 'ne', 2689 + name: 'Nepali', 2690 + en: 'Nepali', 2691 + android: false, 2692 + ios: false, 2693 + }, 2694 + { 2695 + code3: 'new', 2696 + code2: '', 2697 + name: 'Nepal Bhasa; Newari', 2698 + en: 'Nepal Bhasa; Newari', 2699 + android: false, 2700 + ios: false, 2701 + }, 2702 + { 2703 + code3: 'nia', 2704 + code2: '', 2705 + name: 'Nias', 2706 + en: 'Nias', 2707 + android: false, 2708 + ios: false, 2709 + }, 2710 + { 2711 + code3: 'nic', 2712 + code2: '', 2713 + name: 'Niger-Kordofanian languages', 2714 + en: 'Niger-Kordofanian languages', 2715 + android: false, 2716 + ios: false, 2717 + }, 2718 + { 2719 + code3: 'niu', 2720 + code2: '', 2721 + name: 'Niuean', 2722 + en: 'Niuean', 2723 + android: false, 2724 + ios: false, 2725 + }, 2726 + { 2727 + code3: 'nld', 2728 + code2: 'nl', 2729 + name: 'Nederlands', 2730 + en: 'Dutch', 2731 + android: true, 2732 + ios: true, 2733 + }, 2734 + { 2735 + code3: 'nno', 2736 + code2: 'nn', 2737 + name: 'Norwegian Nynorsk', 2738 + en: 'Norwegian Nynorsk', 2739 + android: false, 2740 + ios: false, 2741 + }, 2742 + { 2743 + code3: 'nob', 2744 + code2: 'nb', 2745 + name: 'norsk bokmål', 2746 + en: 'Norwegian Bokmål', 2747 + android: false, 2748 + ios: false, 2749 + }, 2750 + { 2751 + code3: 'nog', 2752 + code2: '', 2753 + name: 'Nogai', 2754 + en: 'Nogai', 2755 + android: false, 2756 + ios: false, 2757 + }, 2758 + { 2759 + code3: 'non', 2760 + code2: '', 2761 + name: 'Norse, Old', 2762 + en: 'Norse, Old', 2763 + android: false, 2764 + ios: false, 2765 + }, 2766 + { 2767 + code3: 'nor', 2768 + code2: 'no', 2769 + name: 'norsk', 2770 + en: 'Norwegian', 2771 + android: true, 2772 + ios: false, 2773 + }, 2774 + { 2775 + code3: 'nqo', 2776 + code2: '', 2777 + name: "N'Ko", 2778 + en: "N'Ko", 2779 + android: false, 2780 + ios: false, 2781 + }, 2782 + { 2783 + code3: 'nso', 2784 + code2: '', 2785 + name: 'Northern Sotho', 2786 + en: 'Northern Sotho', 2787 + android: false, 2788 + ios: false, 2789 + }, 2790 + { 2791 + code3: 'nub', 2792 + code2: '', 2793 + name: 'Nubian languages', 2794 + en: 'Nubian languages', 2795 + android: false, 2796 + ios: false, 2797 + }, 456 2798 { 457 2799 code3: 'nwc', 458 2800 code2: '', 459 2801 name: 'Classical Newari; Old Newari; Classical Nepal Bhasa', 2802 + en: 'Classical Newari; Old Newari; Classical Nepal Bhasa', 2803 + android: false, 2804 + ios: false, 460 2805 }, 461 - {code3: 'nya', code2: 'ny', name: 'Chichewa; Chewa; Nyanja'}, 462 - {code3: 'nym', code2: '', name: 'Nyamwezi'}, 463 - {code3: 'nyn', code2: '', name: 'Nyankole'}, 464 - {code3: 'nyo', code2: '', name: 'Nyoro'}, 465 - {code3: 'nzi', code2: '', name: 'Nzima'}, 466 - {code3: 'oci', code2: 'oc', name: 'Occitan (post 1500)'}, 467 - {code3: 'oji', code2: 'oj', name: 'Ojibwa'}, 468 - {code3: 'ori', code2: 'or', name: 'Oriya'}, 469 - {code3: 'orm', code2: 'om', name: 'Oromo'}, 470 - {code3: 'osa', code2: '', name: 'Osage'}, 471 - {code3: 'oss', code2: 'os', name: 'Ossetic'}, 472 - {code3: 'ota', code2: '', name: 'Turkish, Ottoman (1500-1928)'}, 473 - {code3: 'oto', code2: '', name: 'Otomian languages'}, 474 - {code3: 'paa', code2: '', name: 'Papuan languages'}, 475 - {code3: 'pag', code2: '', name: 'Pangasinan'}, 476 - {code3: 'pal', code2: '', name: 'Pahlavi'}, 477 - {code3: 'pam', code2: '', name: 'Pampanga; Kapampangan'}, 478 - {code3: 'pan', code2: 'pa', name: 'Panjabi; Punjabi'}, 479 - {code3: 'pap', code2: '', name: 'Papiamento'}, 480 - {code3: 'pau', code2: '', name: 'Palauan'}, 481 - {code3: 'peo', code2: '', name: 'Persian, Old (ca.600-400 B.C.)'}, 482 - {code3: 'per', code2: 'fa', name: 'Persian'}, 483 - {code3: 'phi', code2: '', name: 'Philippine languages'}, 484 - {code3: 'phn', code2: '', name: 'Phoenician'}, 485 - {code3: 'pli', code2: 'pi', name: 'Pali'}, 486 - {code3: 'pol', code2: 'pl', name: 'Polish'}, 487 - {code3: 'pon', code2: '', name: 'Pohnpeian'}, 488 - {code3: 'por', code2: 'pt', name: 'Portuguese'}, 489 - {code3: 'pra', code2: '', name: 'Prakrit languages'}, 2806 + { 2807 + code3: 'nya', 2808 + code2: 'ny', 2809 + name: 'Nyanja', 2810 + en: 'Nyanja', 2811 + android: false, 2812 + ios: false, 2813 + }, 2814 + { 2815 + code3: 'nym', 2816 + code2: '', 2817 + name: 'Nyamwezi', 2818 + en: 'Nyamwezi', 2819 + android: false, 2820 + ios: false, 2821 + }, 2822 + { 2823 + code3: 'nyn', 2824 + code2: '', 2825 + name: 'Nyankole', 2826 + en: 'Nyankole', 2827 + android: false, 2828 + ios: false, 2829 + }, 2830 + { 2831 + code3: 'nyo', 2832 + code2: '', 2833 + name: 'Nyoro', 2834 + en: 'Nyoro', 2835 + android: false, 2836 + ios: false, 2837 + }, 2838 + { 2839 + code3: 'nzi', 2840 + code2: '', 2841 + name: 'Nzima', 2842 + en: 'Nzima', 2843 + android: false, 2844 + ios: false, 2845 + }, 2846 + { 2847 + code3: 'oci', 2848 + code2: 'oc', 2849 + name: 'Occitan', 2850 + en: 'Occitan', 2851 + android: false, 2852 + ios: false, 2853 + }, 2854 + { 2855 + code3: 'oji', 2856 + code2: 'oj', 2857 + name: 'Ojibwa', 2858 + en: 'Ojibwa', 2859 + android: false, 2860 + ios: false, 2861 + }, 2862 + { 2863 + code3: 'ori', 2864 + code2: 'or', 2865 + name: 'Odia', 2866 + en: 'Odia', 2867 + android: false, 2868 + ios: false, 2869 + }, 2870 + { 2871 + code3: 'orm', 2872 + code2: 'om', 2873 + name: 'Oromo', 2874 + en: 'Oromo', 2875 + android: false, 2876 + ios: false, 2877 + }, 2878 + { 2879 + code3: 'osa', 2880 + code2: '', 2881 + name: 'Osage', 2882 + en: 'Osage', 2883 + android: false, 2884 + ios: false, 2885 + }, 2886 + { 2887 + code3: 'oss', 2888 + code2: 'os', 2889 + name: 'Ossetic', 2890 + en: 'Ossetic', 2891 + android: false, 2892 + ios: false, 2893 + }, 2894 + { 2895 + code3: 'ota', 2896 + code2: '', 2897 + name: 'Turkish, Ottoman (1500-1928)', 2898 + en: 'Turkish, Ottoman (1500-1928)', 2899 + android: false, 2900 + ios: false, 2901 + }, 2902 + { 2903 + code3: 'oto', 2904 + code2: '', 2905 + name: 'Otomian languages', 2906 + en: 'Otomian languages', 2907 + android: false, 2908 + ios: false, 2909 + }, 2910 + { 2911 + code3: 'paa', 2912 + code2: '', 2913 + name: 'Papuan languages', 2914 + en: 'Papuan languages', 2915 + android: false, 2916 + ios: false, 2917 + }, 2918 + { 2919 + code3: 'pag', 2920 + code2: '', 2921 + name: 'Pangasinan', 2922 + en: 'Pangasinan', 2923 + android: false, 2924 + ios: false, 2925 + }, 2926 + { 2927 + code3: 'pal', 2928 + code2: '', 2929 + name: 'Pahlavi', 2930 + en: 'Pahlavi', 2931 + android: false, 2932 + ios: false, 2933 + }, 2934 + { 2935 + code3: 'pam', 2936 + code2: '', 2937 + name: 'Pampanga; Kapampangan', 2938 + en: 'Pampanga; Kapampangan', 2939 + android: false, 2940 + ios: false, 2941 + }, 2942 + { 2943 + code3: 'pan', 2944 + code2: 'pa', 2945 + name: 'ਪੰਜਾਬੀ', 2946 + en: 'Punjabi', 2947 + android: false, 2948 + ios: false, 2949 + }, 2950 + { 2951 + code3: 'pap', 2952 + code2: '', 2953 + name: 'Papiamento', 2954 + en: 'Papiamento', 2955 + android: false, 2956 + ios: false, 2957 + }, 2958 + { 2959 + code3: 'pau', 2960 + code2: '', 2961 + name: 'Palauan', 2962 + en: 'Palauan', 2963 + android: false, 2964 + ios: false, 2965 + }, 2966 + { 2967 + code3: 'peo', 2968 + code2: '', 2969 + name: 'Persian, Old (ca.600-400 B.C.)', 2970 + en: 'Persian, Old (ca.600-400 B.C.)', 2971 + android: false, 2972 + ios: false, 2973 + }, 2974 + { 2975 + code3: 'per', 2976 + code2: 'fa', 2977 + name: 'فارسی', 2978 + en: 'Persian', 2979 + android: true, 2980 + ios: false, 2981 + }, 2982 + { 2983 + code3: 'phi', 2984 + code2: '', 2985 + name: 'Philippine languages', 2986 + en: 'Philippine languages', 2987 + android: false, 2988 + ios: false, 2989 + }, 2990 + { 2991 + code3: 'phn', 2992 + code2: '', 2993 + name: 'Phoenician', 2994 + en: 'Phoenician', 2995 + android: false, 2996 + ios: false, 2997 + }, 2998 + { 2999 + code3: 'pli', 3000 + code2: 'pi', 3001 + name: 'Pali', 3002 + en: 'Pali', 3003 + android: false, 3004 + ios: false, 3005 + }, 3006 + { 3007 + code3: 'pol', 3008 + code2: 'pl', 3009 + name: 'polski', 3010 + en: 'Polish', 3011 + android: true, 3012 + ios: true, 3013 + }, 3014 + { 3015 + code3: 'pon', 3016 + code2: '', 3017 + name: 'Pohnpeian', 3018 + en: 'Pohnpeian', 3019 + android: false, 3020 + ios: false, 3021 + }, 3022 + { 3023 + code3: 'por', 3024 + code2: 'pt', 3025 + name: 'português', 3026 + en: 'Portuguese', 3027 + android: true, 3028 + ios: true, 3029 + }, 3030 + { 3031 + code3: 'pra', 3032 + code2: '', 3033 + name: 'Prakrit languages', 3034 + en: 'Prakrit languages', 3035 + android: false, 3036 + ios: false, 3037 + }, 490 3038 { 491 3039 code3: 'pro', 492 3040 code2: '', 493 3041 name: 'Provençal, Old (to 1500);Occitan, Old (to 1500)', 3042 + en: 'Provençal, Old (to 1500);Occitan, Old (to 1500)', 3043 + android: false, 3044 + ios: false, 494 3045 }, 495 - {code3: 'pus', code2: 'ps', name: 'Pushto; Pashto'}, 496 - {code3: 'que', code2: 'qu', name: 'Quechua'}, 497 - {code3: 'raj', code2: '', name: 'Rajasthani'}, 498 - {code3: 'rap', code2: '', name: 'Rapanui'}, 499 - {code3: 'rar', code2: '', name: 'Rarotongan; Cook Islands Maori'}, 500 - {code3: 'roa', code2: '', name: 'Romance languages'}, 501 - {code3: 'roh', code2: 'rm', name: 'Romansh'}, 502 - {code3: 'rom', code2: '', name: 'Romany'}, 503 - {code3: 'rum', code2: 'ro', name: 'Romanian'}, 504 - {code3: 'ron', code2: 'ro', name: 'Romanian'}, 505 - {code3: 'run', code2: 'rn', name: 'Rundi'}, 506 - {code3: 'rup', code2: '', name: 'Aromanian; Arumanian; Macedo-Romanian'}, 507 - {code3: 'rus', code2: 'ru', name: 'Russian'}, 508 - {code3: 'sad', code2: '', name: 'Sandawe'}, 509 - {code3: 'sag', code2: 'sg', name: 'Sango'}, 510 - {code3: 'sah', code2: '', name: 'Yakut'}, 511 - {code3: 'sai', code2: '', name: 'South American Indian languages'}, 512 - {code3: 'sal', code2: '', name: 'Salishan languages'}, 513 - {code3: 'sam', code2: '', name: 'Samaritan Aramaic'}, 514 - {code3: 'san', code2: 'sa', name: 'Sanskrit'}, 515 - {code3: 'sas', code2: '', name: 'Sasak'}, 516 - {code3: 'sat', code2: '', name: 'Santali'}, 517 - {code3: 'scn', code2: '', name: 'Sicilian'}, 518 - {code3: 'sco', code2: '', name: 'Scots'}, 519 - {code3: 'sel', code2: '', name: 'Selkup'}, 520 - {code3: 'sem', code2: '', name: 'Semitic languages'}, 521 - {code3: 'sga', code2: '', name: 'Irish, Old (to 900)'}, 522 - {code3: 'sgn', code2: '', name: 'Sign Languages'}, 523 - {code3: 'shn', code2: '', name: 'Shan'}, 524 - {code3: 'sid', code2: '', name: 'Sidamo'}, 525 - {code3: 'sin', code2: 'si', name: 'Sinhala; Sinhalese'}, 526 - {code3: 'sio', code2: '', name: 'Siouan languages'}, 527 - {code3: 'sit', code2: '', name: 'Sino-Tibetan languages'}, 528 - {code3: 'sla', code2: '', name: 'Slavic languages'}, 529 - {code3: 'slo', code2: 'sk', name: 'Slovak'}, 530 - {code3: 'slk', code2: 'sk', name: 'Slovak'}, 531 - {code3: 'slv', code2: 'sl', name: 'Slovenian'}, 532 - {code3: 'sma', code2: '', name: 'Southern Sami'}, 533 - {code3: 'sme', code2: 'se', name: 'Northern Sami'}, 534 - {code3: 'smi', code2: '', name: 'Sami languages'}, 535 - {code3: 'smj', code2: '', name: 'Lule Sami'}, 536 - {code3: 'smn', code2: '', name: 'Inari Sami'}, 537 - {code3: 'smo', code2: 'sm', name: 'Samoan'}, 538 - {code3: 'sms', code2: '', name: 'Skolt Sami'}, 539 - {code3: 'sna', code2: 'sn', name: 'Shona'}, 540 - {code3: 'snd', code2: 'sd', name: 'Sindhi'}, 541 - {code3: 'snk', code2: '', name: 'Soninke'}, 542 - {code3: 'sog', code2: '', name: 'Sogdian'}, 543 - {code3: 'som', code2: 'so', name: 'Somali'}, 544 - {code3: 'son', code2: '', name: 'Songhai languages'}, 545 - {code3: 'sot', code2: 'st', name: 'Sotho, Southern'}, 546 - {code3: 'spa', code2: 'es', name: 'Spanish'}, 547 - {code3: 'sqi', code2: 'sq', name: 'Albanian'}, 548 - {code3: 'srd', code2: 'sc', name: 'Sardinian'}, 549 - {code3: 'srn', code2: '', name: 'Sranan Tongo'}, 550 - {code3: 'srp', code2: 'sr', name: 'Serbian'}, 551 - {code3: 'srr', code2: '', name: 'Serer'}, 552 - {code3: 'ssa', code2: '', name: 'Nilo-Saharan languages'}, 553 - {code3: 'ssw', code2: 'ss', name: 'Swati'}, 554 - {code3: 'suk', code2: '', name: 'Sukuma'}, 555 - {code3: 'sun', code2: 'su', name: 'Sundanese'}, 556 - {code3: 'sus', code2: '', name: 'Susu'}, 557 - {code3: 'sux', code2: '', name: 'Sumerian'}, 558 - {code3: 'swa', code2: 'sw', name: 'Swahili'}, 559 - {code3: 'swe', code2: 'sv', name: 'Swedish'}, 560 - {code3: 'syc', code2: '', name: 'Classical Syriac'}, 561 - {code3: 'syr', code2: '', name: 'Syriac'}, 562 - {code3: 'tah', code2: 'ty', name: 'Tahitian'}, 563 - {code3: 'tai', code2: '', name: 'Tai languages'}, 564 - {code3: 'tam', code2: 'ta', name: 'Tamil'}, 565 - {code3: 'tat', code2: 'tt', name: 'Tatar'}, 566 - {code3: 'tel', code2: 'te', name: 'Telugu'}, 567 - {code3: 'tem', code2: '', name: 'Timne'}, 568 - {code3: 'ter', code2: '', name: 'Tereno'}, 569 - {code3: 'tet', code2: '', name: 'Tetum'}, 570 - {code3: 'tgk', code2: 'tg', name: 'Tajik'}, 571 - {code3: 'tgl', code2: 'tl', name: 'Tagalog'}, 572 - {code3: 'tha', code2: 'th', name: 'Thai'}, 573 - {code3: 'tib', code2: 'bo', name: 'Tibetan'}, 574 - {code3: 'tig', code2: '', name: 'Tigre'}, 575 - {code3: 'tir', code2: 'ti', name: 'Tigrinya'}, 576 - {code3: 'tiv', code2: '', name: 'Tiv'}, 577 - {code3: 'tkl', code2: '', name: 'Tokelau'}, 578 - {code3: 'tlh', code2: '', name: 'Klingon; tlhIngan-Hol'}, 579 - {code3: 'tli', code2: '', name: 'Tlingit'}, 580 - {code3: 'tmh', code2: '', name: 'Tamashek'}, 581 - {code3: 'tog', code2: '', name: 'Tonga (Nyasa)'}, 582 - {code3: 'ton', code2: 'to', name: 'Tonga (Tonga Islands)'}, 583 - {code3: 'tpi', code2: '', name: 'Tok Pisin'}, 584 - {code3: 'tsi', code2: '', name: 'Tsimshian'}, 585 - {code3: 'tsn', code2: 'tn', name: 'Tswana'}, 586 - {code3: 'tso', code2: 'ts', name: 'Tsonga'}, 587 - {code3: 'tuk', code2: 'tk', name: 'Turkmen'}, 588 - {code3: 'tum', code2: '', name: 'Tumbuka'}, 589 - {code3: 'tup', code2: '', name: 'Tupi languages'}, 590 - {code3: 'tur', code2: 'tr', name: 'Turkish'}, 591 - {code3: 'tut', code2: '', name: 'Altaic languages'}, 592 - {code3: 'tvl', code2: '', name: 'Tuvalu'}, 593 - {code3: 'twi', code2: 'tw', name: 'Twi'}, 594 - {code3: 'tyv', code2: '', name: 'Tuvinian'}, 595 - {code3: 'udm', code2: '', name: 'Udmurt'}, 596 - {code3: 'uga', code2: '', name: 'Ugaritic'}, 597 - {code3: 'uig', code2: 'ug', name: 'Uighur; Uyghur'}, 598 - {code3: 'ukr', code2: 'uk', name: 'Ukrainian'}, 599 - {code3: 'umb', code2: '', name: 'Umbundu'}, 600 - {code3: 'und', code2: '', name: 'Undetermined'}, 601 - {code3: 'urd', code2: 'ur', name: 'Urdu'}, 602 - {code3: 'uzb', code2: 'uz', name: 'Uzbek'}, 603 - {code3: 'vai', code2: '', name: 'Vai'}, 604 - {code3: 'ven', code2: 've', name: 'Venda'}, 605 - {code3: 'vie', code2: 'vi', name: 'Vietnamese'}, 606 - {code3: 'vol', code2: 'vo', name: 'Volapük'}, 607 - {code3: 'vot', code2: '', name: 'Votic'}, 608 - {code3: 'wak', code2: '', name: 'Wakashan languages'}, 609 - {code3: 'wal', code2: '', name: 'Wolaitta; Wolaytta'}, 610 - {code3: 'war', code2: '', name: 'Waray'}, 611 - {code3: 'was', code2: '', name: 'Washo'}, 612 - {code3: 'wel', code2: 'cy', name: 'Welsh'}, 613 - {code3: 'wen', code2: '', name: 'Sorbian languages'}, 614 - {code3: 'wln', code2: 'wa', name: 'Walloon'}, 615 - {code3: 'wol', code2: 'wo', name: 'Wolof'}, 616 - {code3: 'xal', code2: '', name: 'Kalmyk; Oirat'}, 617 - {code3: 'xho', code2: 'xh', name: 'Xhosa'}, 618 - {code3: 'yao', code2: '', name: 'Yao'}, 619 - {code3: 'yap', code2: '', name: 'Yapese'}, 620 - {code3: 'yid', code2: 'yi', name: 'Yiddish'}, 621 - {code3: 'yor', code2: 'yo', name: 'Yoruba'}, 622 - {code3: 'ypk', code2: '', name: 'Yupik languages'}, 623 - {code3: 'zap', code2: '', name: 'Zapotec'}, 624 - {code3: 'zbl', code2: '', name: 'Blissymbols; Blissymbolics; Bliss'}, 625 - {code3: 'zen', code2: '', name: 'Zenaga'}, 626 - {code3: 'zgh', code2: '', name: 'Standard Moroccan Tamazight'}, 627 - {code3: 'zha', code2: 'za', name: 'Zhuang; Chuang'}, 628 - {code3: 'zho', code2: 'zh', name: 'Chinese'}, 629 - {code3: 'znd', code2: '', name: 'Zande languages'}, 630 - {code3: 'zul', code2: 'zu', name: 'Zulu'}, 631 - {code3: 'zun', code2: '', name: 'Zuni'}, 3046 + { 3047 + code3: 'pus', 3048 + code2: 'ps', 3049 + name: 'Pashto', 3050 + en: 'Pashto', 3051 + android: false, 3052 + ios: false, 3053 + }, 3054 + { 3055 + code3: 'que', 3056 + code2: 'qu', 3057 + name: 'Quechua', 3058 + en: 'Quechua', 3059 + android: false, 3060 + ios: false, 3061 + }, 3062 + { 3063 + code3: 'raj', 3064 + code2: '', 3065 + name: 'Rajasthani', 3066 + en: 'Rajasthani', 3067 + android: false, 3068 + ios: false, 3069 + }, 3070 + { 3071 + code3: 'rap', 3072 + code2: '', 3073 + name: 'Rapanui', 3074 + en: 'Rapanui', 3075 + android: false, 3076 + ios: false, 3077 + }, 3078 + { 3079 + code3: 'rar', 3080 + code2: '', 3081 + name: 'Rarotongan; Cook Islands Maori', 3082 + en: 'Rarotongan; Cook Islands Maori', 3083 + android: false, 3084 + ios: false, 3085 + }, 3086 + { 3087 + code3: 'roa', 3088 + code2: '', 3089 + name: 'Romance languages', 3090 + en: 'Romance languages', 3091 + android: false, 3092 + ios: false, 3093 + }, 3094 + { 3095 + code3: 'roh', 3096 + code2: 'rm', 3097 + name: 'Romansh', 3098 + en: 'Romansh', 3099 + android: false, 3100 + ios: false, 3101 + }, 3102 + { 3103 + code3: 'rom', 3104 + code2: '', 3105 + name: 'Romany', 3106 + en: 'Romany', 3107 + android: false, 3108 + ios: false, 3109 + }, 3110 + { 3111 + code3: 'rum', 3112 + code2: 'ro', 3113 + name: 'română', 3114 + en: 'Romanian', 3115 + android: true, 3116 + ios: false, 3117 + }, 3118 + { 3119 + code3: 'ron', 3120 + code2: 'ro', 3121 + name: 'română', 3122 + en: 'Romanian', 3123 + android: true, 3124 + ios: false, 3125 + }, 3126 + { 3127 + code3: 'run', 3128 + code2: 'rn', 3129 + name: 'Rundi', 3130 + en: 'Rundi', 3131 + android: false, 3132 + ios: false, 3133 + }, 3134 + { 3135 + code3: 'rup', 3136 + code2: '', 3137 + name: 'Aromanian; Arumanian; Macedo-Romanian', 3138 + en: 'Aromanian; Arumanian; Macedo-Romanian', 3139 + android: false, 3140 + ios: false, 3141 + }, 3142 + { 3143 + code3: 'rus', 3144 + code2: 'ru', 3145 + name: 'русский', 3146 + en: 'Russian', 3147 + android: true, 3148 + ios: true, 3149 + }, 3150 + { 3151 + code3: 'sad', 3152 + code2: '', 3153 + name: 'Sandawe', 3154 + en: 'Sandawe', 3155 + android: false, 3156 + ios: false, 3157 + }, 3158 + { 3159 + code3: 'sag', 3160 + code2: 'sg', 3161 + name: 'Sango', 3162 + en: 'Sango', 3163 + android: false, 3164 + ios: false, 3165 + }, 3166 + { 3167 + code3: 'sah', 3168 + code2: '', 3169 + name: 'Yakut', 3170 + en: 'Yakut', 3171 + android: false, 3172 + ios: false, 3173 + }, 3174 + { 3175 + code3: 'sai', 3176 + code2: '', 3177 + name: 'South American Indian languages', 3178 + en: 'South American Indian languages', 3179 + android: false, 3180 + ios: false, 3181 + }, 3182 + { 3183 + code3: 'sal', 3184 + code2: '', 3185 + name: 'Salishan languages', 3186 + en: 'Salishan languages', 3187 + android: false, 3188 + ios: false, 3189 + }, 3190 + { 3191 + code3: 'sam', 3192 + code2: '', 3193 + name: 'Samaritan Aramaic', 3194 + en: 'Samaritan Aramaic', 3195 + android: false, 3196 + ios: false, 3197 + }, 3198 + { 3199 + code3: 'san', 3200 + code2: 'sa', 3201 + name: 'Sanskrit', 3202 + en: 'Sanskrit', 3203 + android: false, 3204 + ios: false, 3205 + }, 3206 + { 3207 + code3: 'sas', 3208 + code2: '', 3209 + name: 'Sasak', 3210 + en: 'Sasak', 3211 + android: false, 3212 + ios: false, 3213 + }, 3214 + { 3215 + code3: 'sat', 3216 + code2: '', 3217 + name: 'Santali', 3218 + en: 'Santali', 3219 + android: false, 3220 + ios: false, 3221 + }, 3222 + { 3223 + code3: 'scn', 3224 + code2: '', 3225 + name: 'Sicilian', 3226 + en: 'Sicilian', 3227 + android: false, 3228 + ios: false, 3229 + }, 3230 + { 3231 + code3: 'sco', 3232 + code2: '', 3233 + name: 'Scots', 3234 + en: 'Scots', 3235 + android: false, 3236 + ios: false, 3237 + }, 3238 + { 3239 + code3: 'sel', 3240 + code2: '', 3241 + name: 'Selkup', 3242 + en: 'Selkup', 3243 + android: false, 3244 + ios: false, 3245 + }, 3246 + { 3247 + code3: 'sem', 3248 + code2: '', 3249 + name: 'Semitic languages', 3250 + en: 'Semitic languages', 3251 + android: false, 3252 + ios: false, 3253 + }, 3254 + { 3255 + code3: 'sga', 3256 + code2: '', 3257 + name: 'Irish, Old (to 900)', 3258 + en: 'Irish, Old (to 900)', 3259 + android: false, 3260 + ios: false, 3261 + }, 3262 + { 3263 + code3: 'sgn', 3264 + code2: '', 3265 + name: 'Sign Languages', 3266 + en: 'Sign Languages', 3267 + android: false, 3268 + ios: false, 3269 + }, 3270 + { 3271 + code3: 'shn', 3272 + code2: '', 3273 + name: 'Shan', 3274 + en: 'Shan', 3275 + android: false, 3276 + ios: false, 3277 + }, 3278 + { 3279 + code3: 'sid', 3280 + code2: '', 3281 + name: 'Sidamo', 3282 + en: 'Sidamo', 3283 + android: false, 3284 + ios: false, 3285 + }, 3286 + { 3287 + code3: 'sin', 3288 + code2: 'si', 3289 + name: 'Sinhala', 3290 + en: 'Sinhala', 3291 + android: false, 3292 + ios: false, 3293 + }, 3294 + { 3295 + code3: 'sio', 3296 + code2: '', 3297 + name: 'Siouan languages', 3298 + en: 'Siouan languages', 3299 + android: false, 3300 + ios: false, 3301 + }, 3302 + { 3303 + code3: 'sit', 3304 + code2: '', 3305 + name: 'Sino-Tibetan languages', 3306 + en: 'Sino-Tibetan languages', 3307 + android: false, 3308 + ios: false, 3309 + }, 3310 + { 3311 + code3: 'sla', 3312 + code2: '', 3313 + name: 'Slavic languages', 3314 + en: 'Slavic languages', 3315 + android: false, 3316 + ios: false, 3317 + }, 3318 + { 3319 + code3: 'slo', 3320 + code2: 'sk', 3321 + name: 'slovenčina', 3322 + en: 'Slovak', 3323 + android: true, 3324 + ios: false, 3325 + }, 3326 + { 3327 + code3: 'slk', 3328 + code2: 'sk', 3329 + name: 'slovenčina', 3330 + en: 'Slovak', 3331 + android: true, 3332 + ios: false, 3333 + }, 3334 + { 3335 + code3: 'slv', 3336 + code2: 'sl', 3337 + name: 'slovenščina', 3338 + en: 'Slovenian', 3339 + android: true, 3340 + ios: false, 3341 + }, 3342 + { 3343 + code3: 'sma', 3344 + code2: '', 3345 + name: 'Southern Sami', 3346 + en: 'Southern Sami', 3347 + android: false, 3348 + ios: false, 3349 + }, 3350 + { 3351 + code3: 'sme', 3352 + code2: 'se', 3353 + name: 'Northern Sami', 3354 + en: 'Northern Sami', 3355 + android: false, 3356 + ios: false, 3357 + }, 3358 + { 3359 + code3: 'smi', 3360 + code2: '', 3361 + name: 'Sami languages', 3362 + en: 'Sami languages', 3363 + android: false, 3364 + ios: false, 3365 + }, 3366 + { 3367 + code3: 'smj', 3368 + code2: '', 3369 + name: 'Lule Sami', 3370 + en: 'Lule Sami', 3371 + android: false, 3372 + ios: false, 3373 + }, 3374 + { 3375 + code3: 'smn', 3376 + code2: '', 3377 + name: 'Inari Sami', 3378 + en: 'Inari Sami', 3379 + android: false, 3380 + ios: false, 3381 + }, 3382 + { 3383 + code3: 'smo', 3384 + code2: 'sm', 3385 + name: 'Samoan', 3386 + en: 'Samoan', 3387 + android: false, 3388 + ios: false, 3389 + }, 3390 + { 3391 + code3: 'sms', 3392 + code2: '', 3393 + name: 'Skolt Sami', 3394 + en: 'Skolt Sami', 3395 + android: false, 3396 + ios: false, 3397 + }, 3398 + { 3399 + code3: 'sna', 3400 + code2: 'sn', 3401 + name: 'Shona', 3402 + en: 'Shona', 3403 + android: false, 3404 + ios: false, 3405 + }, 3406 + { 3407 + code3: 'snd', 3408 + code2: 'sd', 3409 + name: 'سنڌي', 3410 + en: 'Sindhi', 3411 + android: false, 3412 + ios: false, 3413 + }, 3414 + { 3415 + code3: 'snk', 3416 + code2: '', 3417 + name: 'Soninke', 3418 + en: 'Soninke', 3419 + android: false, 3420 + ios: false, 3421 + }, 3422 + { 3423 + code3: 'sog', 3424 + code2: '', 3425 + name: 'Sogdian', 3426 + en: 'Sogdian', 3427 + android: false, 3428 + ios: false, 3429 + }, 3430 + { 3431 + code3: 'som', 3432 + code2: 'so', 3433 + name: 'Somali', 3434 + en: 'Somali', 3435 + android: false, 3436 + ios: false, 3437 + }, 3438 + { 3439 + code3: 'son', 3440 + code2: '', 3441 + name: 'Songhai languages', 3442 + en: 'Songhai languages', 3443 + android: false, 3444 + ios: false, 3445 + }, 3446 + { 3447 + code3: 'sot', 3448 + code2: 'st', 3449 + name: 'Southern Sotho', 3450 + en: 'Southern Sotho', 3451 + android: false, 3452 + ios: false, 3453 + }, 3454 + { 3455 + code3: 'spa', 3456 + code2: 'es', 3457 + name: 'español', 3458 + en: 'Spanish', 3459 + android: true, 3460 + ios: true, 3461 + }, 3462 + { 3463 + code3: 'sqi', 3464 + code2: 'sq', 3465 + name: 'Albanian', 3466 + en: 'Albanian', 3467 + android: true, 3468 + ios: false, 3469 + }, 3470 + { 3471 + code3: 'srd', 3472 + code2: 'sc', 3473 + name: 'Sardinian', 3474 + en: 'Sardinian', 3475 + android: false, 3476 + ios: false, 3477 + }, 3478 + { 3479 + code3: 'srn', 3480 + code2: '', 3481 + name: 'Sranan Tongo', 3482 + en: 'Sranan Tongo', 3483 + android: false, 3484 + ios: false, 3485 + }, 3486 + { 3487 + code3: 'srp', 3488 + code2: 'sr', 3489 + name: 'српски', 3490 + en: 'Serbian', 3491 + android: false, 3492 + ios: false, 3493 + }, 3494 + { 3495 + code3: 'srr', 3496 + code2: '', 3497 + name: 'Serer', 3498 + en: 'Serer', 3499 + android: false, 3500 + ios: false, 3501 + }, 3502 + { 3503 + code3: 'ssa', 3504 + code2: '', 3505 + name: 'Nilo-Saharan languages', 3506 + en: 'Nilo-Saharan languages', 3507 + android: false, 3508 + ios: false, 3509 + }, 3510 + { 3511 + code3: 'ssw', 3512 + code2: 'ss', 3513 + name: 'Swati', 3514 + en: 'Swati', 3515 + android: false, 3516 + ios: false, 3517 + }, 3518 + { 3519 + code3: 'suk', 3520 + code2: '', 3521 + name: 'Sukuma', 3522 + en: 'Sukuma', 3523 + android: false, 3524 + ios: false, 3525 + }, 3526 + { 3527 + code3: 'sun', 3528 + code2: 'su', 3529 + name: 'Sundanese', 3530 + en: 'Sundanese', 3531 + android: false, 3532 + ios: false, 3533 + }, 3534 + { 3535 + code3: 'sus', 3536 + code2: '', 3537 + name: 'Susu', 3538 + en: 'Susu', 3539 + android: false, 3540 + ios: false, 3541 + }, 3542 + { 3543 + code3: 'sux', 3544 + code2: '', 3545 + name: 'Sumerian', 3546 + en: 'Sumerian', 3547 + android: false, 3548 + ios: false, 3549 + }, 3550 + { 3551 + code3: 'swa', 3552 + code2: 'sw', 3553 + name: 'Kiswahili', 3554 + en: 'Swahili', 3555 + android: true, 3556 + ios: false, 3557 + }, 3558 + { 3559 + code3: 'swe', 3560 + code2: 'sv', 3561 + name: 'svenska', 3562 + en: 'Swedish', 3563 + android: true, 3564 + ios: false, 3565 + }, 3566 + { 3567 + code3: 'syc', 3568 + code2: '', 3569 + name: 'Classical Syriac', 3570 + en: 'Classical Syriac', 3571 + android: false, 3572 + ios: false, 3573 + }, 3574 + { 3575 + code3: 'syr', 3576 + code2: '', 3577 + name: 'Syriac', 3578 + en: 'Syriac', 3579 + android: false, 3580 + ios: false, 3581 + }, 3582 + { 3583 + code3: 'tah', 3584 + code2: 'ty', 3585 + name: 'Tahitian', 3586 + en: 'Tahitian', 3587 + android: false, 3588 + ios: false, 3589 + }, 3590 + { 3591 + code3: 'tai', 3592 + code2: '', 3593 + name: 'Tai languages', 3594 + en: 'Tai languages', 3595 + android: false, 3596 + ios: false, 3597 + }, 3598 + { 3599 + code3: 'tam', 3600 + code2: 'ta', 3601 + name: 'தமிழ்', 3602 + en: 'Tamil', 3603 + android: true, 3604 + ios: false, 3605 + }, 3606 + { 3607 + code3: 'tat', 3608 + code2: 'tt', 3609 + name: 'Tatar', 3610 + en: 'Tatar', 3611 + android: false, 3612 + ios: false, 3613 + }, 3614 + { 3615 + code3: 'tel', 3616 + code2: 'te', 3617 + name: 'తెలుగు', 3618 + en: 'Telugu', 3619 + android: true, 3620 + ios: false, 3621 + }, 3622 + { 3623 + code3: 'tem', 3624 + code2: '', 3625 + name: 'Timne', 3626 + en: 'Timne', 3627 + android: false, 3628 + ios: false, 3629 + }, 3630 + { 3631 + code3: 'ter', 3632 + code2: '', 3633 + name: 'Tereno', 3634 + en: 'Tereno', 3635 + android: false, 3636 + ios: false, 3637 + }, 3638 + { 3639 + code3: 'tet', 3640 + code2: '', 3641 + name: 'Tetum', 3642 + en: 'Tetum', 3643 + android: false, 3644 + ios: false, 3645 + }, 3646 + { 3647 + code3: 'tgk', 3648 + code2: 'tg', 3649 + name: 'Tajik', 3650 + en: 'Tajik', 3651 + android: false, 3652 + ios: false, 3653 + }, 3654 + { 3655 + code3: 'tgl', 3656 + code2: 'tl', 3657 + name: 'Filipino', 3658 + en: 'Filipino', 3659 + android: true, 3660 + ios: false, 3661 + }, 3662 + { 3663 + code3: 'tha', 3664 + code2: 'th', 3665 + name: 'ไทย', 3666 + en: 'Thai', 3667 + android: true, 3668 + ios: true, 3669 + }, 3670 + { 3671 + code3: 'tib', 3672 + code2: 'bo', 3673 + name: 'Tibetan', 3674 + en: 'Tibetan', 3675 + android: false, 3676 + ios: false, 3677 + }, 3678 + { 3679 + code3: 'tig', 3680 + code2: '', 3681 + name: 'Tigre', 3682 + en: 'Tigre', 3683 + android: false, 3684 + ios: false, 3685 + }, 3686 + { 3687 + code3: 'tir', 3688 + code2: 'ti', 3689 + name: 'Tigrinya', 3690 + en: 'Tigrinya', 3691 + android: false, 3692 + ios: false, 3693 + }, 3694 + { 3695 + code3: 'tiv', 3696 + code2: '', 3697 + name: 'Tiv', 3698 + en: 'Tiv', 3699 + android: false, 3700 + ios: false, 3701 + }, 3702 + { 3703 + code3: 'tkl', 3704 + code2: '', 3705 + name: 'Tokelau', 3706 + en: 'Tokelau', 3707 + android: false, 3708 + ios: false, 3709 + }, 3710 + { 3711 + code3: 'tlh', 3712 + code2: '', 3713 + name: 'Klingon; tlhIngan-Hol', 3714 + en: 'Klingon; tlhIngan-Hol', 3715 + android: false, 3716 + ios: false, 3717 + }, 3718 + { 3719 + code3: 'tli', 3720 + code2: '', 3721 + name: 'Tlingit', 3722 + en: 'Tlingit', 3723 + android: false, 3724 + ios: false, 3725 + }, 3726 + { 3727 + code3: 'tmh', 3728 + code2: '', 3729 + name: 'Tamashek', 3730 + en: 'Tamashek', 3731 + android: false, 3732 + ios: false, 3733 + }, 3734 + { 3735 + code3: 'tog', 3736 + code2: '', 3737 + name: 'Tonga (Nyasa)', 3738 + en: 'Tonga (Nyasa)', 3739 + android: false, 3740 + ios: false, 3741 + }, 3742 + { 3743 + code3: 'ton', 3744 + code2: 'to', 3745 + name: 'Tongan', 3746 + en: 'Tongan', 3747 + android: false, 3748 + ios: false, 3749 + }, 3750 + { 3751 + code3: 'tpi', 3752 + code2: '', 3753 + name: 'Tok Pisin', 3754 + en: 'Tok Pisin', 3755 + android: false, 3756 + ios: false, 3757 + }, 3758 + { 3759 + code3: 'tsi', 3760 + code2: '', 3761 + name: 'Tsimshian', 3762 + en: 'Tsimshian', 3763 + android: false, 3764 + ios: false, 3765 + }, 3766 + { 3767 + code3: 'tsn', 3768 + code2: 'tn', 3769 + name: 'Tswana', 3770 + en: 'Tswana', 3771 + android: false, 3772 + ios: false, 3773 + }, 3774 + { 3775 + code3: 'tso', 3776 + code2: 'ts', 3777 + name: 'Tsonga', 3778 + en: 'Tsonga', 3779 + android: false, 3780 + ios: false, 3781 + }, 3782 + { 3783 + code3: 'tuk', 3784 + code2: 'tk', 3785 + name: 'Turkmen', 3786 + en: 'Turkmen', 3787 + android: false, 3788 + ios: false, 3789 + }, 3790 + { 3791 + code3: 'tum', 3792 + code2: '', 3793 + name: 'Tumbuka', 3794 + en: 'Tumbuka', 3795 + android: false, 3796 + ios: false, 3797 + }, 3798 + { 3799 + code3: 'tup', 3800 + code2: '', 3801 + name: 'Tupi languages', 3802 + en: 'Tupi languages', 3803 + android: false, 3804 + ios: false, 3805 + }, 3806 + { 3807 + code3: 'tur', 3808 + code2: 'tr', 3809 + name: 'Türkçe', 3810 + en: 'Turkish', 3811 + android: true, 3812 + ios: true, 3813 + }, 3814 + { 3815 + code3: 'tut', 3816 + code2: '', 3817 + name: 'Altaic languages', 3818 + en: 'Altaic languages', 3819 + android: false, 3820 + ios: false, 3821 + }, 3822 + { 3823 + code3: 'tvl', 3824 + code2: '', 3825 + name: 'Tuvalu', 3826 + en: 'Tuvalu', 3827 + android: false, 3828 + ios: false, 3829 + }, 3830 + { 3831 + code3: 'twi', 3832 + code2: 'tw', 3833 + name: 'Akan', 3834 + en: 'Akan', 3835 + android: false, 3836 + ios: false, 3837 + }, 3838 + { 3839 + code3: 'tyv', 3840 + code2: '', 3841 + name: 'Tuvinian', 3842 + en: 'Tuvinian', 3843 + android: false, 3844 + ios: false, 3845 + }, 3846 + { 3847 + code3: 'udm', 3848 + code2: '', 3849 + name: 'Udmurt', 3850 + en: 'Udmurt', 3851 + android: false, 3852 + ios: false, 3853 + }, 3854 + { 3855 + code3: 'uga', 3856 + code2: '', 3857 + name: 'Ugaritic', 3858 + en: 'Ugaritic', 3859 + android: false, 3860 + ios: false, 3861 + }, 3862 + { 3863 + code3: 'uig', 3864 + code2: 'ug', 3865 + name: 'Uyghur', 3866 + en: 'Uyghur', 3867 + android: false, 3868 + ios: false, 3869 + }, 3870 + { 3871 + code3: 'ukr', 3872 + code2: 'uk', 3873 + name: 'українська', 3874 + en: 'Ukrainian', 3875 + android: true, 3876 + ios: true, 3877 + }, 3878 + { 3879 + code3: 'umb', 3880 + code2: '', 3881 + name: 'Umbundu', 3882 + en: 'Umbundu', 3883 + android: false, 3884 + ios: false, 3885 + }, 3886 + { 3887 + code3: 'und', 3888 + code2: '', 3889 + name: 'Undetermined', 3890 + en: 'Undetermined', 3891 + android: false, 3892 + ios: false, 3893 + }, 3894 + { 3895 + code3: 'urd', 3896 + code2: 'ur', 3897 + name: 'اردو', 3898 + en: 'Urdu', 3899 + android: true, 3900 + ios: false, 3901 + }, 3902 + { 3903 + code3: 'uzb', 3904 + code2: 'uz', 3905 + name: 'o‘zbek', 3906 + en: 'Uzbek', 3907 + android: false, 3908 + ios: false, 3909 + }, 3910 + { 3911 + code3: 'vai', 3912 + code2: '', 3913 + name: 'Vai', 3914 + en: 'Vai', 3915 + android: false, 3916 + ios: false, 3917 + }, 3918 + { 3919 + code3: 'ven', 3920 + code2: 've', 3921 + name: 'Venda', 3922 + en: 'Venda', 3923 + android: false, 3924 + ios: false, 3925 + }, 3926 + { 3927 + code3: 'vie', 3928 + code2: 'vi', 3929 + name: 'Tiếng Việt', 3930 + en: 'Vietnamese', 3931 + android: true, 3932 + ios: true, 3933 + }, 3934 + { 3935 + code3: 'vol', 3936 + code2: 'vo', 3937 + name: 'Volapük', 3938 + en: 'Volapük', 3939 + android: false, 3940 + ios: false, 3941 + }, 3942 + { 3943 + code3: 'vot', 3944 + code2: '', 3945 + name: 'Votic', 3946 + en: 'Votic', 3947 + android: false, 3948 + ios: false, 3949 + }, 3950 + { 3951 + code3: 'wak', 3952 + code2: '', 3953 + name: 'Wakashan languages', 3954 + en: 'Wakashan languages', 3955 + android: false, 3956 + ios: false, 3957 + }, 3958 + { 3959 + code3: 'wal', 3960 + code2: '', 3961 + name: 'Wolaitta; Wolaytta', 3962 + en: 'Wolaitta; Wolaytta', 3963 + android: false, 3964 + ios: false, 3965 + }, 3966 + { 3967 + code3: 'war', 3968 + code2: '', 3969 + name: 'Waray', 3970 + en: 'Waray', 3971 + android: false, 3972 + ios: false, 3973 + }, 3974 + { 3975 + code3: 'was', 3976 + code2: '', 3977 + name: 'Washo', 3978 + en: 'Washo', 3979 + android: false, 3980 + ios: false, 3981 + }, 3982 + { 3983 + code3: 'wel', 3984 + code2: 'cy', 3985 + name: 'Welsh', 3986 + en: 'Welsh', 3987 + android: true, 3988 + ios: false, 3989 + }, 3990 + { 3991 + code3: 'wen', 3992 + code2: '', 3993 + name: 'Sorbian languages', 3994 + en: 'Sorbian languages', 3995 + android: false, 3996 + ios: false, 3997 + }, 3998 + { 3999 + code3: 'wln', 4000 + code2: 'wa', 4001 + name: 'Walloon', 4002 + en: 'Walloon', 4003 + android: false, 4004 + ios: false, 4005 + }, 4006 + { 4007 + code3: 'wol', 4008 + code2: 'wo', 4009 + name: 'Wolof', 4010 + en: 'Wolof', 4011 + android: false, 4012 + ios: false, 4013 + }, 4014 + { 4015 + code3: 'xal', 4016 + code2: '', 4017 + name: 'Kalmyk; Oirat', 4018 + en: 'Kalmyk; Oirat', 4019 + android: false, 4020 + ios: false, 4021 + }, 4022 + { 4023 + code3: 'xho', 4024 + code2: 'xh', 4025 + name: 'Xhosa', 4026 + en: 'Xhosa', 4027 + android: false, 4028 + ios: false, 4029 + }, 4030 + { 4031 + code3: 'yao', 4032 + code2: '', 4033 + name: 'Yao', 4034 + en: 'Yao', 4035 + android: false, 4036 + ios: false, 4037 + }, 4038 + { 4039 + code3: 'yap', 4040 + code2: '', 4041 + name: 'Yapese', 4042 + en: 'Yapese', 4043 + android: false, 4044 + ios: false, 4045 + }, 4046 + { 4047 + code3: 'yid', 4048 + code2: 'yi', 4049 + name: 'Yiddish', 4050 + en: 'Yiddish', 4051 + android: false, 4052 + ios: false, 4053 + }, 4054 + { 4055 + code3: 'yor', 4056 + code2: 'yo', 4057 + name: 'Yoruba', 4058 + en: 'Yoruba', 4059 + android: false, 4060 + ios: false, 4061 + }, 4062 + { 4063 + code3: 'ypk', 4064 + code2: '', 4065 + name: 'Yupik languages', 4066 + en: 'Yupik languages', 4067 + android: false, 4068 + ios: false, 4069 + }, 4070 + { 4071 + code3: 'zap', 4072 + code2: '', 4073 + name: 'Zapotec', 4074 + en: 'Zapotec', 4075 + android: false, 4076 + ios: false, 4077 + }, 4078 + { 4079 + code3: 'zbl', 4080 + code2: '', 4081 + name: 'Blissymbols; Blissymbolics; Bliss', 4082 + en: 'Blissymbols; Blissymbolics; Bliss', 4083 + android: false, 4084 + ios: false, 4085 + }, 4086 + { 4087 + code3: 'zen', 4088 + code2: '', 4089 + name: 'Zenaga', 4090 + en: 'Zenaga', 4091 + android: false, 4092 + ios: false, 4093 + }, 4094 + { 4095 + code3: 'zgh', 4096 + code2: '', 4097 + name: 'Standard Moroccan Tamazight', 4098 + en: 'Standard Moroccan Tamazight', 4099 + android: false, 4100 + ios: false, 4101 + }, 4102 + { 4103 + code3: 'zha', 4104 + code2: 'za', 4105 + name: 'Zhuang; Chuang', 4106 + en: 'Zhuang; Chuang', 4107 + android: false, 4108 + ios: false, 4109 + }, 4110 + { 4111 + code3: 'zho', 4112 + code2: 'zh', 4113 + name: '中文', 4114 + en: 'Chinese', 4115 + android: true, 4116 + ios: true, 4117 + }, 4118 + { 4119 + code3: 'znd', 4120 + code2: '', 4121 + name: 'Zande languages', 4122 + en: 'Zande languages', 4123 + android: false, 4124 + ios: false, 4125 + }, 4126 + { 4127 + code3: 'zul', 4128 + code2: 'zu', 4129 + name: 'Zulu', 4130 + en: 'Zulu', 4131 + android: false, 4132 + ios: false, 4133 + }, 4134 + { 4135 + code3: 'zun', 4136 + code2: '', 4137 + name: 'Zuni', 4138 + en: 'Zuni', 4139 + android: false, 4140 + ios: false, 4141 + }, 632 4142 { 633 4143 code3: 'zza', 634 4144 code2: '', 635 4145 name: 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki', 4146 + en: 'Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki', 4147 + android: false, 4148 + ios: false, 636 4149 }, 637 4150 ] 638 4151
+98 -53
src/screens/PostThread/components/ThreadItemAnchor.tsx
··· 7 7 AtUri, 8 8 RichText as RichTextAPI, 9 9 } from '@atproto/api' 10 - import {msg} from '@lingui/core/macro' 11 - import {useLingui} from '@lingui/react' 12 - import {Plural, Trans} from '@lingui/react/macro' 10 + import {Plural, Trans, useLingui} from '@lingui/react/macro' 13 11 14 12 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 15 - import {useTranslate} from '#/lib/hooks/useTranslate' 16 13 import {makeProfileLink} from '#/lib/routes/links' 17 14 import {sanitizeDisplayName} from '#/lib/strings/display-names' 18 15 import {sanitizeHandle} from '#/lib/strings/handles' 19 16 import {niceDate} from '#/lib/strings/time' 20 - import {getTranslatorLink, isPostInLanguage} from '#/locale/helpers' 17 + import { 18 + getPostLanguage, 19 + getTranslatorLink, 20 + isPostInLanguage, 21 + } from '#/locale/helpers' 21 22 import { 22 23 POST_TOMBSTONE, 23 24 type Shadow, ··· 44 45 import {CalendarClock_Stroke2_Corner0_Rounded as CalendarClockIcon} from '#/components/icons/CalendarClock' 45 46 import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 46 47 import {InlineLinkText, Link} from '#/components/Link' 48 + import {Loader} from '#/components/Loader' 47 49 import {ContentHider} from '#/components/moderation/ContentHider' 48 50 import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' 49 51 import {PostAlerts} from '#/components/moderation/PostAlerts' 50 52 import {type AppModerationCause} from '#/components/Pills' 51 53 import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' 54 + import {TranslatedPost} from '#/components/Post/Translated' 52 55 import {PostControls, PostControlsSkeleton} from '#/components/PostControls' 53 56 import {useFormatPostStatCount} from '#/components/PostControls/util' 54 57 import {ProfileHoverCard} from '#/components/ProfileHoverCard' ··· 60 63 import {WhoCanReply} from '#/components/WhoCanReply' 61 64 import {useAnalytics} from '#/analytics' 62 65 import {useActorStatus} from '#/features/liveNow' 66 + import { 67 + Provider as TranslateOnDeviceProvider, 68 + useTranslateOnDevice, 69 + } from '#/translation' 63 70 import * as bsky from '#/types/bsky' 64 71 65 72 export function ThreadItemAnchor({ ··· 82 89 } 83 90 84 91 return ( 85 - <ThreadItemAnchorInner 86 - // Safeguard from clobbering per-post state below: 87 - key={postShadow.uri} 88 - item={item} 89 - isRoot={isRoot} 90 - postShadow={postShadow} 91 - onPostSuccess={onPostSuccess} 92 - threadgateRecord={threadgateRecord} 93 - postSource={postSource} 94 - /> 92 + <TranslateOnDeviceProvider> 93 + <ThreadItemAnchorInner 94 + // Safeguard from clobbering per-post state below: 95 + key={postShadow.uri} 96 + item={item} 97 + isRoot={isRoot} 98 + postShadow={postShadow} 99 + onPostSuccess={onPostSuccess} 100 + threadgateRecord={threadgateRecord} 101 + postSource={postSource} 102 + /> 103 + </TranslateOnDeviceProvider> 95 104 ) 96 105 } 97 106 ··· 178 187 }) { 179 188 const t = useTheme() 180 189 const ax = useAnalytics() 181 - const {_} = useLingui() 190 + const {t: l} = useLingui() 182 191 const {openComposer} = useOpenComposer() 183 192 const {currentAccount, hasSession} = useSession() 184 193 const feedFeedback = useFeedFeedback(postSource?.feedSourceInfo, hasSession) ··· 411 420 shouldProxyLinks={true} 412 421 /> 413 422 ) : undefined} 423 + <TranslatedPost postText={record.text} hideLoading /> 424 + <TranslateLink post={item.value.post} /> 414 425 {post.embed && ( 415 426 <View style={[a.py_xs]}> 416 427 <Embed ··· 447 458 t.atoms.border_contrast_low, 448 459 ]}> 449 460 {post.repostCount != null && post.repostCount !== 0 ? ( 450 - <Link to={repostsHref} label={_(msg`Reposts of this post`)}> 461 + <Link to={repostsHref} label={l`Reposts of this post`}> 451 462 <Text 452 463 testID="repostCount-expanded" 453 464 style={[a.text_md, t.atoms.text_contrast_medium]}> ··· 467 478 {post.quoteCount != null && 468 479 post.quoteCount !== 0 && 469 480 !post.viewer?.embeddingDisabled ? ( 470 - <Link to={quotesHref} label={_(msg`Quotes of this post`)}> 481 + <Link to={quotesHref} label={l`Quotes of this post`}> 471 482 <Text 472 483 testID="quoteCount-expanded" 473 484 style={[a.text_md, t.atoms.text_contrast_medium]}> ··· 485 496 </Link> 486 497 ) : null} 487 498 {post.likeCount != null && post.likeCount !== 0 ? ( 488 - <Link to={likesHref} label={_(msg`Likes on this post`)}> 499 + <Link to={likesHref} label={l`Likes on this post`}> 489 500 <Text 490 501 testID="likeCount-expanded" 491 502 style={[a.text_md, t.atoms.text_contrast_medium]}> ··· 546 557 ) 547 558 }) 548 559 549 - function ExpandedPostDetails({ 560 + function TranslateLink({ 550 561 post, 551 - isThreadAuthor, 552 562 }: { 553 563 post: Extract<ThreadItem, {type: 'threadPost'}>['value']['post'] 554 - isThreadAuthor: boolean 555 564 }) { 556 565 const t = useTheme() 557 566 const ax = useAnalytics() 558 - const {_, i18n} = useLingui() 559 - const translate = useTranslate() 560 - const isRootPost = !('reply' in post.record) 567 + const {t: l} = useLingui() 561 568 const langPrefs = useLanguagePrefs() 562 569 570 + const {translate, clearTranslation, translationState} = useTranslateOnDevice() 571 + 563 572 const needsTranslation = useMemo( 564 573 () => 565 574 Boolean( ··· 569 578 [post, langPrefs.primaryLanguage], 570 579 ) 571 580 581 + const sourceLanguage = getPostLanguage(post) 582 + 572 583 const onTranslatePress = useCallback( 573 584 (e: GestureResponderEvent) => { 574 585 e.preventDefault() 575 - translate(post.record.text || '', langPrefs.primaryLanguage) 586 + void translate( 587 + post.record.text || '', 588 + langPrefs.primaryLanguage, 589 + sourceLanguage, 590 + ) 576 591 577 592 if ( 578 593 bsky.dangerousIsType<AppBskyFeedPost.Record>( ··· 589 604 590 605 return false 591 606 }, 592 - [ax, translate, langPrefs, post], 607 + [ax, sourceLanguage, translate, langPrefs, post], 608 + ) 609 + 610 + const onHideTranslation = useCallback( 611 + (e: GestureResponderEvent) => { 612 + e.preventDefault() 613 + clearTranslation() 614 + return false 615 + }, 616 + [clearTranslation], 593 617 ) 594 618 595 619 return ( 620 + needsTranslation && ( 621 + <View style={[a.gap_md, a.pt_md, a.align_start]}> 622 + {translationState.status === 'loading' ? ( 623 + <View style={[a.flex_row, a.align_center, a.gap_xs]}> 624 + <Loader size="xs" /> 625 + <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 626 + <Trans>Translating…</Trans> 627 + </Text> 628 + </View> 629 + ) : translationState.status === 'success' ? ( 630 + <InlineLinkText 631 + to="#" 632 + label={l`Hide translation`} 633 + style={[a.text_sm]} 634 + onPress={onHideTranslation}> 635 + <Trans>Hide translation</Trans> 636 + </InlineLinkText> 637 + ) : ( 638 + <InlineLinkText 639 + to={getTranslatorLink(post.record.text, langPrefs.primaryLanguage)} 640 + label={l`Translate`} 641 + style={[a.text_sm]} 642 + onPress={onTranslatePress}> 643 + <Trans>Translate</Trans> 644 + </InlineLinkText> 645 + )} 646 + </View> 647 + ) 648 + ) 649 + } 650 + 651 + function ExpandedPostDetails({ 652 + post, 653 + isThreadAuthor, 654 + }: { 655 + post: Extract<ThreadItem, {type: 'threadPost'}>['value']['post'] 656 + isThreadAuthor: boolean 657 + }) { 658 + const t = useTheme() 659 + const {i18n} = useLingui() 660 + const isRootPost = !('reply' in post.record) 661 + 662 + return ( 596 663 <View style={[a.gap_md, a.pt_md, a.align_start]}> 597 664 <BackdatedPostIndicator post={post} /> 598 665 <View style={[a.flex_row, a.align_center, a.flex_wrap, a.gap_sm]}> ··· 602 669 {isRootPost && ( 603 670 <WhoCanReply post={post} isThreadAuthor={isThreadAuthor} /> 604 671 )} 605 - {needsTranslation && ( 606 - <> 607 - <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 608 - &middot; 609 - </Text> 610 - 611 - <InlineLinkText 612 - // overridden to open an intent on android, but keep 613 - // as anchor tag for accessibility 614 - to={getTranslatorLink( 615 - post.record.text, 616 - langPrefs.primaryLanguage, 617 - )} 618 - label={_(msg`Translate`)} 619 - style={[a.text_sm]} 620 - onPress={onTranslatePress}> 621 - <Trans>Translate</Trans> 622 - </InlineLinkText> 623 - </> 624 - )} 625 672 </View> 626 673 </View> 627 674 ) ··· 629 676 630 677 function BackdatedPostIndicator({post}: {post: AppBskyFeedDefs.PostView}) { 631 678 const t = useTheme() 632 - const {_, i18n} = useLingui() 679 + const {t: l, i18n} = useLingui() 633 680 const control = Prompt.usePromptControl() 634 681 635 682 const indexedAt = new Date(post.indexedAt) ··· 649 696 return ( 650 697 <> 651 698 <Button 652 - label={_(msg`Archived post`)} 653 - accessibilityHint={_( 654 - msg`Shows information about when this post was created`, 655 - )} 699 + label={l`Archived post`} 700 + accessibilityHint={l`Shows information about when this post was created`} 656 701 onPress={e => { 657 702 e.preventDefault() 658 703 e.stopPropagation() ··· 711 756 </Prompt.DescriptionText> 712 757 </Prompt.Content> 713 758 <Prompt.Actions> 714 - <Prompt.Action cta={_(msg`Okay`)} onPress={() => {}} /> 759 + <Prompt.Action cta={l`Okay`} onPress={() => {}} /> 715 760 </Prompt.Actions> 716 761 </Prompt.Outer> 717 762 </>
+5 -5
src/screens/Search/components/SearchLanguageDropdown.tsx
··· 23 23 onChange(value: string): void 24 24 }) { 25 25 const {_} = useLingui() 26 - const {appLanguage, contentLanguages} = useLanguagePrefs() 26 + const {appLanguage, contentLanguages, primaryLanguage} = useLanguagePrefs() 27 27 28 28 const languages = useMemo(() => { 29 29 return LANGUAGES.filter( ··· 47 47 al => 48 48 // skip `ast`, because it uses a 3-letter code which conflicts with `as` 49 49 // it begins with `a` anyway so still is top of the list 50 - al.code2 !== 'ast' && al.code2.startsWith(a.value), 50 + (al.code2 as string) !== 'ast' && al.code2.startsWith(a.value), 51 51 ) 52 52 const bIsCommon = !!APP_LANGUAGES.find( 53 53 al => 54 54 // ditto 55 - al.code2 !== 'ast' && al.code2.startsWith(b.value), 55 + (al.code2 as string) !== 'ast' && al.code2.startsWith(b.value), 56 56 ) 57 57 if (aIsCommon && !bIsCommon) return -1 58 58 if (bIsCommon && !aIsCommon) return 1 59 59 // fall back to alphabetical 60 - return a.label.localeCompare(b.label) 60 + return a.label.localeCompare(b.label, primaryLanguage) 61 61 }) 62 - }, [appLanguage, contentLanguages]) 62 + }, [appLanguage, contentLanguages, primaryLanguage]) 63 63 64 64 const currentLanguageLabel = 65 65 languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`)
+30 -24
src/screens/Settings/LanguageSettings.tsx
··· 8 8 type CommonNavigatorParams, 9 9 type NativeStackScreenProps, 10 10 } from '#/lib/routes/types' 11 - import {languageName, sanitizeAppLanguageSetting} from '#/locale/helpers' 11 + import {sanitizeAppLanguageSetting} from '#/locale/helpers' 12 12 import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages' 13 13 import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' 14 14 import {atoms as a, web} from '#/alf' ··· 144 144 <Select.ItemText>{label}</Select.ItemText> 145 145 </Select.Item> 146 146 )} 147 - items={DEDUPED_LANGUAGES.map(l => ({ 148 - label: languageName(l, langPrefs.appLanguage), 147 + items={DEDUPED_LANGUAGES.sort( 148 + (a, b) => 149 + a.name.localeCompare(b.name, langPrefs.appLanguage), // Localized sort 150 + ).map(l => ({ 151 + label: l.name, // Pre-generated name using Intl.DisplayNames 149 152 value: l.code2, 150 153 }))} 151 154 /> ··· 177 180 values={langPrefs.contentLanguages} 178 181 onChange={setLangPrefs.setContentLanguages}> 179 182 <Toggle.PanelGroup> 180 - {possibleLanguages.map((language, index) => { 181 - const name = languageName(language, langPrefs.appLanguage) 182 - return ( 183 - <Toggle.Item 184 - key={language.code2} 185 - name={language.code2} 186 - label={name}> 187 - {({selected}) => ( 188 - <Toggle.Panel 189 - active={selected} 190 - adjacent={index === 0 ? 'trailing' : 'both'}> 191 - <Toggle.Checkbox /> 192 - <Toggle.PanelText>{name}</Toggle.PanelText> 193 - </Toggle.Panel> 194 - )} 195 - </Toggle.Item> 183 + {possibleLanguages 184 + .sort( 185 + (a, b) => 186 + a.name.localeCompare(b.name, langPrefs.appLanguage), // Localized sort 196 187 ) 197 - })} 188 + .map((language, index) => { 189 + const name = language.name // Pre-generated name using Intl.DisplayNames 190 + return ( 191 + <Toggle.Item 192 + key={language.code2} 193 + name={language.code2} 194 + label={name}> 195 + {({selected}) => ( 196 + <Toggle.Panel 197 + active={selected} 198 + adjacent={index === 0 ? 'trailing' : 'both'}> 199 + <Toggle.Checkbox /> 200 + <Toggle.PanelText>{name}</Toggle.PanelText> 201 + </Toggle.Panel> 202 + )} 203 + </Toggle.Item> 204 + ) 205 + })} 198 206 <Button 199 - label={_(msg`Add more languages...`)} 207 + label={_(msg`Add more languages…`)} 200 208 onPress={contentLanguagePrefsControl.open}> 201 209 <Toggle.Panel adjacent="leading"> 202 210 <Toggle.PanelIcon icon={PlusIcon} /> 203 - <Toggle.PanelText> 204 - Add more languages... 205 - </Toggle.PanelText> 211 + <Toggle.PanelText>Add more languages…</Toggle.PanelText> 206 212 </Toggle.Panel> 207 213 </Button> 208 214 </Toggle.PanelGroup>
+188
src/translation/index.tsx
··· 1 + import React, { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useMemo, 6 + useState, 7 + } from 'react' 8 + import {LayoutAnimation, Platform} from 'react-native' 9 + import {getLocales} from 'expo-localization' 10 + import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types' 11 + 12 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 13 + import {getTranslatorLink} from '#/locale/helpers' 14 + import {logger} from '#/logger' 15 + import {useLanguagePrefs} from '#/state/preferences' 16 + import {useAnalytics} from '#/analytics' 17 + 18 + type TranslationState = 19 + | {status: 'idle'} 20 + | {status: 'loading'} 21 + | { 22 + status: 'success' 23 + translatedText: string 24 + sourceLanguage: TranslationTaskResult['sourceLanguage'] 25 + targetLanguage: TranslationTaskResult['targetLanguage'] 26 + } 27 + 28 + const IDLE: TranslationState = {status: 'idle'} 29 + 30 + /** 31 + * Attempts on-device translation via @bsky.app/expo-translate-text. 32 + * Uses a lazy import to avoid crashing if the native module isn't linked into 33 + * the current build. 34 + */ 35 + async function attemptTranslation( 36 + input: string, 37 + targetLangCodeOriginal: string, 38 + sourceLangCodeOriginal?: string, // Auto-detects if not provided 39 + ): Promise<{ 40 + translatedText: string 41 + targetLanguage: TranslationTaskResult['targetLanguage'] 42 + sourceLanguage: TranslationTaskResult['sourceLanguage'] 43 + }> { 44 + // Note that Android only supports two-character language codes and will fail 45 + // on other input. 46 + // https://developers.google.com/android/reference/com/google/mlkit/nl/translate/TranslateLanguage 47 + let targetLangCode = 48 + Platform.OS === 'android' 49 + ? targetLangCodeOriginal.split('-')[0] 50 + : targetLangCodeOriginal 51 + const sourceLangCode = 52 + Platform.OS === 'android' 53 + ? sourceLangCodeOriginal?.split('-')[0] 54 + : sourceLangCodeOriginal 55 + 56 + // Special cases for regional languages 57 + if (Platform.OS !== 'android') { 58 + const deviceLocales = getLocales() 59 + const primaryLanguageTag = deviceLocales[0]?.languageTag 60 + switch (targetLangCodeOriginal) { 61 + case 'en': // en-US, en-GB 62 + case 'es': // es-419, es-ES 63 + case 'pt': // pt-BR, pt-PT 64 + case 'zh': // zh-Hans-CN, zh-Hant-HK, zh-Hant-TW 65 + targetLangCode = primaryLanguageTag ?? targetLangCodeOriginal 66 + break 67 + } 68 + } 69 + 70 + const {onTranslateTask} = 71 + // Needed in order to type check the dynamically imported module. 72 + // eslint-disable-next-line @typescript-eslint/consistent-type-imports 73 + require('@bsky.app/expo-translate-text') as typeof import('@bsky.app/expo-translate-text') 74 + const result = await onTranslateTask({ 75 + input, 76 + targetLangCode, 77 + sourceLangCode, 78 + }) 79 + 80 + // Since `input` is always a string, the result should always be a string. 81 + return { 82 + translatedText: 83 + typeof result.translatedTexts === 'string' ? result.translatedTexts : '', 84 + targetLanguage: result.targetLanguage, 85 + sourceLanguage: result.sourceLanguage ?? sourceLangCode ?? null, // iOS doesn't return the source language 86 + } 87 + } 88 + 89 + const Context = createContext<{ 90 + translationState: TranslationState 91 + translate: ( 92 + text: string, 93 + targetLangCode: string, 94 + sourceLangCode?: string, 95 + ) => Promise<void> 96 + clearTranslation: () => void 97 + }>({ 98 + translationState: IDLE, 99 + translate: async () => {}, 100 + clearTranslation: () => {}, 101 + }) 102 + Context.displayName = 'TranslationContext' 103 + 104 + /** 105 + * Native translation hook. Attempts on-device translation using Apple 106 + * Translation (iOS 18+) or Google ML Kit (Android). 107 + * 108 + * Falls back to Google Translate URL if the language pack is unavailable. 109 + * 110 + * Web uses index.web.ts which always opens Google Translate. 111 + */ 112 + export function useTranslateOnDevice() { 113 + const context = useContext(Context) 114 + if (!context) { 115 + throw new Error( 116 + 'useTranslateOnDevice must be used within a TranslateOnDeviceProvider', 117 + ) 118 + } 119 + return context 120 + } 121 + 122 + export function Provider({children}: {children?: React.ReactNode}) { 123 + const [translationState, setTranslationState] = 124 + useState<TranslationState>(IDLE) 125 + const openLink = useOpenLink() 126 + const ax = useAnalytics() 127 + const {primaryLanguage} = useLanguagePrefs() 128 + 129 + const clearTranslation = useCallback(() => { 130 + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 131 + setTranslationState(IDLE) 132 + }, []) 133 + 134 + const translate = useCallback( 135 + async ( 136 + text: string, 137 + targetLangCode: string = primaryLanguage, 138 + sourceLangCode?: string, 139 + ) => { 140 + setTranslationState({status: 'loading'}) 141 + try { 142 + const result = await attemptTranslation( 143 + text, 144 + targetLangCode, 145 + sourceLangCode, 146 + ) 147 + ax.metric('translate:result', { 148 + method: 'on-device', 149 + os: Platform.OS, 150 + sourceLanguage: result.sourceLanguage, 151 + targetLanguage: result.targetLanguage, 152 + }) 153 + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 154 + setTranslationState({ 155 + status: 'success', 156 + translatedText: result.translatedText, 157 + sourceLanguage: result.sourceLanguage, 158 + targetLanguage: result.targetLanguage, 159 + }) 160 + } catch (e) { 161 + logger.error('Failed to translate post on device', {safeMessage: e}) 162 + // On-device translation failed (language pack missing or user dismissed 163 + // the download prompt). Fall back to Google Translate. 164 + ax.metric('translate:result', { 165 + method: 'fallback-alert', 166 + os: Platform.OS, 167 + sourceLanguage: sourceLangCode ?? null, 168 + targetLanguage: targetLangCode, 169 + }) 170 + setTranslationState({status: 'idle'}) 171 + const translateUrl = getTranslatorLink( 172 + text, 173 + targetLangCode, 174 + sourceLangCode, 175 + ) 176 + await openLink(translateUrl) 177 + } 178 + }, 179 + [ax, openLink, primaryLanguage, setTranslationState], 180 + ) 181 + 182 + const ctx = useMemo( 183 + () => ({clearTranslation, translate, translationState}), 184 + [clearTranslation, translate, translationState], 185 + ) 186 + 187 + return <Context.Provider value={ctx}>{children}</Context.Provider> 188 + }
+43
src/translation/index.web.tsx
··· 1 + import {useCallback} from 'react' 2 + import {Platform} from 'react-native' 3 + 4 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 5 + import {getTranslatorLink} from '#/locale/helpers' 6 + import {useLanguagePrefs} from '#/state/preferences' 7 + import {useAnalytics} from '#/analytics' 8 + 9 + const translationState = {status: 'idle'} // No on-device translations for web. 10 + 11 + const clearTranslation = () => {} // no-op on web 12 + 13 + /** 14 + * Web always opens Google Translate. 15 + */ 16 + export function useTranslateOnDevice() { 17 + const openLink = useOpenLink() 18 + const ax = useAnalytics() 19 + const {primaryLanguage} = useLanguagePrefs() 20 + 21 + const translate = useCallback( 22 + async ( 23 + text: string, 24 + targetLangCode: string = primaryLanguage, 25 + sourceLangCode: string, 26 + ) => { 27 + const translateUrl = getTranslatorLink( 28 + text, 29 + targetLangCode, 30 + sourceLangCode, 31 + ) 32 + ax.metric('translate:result', { 33 + method: 'google-translate', 34 + os: Platform.OS, 35 + sourceLanguage: sourceLangCode ?? null, 36 + targetLanguage: targetLangCode, 37 + }) 38 + await openLink(translateUrl) 39 + }, 40 + [ax, openLink, primaryLanguage], 41 + ) 42 + return {clearTranslation, translate, translationState} 43 + }
+2 -1
src/view/com/home/HomeHeaderLayout.web.tsx
··· 4 4 import {useLingui} from '@lingui/react' 5 5 import type React from 'react' 6 6 7 + import {HITSLOP_10} from '#/lib/constants' 7 8 import {useKawaiiMode} from '#/state/preferences/kawaii' 8 9 import {useSession} from '#/state/session' 9 10 import {useShellLayout} from '#/state/shell/shell-layout' ··· 53 54 </View> 54 55 <Link 55 56 to="/feeds" 56 - hitSlop={10} 57 + hitSlop={HITSLOP_10} 57 58 label={_(msg`View your feeds and explore more`)} 58 59 size="small" 59 60 variant="ghost"
+5
yarn.lock
··· 3731 3731 resolved "https://registry.yarnpkg.com/@bsky.app/expo-image-crop-tool/-/expo-image-crop-tool-0.5.0.tgz#4308fbde5c15e6be9122601797bc3d9549c95e31" 3732 3732 integrity sha512-gmhQr2HWTRFyPO00fn5OmtiEVtikXusHMrN5Zoq26pu1VZX3zVE+aoc668etTqrvsQcm2Qu8fo96k5F3Wu+6wg== 3733 3733 3734 + "@bsky.app/expo-translate-text@^0.2.4": 3735 + version "0.2.4" 3736 + resolved "https://registry.yarnpkg.com/@bsky.app/expo-translate-text/-/expo-translate-text-0.2.4.tgz#6e7f20f286111ee4d550c0c84f57393fc215a675" 3737 + integrity sha512-7mvFggNfkJEufI5A3WnjfjdN3H9P6Dpx7CpDkA9npWqA8Cb2icXq3k3nz3MaXGrVKTYiJnytAffYtos7mPoeOg== 3738 + 3734 3739 "@bsky.app/react-native-mmkv@2.12.5": 3735 3740 version "2.12.5" 3736 3741 resolved "https://registry.yarnpkg.com/@bsky.app/react-native-mmkv/-/react-native-mmkv-2.12.5.tgz#eb17d31a6158c74393f617a1763ac223ff3f83a6"