summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-11-23 01:12:53 +0700
committerpolwex <polwex@sortug.com>2025-11-23 01:12:53 +0700
commitcb1b56f5a0eddbf77446f415f2beda57c8305f85 (patch)
treed333ca5c143063af8ee1b2f9e2d1d25f8ef2007c
wut
-rw-r--r--.gitignore35
-rw-r--r--CLAUDE.md106
-rw-r--r--README.md15
-rw-r--r--index.ts1
-rw-r--r--package.json13
-rw-r--r--packages/ai/.gitignore175
-rw-r--r--packages/ai/README.md15
-rw-r--r--packages/ai/bun.lock361
-rw-r--r--packages/ai/debug.ts30
-rw-r--r--packages/ai/index.ts76
-rw-r--r--packages/ai/package.json24
-rw-r--r--packages/ai/src/cache.ts150
-rw-r--r--packages/ai/src/claude.ts173
-rw-r--r--packages/ai/src/gemini.ts199
-rw-r--r--packages/ai/src/gemini2.ts149
-rw-r--r--packages/ai/src/generic.ts204
-rw-r--r--packages/ai/src/genericnew.ts169
-rw-r--r--packages/ai/src/logic/constants.ts3
-rw-r--r--packages/ai/src/nlp/index.ts7
-rw-r--r--packages/ai/src/nlp/nlp.ts208
-rw-r--r--packages/ai/src/nlp/ocr.ts18
-rw-r--r--packages/ai/src/nlp/spacy.ts79
-rw-r--r--packages/ai/src/nlp/stanza.ts210
-rw-r--r--packages/ai/src/nlp/types.ts50
-rw-r--r--packages/ai/src/openai-responses.ts186
-rw-r--r--packages/ai/src/openai.ts260
-rw-r--r--packages/ai/src/openai_tools.ts66
-rw-r--r--packages/ai/src/prompts.ts14
-rw-r--r--packages/ai/src/tts/eleven.ts20
-rw-r--r--packages/ai/src/tts/minimax.ts107
-rw-r--r--packages/ai/src/tts/output.mp3bin0 -> 1020960 bytes
-rw-r--r--packages/ai/src/types/index.ts56
-rw-r--r--packages/ai/src/types/mtproto.ts0
-rw-r--r--packages/ai/tests/cache.test.ts81
-rw-r--r--packages/ai/tests/example.ts279
-rw-r--r--packages/ai/tests/integration.test.ts481
-rw-r--r--packages/ai/tests/models.test.ts994
-rw-r--r--packages/ai/tests/performance.test.ts465
-rw-r--r--packages/ai/tests/setup.ts180
-rw-r--r--packages/ai/tests/vllm.ts23
-rw-r--r--packages/ai/tsconfig.json27
-rw-r--r--packages/db/.gitignore34
-rw-r--r--packages/db/CLAUDE.md106
-rw-r--r--packages/db/README.md15
-rw-r--r--packages/db/bun.lock25
-rw-r--r--packages/db/index.ts3
-rw-r--r--packages/db/package.json11
-rw-r--r--packages/db/src/index.ts253
-rw-r--r--packages/db/src/migration.ts430
-rw-r--r--packages/db/src/phonetics.ts523
-rw-r--r--packages/db/src/schema.sql729
-rw-r--r--packages/db/src/semantic.ts554
-rw-r--r--packages/db/src/srs.ts402
-rw-r--r--packages/db/src/test.ts149
-rw-r--r--packages/db/src/types.ts84
-rw-r--r--packages/db/src/users.ts305
-rw-r--r--packages/db/tsconfig.json29
-rw-r--r--packages/lang/.gitignore34
-rw-r--r--packages/lang/CLAUDE.md106
-rw-r--r--packages/lang/README.md23
-rw-r--r--packages/lang/bun.lock64
-rw-r--r--packages/lang/package.json14
-rw-r--r--packages/lang/src/index.ts3
-rw-r--r--packages/lang/src/iso/index.ts17
-rw-r--r--packages/lang/src/iso/iso15924.ts1508
-rw-r--r--packages/lang/src/iso/iso6393-to-1.js192
-rw-r--r--packages/lang/src/iso/iso6393-to-2b.js428
-rw-r--r--packages/lang/src/iso/iso6393-to-2t.js427
-rw-r--r--packages/lang/src/iso/iso6393.js48289
-rw-r--r--packages/lang/src/lang/index.ts11
-rw-r--r--packages/lang/src/types.ts50
-rw-r--r--packages/lang/src/unicode/index.ts1424
-rw-r--r--packages/lang/test.ts24
-rw-r--r--packages/lang/tsconfig.json29
-rw-r--r--packages/prosody-ui/.gitignore175
-rw-r--r--packages/prosody-ui/README.md15
-rw-r--r--packages/prosody-ui/bun.lock318
-rw-r--r--packages/prosody-ui/index.ts6
-rw-r--r--packages/prosody-ui/package.json22
-rw-r--r--packages/prosody-ui/src/LangText.tsx78
-rw-r--r--packages/prosody-ui/src/Paragraph.tsx56
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/BeiShiDaJiaGuWenZiTi-1.ttfbin0 -> 1984156 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/GaiLiangShouJinTi-2.ttfbin0 -> 4827080 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/GuDianMingChaoKai-2.ttfbin0 -> 30564660 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/Iansui-Regular.ttfbin0 -> 9396328 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/LiuJianMaoCao-Regular.ttfbin0 -> 4940224 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/LongCang-Regular.ttfbin0 -> 5151180 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/Meirenzhuan.ttfbin0 -> 7336648 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/No.300-ShangShouGuHuangTi-2.ttfbin0 -> 4890264 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Black.ttfbin0 -> 7082052 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Bold.ttfbin0 -> 7088136 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraBold.ttfbin0 -> 7084512 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraLight.ttfbin0 -> 7095176 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Light.ttfbin0 -> 7095576 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Medium.ttfbin0 -> 7089444 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Regular.ttfbin0 -> 7093428 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-SemiBold.ttfbin0 -> 7086788 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Thin.ttfbin0 -> 7092016 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-VariableFont_wght.ttfbin0 -> 11909304 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/YeZiGongChangZuiHanJiangXingCao-2.ttfbin0 -> 5486956 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/YiShanBeiZhuanTi.ttfbin0 -> 2314256 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/ZhiMangXing-Regular.ttfbin0 -> 4052388 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/dingliezhuhaifont-20240831GengXinBan)-2.ttfbin0 -> 5359608 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/style.css223
-rw-r--r--packages/prosody-ui/src/assets/fonts/Hani/字魂天龙行楷.ttfbin0 -> 3344412 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/Judson-Bold.ttfbin0 -> 192452 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/Judson-Italic.ttfbin0 -> 209028 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/Judson-Regular.ttfbin0 -> 217096 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/NotoSans-Italic-VariableFont_wdth,wght.ttfbin0 -> 2637272 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/NotoSans-VariableFont_wdth,wght.ttfbin0 -> 2490816 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/IPA/Voces-Regular.ttfbin0 -> 119992 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/IPA/style.css33
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/DelaGothicOne-Regular.ttfbin0 -> 2487960 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Bold.ttfbin0 -> 4256124 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Medium.ttfbin0 -> 4265764 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Regular.ttfbin0 -> 4472380 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/ZenAntiqueSoft-Regular.ttfbin0 -> 7095584 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/ZenKurenaido-Regular.ttfbin0 -> 4303064 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Jpan/style.css223
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/OFL.txt93
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Black.ttfbin0 -> 151396 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BlackItalic.ttfbin0 -> 171604 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Bold.ttfbin0 -> 153944 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BoldItalic.ttfbin0 -> 176588 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBold.ttfbin0 -> 152764 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBoldItalic.ttfbin0 -> 173916 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLight.ttfbin0 -> 161456 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLightItalic.ttfbin0 -> 186168 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Italic.ttfbin0 -> 182012 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Light.ttfbin0 -> 159892 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-LightItalic.ttfbin0 -> 184460 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Medium.ttfbin0 -> 156520 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-MediumItalic.ttfbin0 -> 180444 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Regular.ttfbin0 -> 158240 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBold.ttfbin0 -> 155232 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBoldItalic.ttfbin0 -> 178584 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Thin.ttfbin0 -> 161652 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ThinItalic.ttfbin0 -> 187044 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Latn/style.css11
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Charm-Bold.ttfbin0 -> 135416 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Charm-Regular.ttfbin0 -> 134652 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Chonburi-Regular.ttfbin0 -> 168404 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Bold.ttfbin0 -> 113924 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-ExtraLight.ttfbin0 -> 117248 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Light.ttfbin0 -> 115728 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Medium.ttfbin0 -> 113640 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Regular.ttfbin0 -> 113392 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-SemiBold.ttfbin0 -> 113856 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Thin.ttfbin0 -> 118256 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Black.ttfbin0 -> 173492 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-BlackItalic.ttfbin0 -> 182076 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Bold.ttfbin0 -> 172876 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-BoldItalic.ttfbin0 -> 180308 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBold.ttfbin0 -> 174464 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBoldItalic.ttfbin0 -> 184928 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLight.ttfbin0 -> 160796 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLightItalic.ttfbin0 -> 164908 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Italic.ttfbin0 -> 171876 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Light.ttfbin0 -> 168036 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-LightItalic.ttfbin0 -> 171596 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Medium.ttfbin0 -> 171336 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-MediumItalic.ttfbin0 -> 172360 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Regular.ttfbin0 -> 169744 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBold.ttfbin0 -> 171548 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBoldItalic.ttfbin0 -> 172244 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-Thin.ttfbin0 -> 155788 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kanit-ThinItalic.ttfbin0 -> 161688 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Bold.ttfbin0 -> 98600 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-BoldItalic.ttfbin0 -> 104048 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLight.ttfbin0 -> 96992 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLightItalic.ttfbin0 -> 101308 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Italic.ttfbin0 -> 103088 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Light.ttfbin0 -> 98168 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-LightItalic.ttfbin0 -> 102364 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Medium.ttfbin0 -> 98740 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-MediumItalic.ttfbin0 -> 103552 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Regular.ttfbin0 -> 98392 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBold.ttfbin0 -> 98824 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBoldItalic.ttfbin0 -> 104024 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-Bold.ttfbin0 -> 105372 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-BoldItalic.ttfbin0 -> 108796 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLight.ttfbin0 -> 105668 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLightItalic.ttfbin0 -> 108572 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-Italic.ttfbin0 -> 108960 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-Light.ttfbin0 -> 105864 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-LightItalic.ttfbin0 -> 108732 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-Medium.ttfbin0 -> 105988 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-MediumItalic.ttfbin0 -> 109008 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-Regular.ttfbin0 -> 105896 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBold.ttfbin0 -> 106032 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBoldItalic.ttfbin0 -> 109000 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Bold.ttfbin0 -> 82592 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-BoldItalic.ttfbin0 -> 85636 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBold.ttfbin0 -> 82632 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBoldItalic.ttfbin0 -> 85448 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLight.ttfbin0 -> 83276 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLightItalic.ttfbin0 -> 86368 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Italic.ttfbin0 -> 86024 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Light.ttfbin0 -> 83224 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-LightItalic.ttfbin0 -> 86072 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Medium.ttfbin0 -> 83080 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-MediumItalic.ttfbin0 -> 86136 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Regular.ttfbin0 -> 83080 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBold.ttfbin0 -> 82952 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBoldItalic.ttfbin0 -> 86020 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-Thin.ttfbin0 -> 83408 bytes
-rwxr-xr-xpackages/prosody-ui/src/assets/fonts/Thai/Sarabun-ThinItalic.ttfbin0 -> 86344 bytes
-rw-r--r--packages/prosody-ui/src/assets/fonts/Thai/style.css76
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/bookmark.svg4
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/font.svg2
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/heart.svg4
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/play.svg4
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/quote.svg28
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/react.svg1
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/share.svg28
-rw-r--r--packages/prosody-ui/src/assets/icons/speaker.svg32
-rwxr-xr-xpackages/prosody-ui/src/assets/icons/spinner.svg1
-rw-r--r--packages/prosody-ui/src/components/Sentence.tsx57
-rw-r--r--packages/prosody-ui/src/components/Word.tsx119
-rw-r--r--packages/prosody-ui/src/components/sentence.css272
-rw-r--r--packages/prosody-ui/src/components/word.css0
-rw-r--r--packages/prosody-ui/src/files.d.ts4
-rw-r--r--packages/prosody-ui/src/fonts/FontChanger.tsx64
-rw-r--r--packages/prosody-ui/src/fonts/Hani.tsx14
-rw-r--r--packages/prosody-ui/src/fonts/Thai.tsx8
-rw-r--r--packages/prosody-ui/src/fonts/useLangFont.tsx44
-rw-r--r--packages/prosody-ui/src/hooks/useLang.tsx184
-rw-r--r--packages/prosody-ui/src/hooks/useModal.tsx53
-rw-r--r--packages/prosody-ui/src/hooks/useTTS.tsx3
-rw-r--r--packages/prosody-ui/src/latin/LatinText.tsx77
-rw-r--r--packages/prosody-ui/src/logic/iso6393to1.ts186
-rw-r--r--packages/prosody-ui/src/logic/stanza.ts86
-rw-r--r--packages/prosody-ui/src/logic/types.ts48
-rw-r--r--packages/prosody-ui/src/logic/utils.ts66
-rw-r--r--packages/prosody-ui/src/logic/wiki.ts138
-rw-r--r--packages/prosody-ui/src/sortug.css248
-rw-r--r--packages/prosody-ui/src/styles/styles.css281
-rw-r--r--packages/prosody-ui/src/thai/ThaiText.tsx49
-rw-r--r--packages/prosody-ui/src/thai/logic/thainlp.ts90
-rw-r--r--packages/prosody-ui/src/zoom/FullText.tsx60
-rw-r--r--packages/prosody-ui/src/zoom/Paragraph.tsx60
-rw-r--r--packages/prosody-ui/src/zoom/Sentence.tsx46
-rw-r--r--packages/prosody-ui/src/zoom/SpacyClause.tsx125
-rw-r--r--packages/prosody-ui/src/zoom/animations.ts199
-rw-r--r--packages/prosody-ui/src/zoom/hooks/useZoom.tsx135
-rw-r--r--packages/prosody-ui/src/zoom/index.ts8
-rw-r--r--packages/prosody-ui/src/zoom/logic/types.ts53
-rw-r--r--packages/prosody-ui/src/zoom/spacy.css39
-rw-r--r--packages/prosody-ui/tsconfig.json27
-rw-r--r--packages/sortug/.gitignore34
-rw-r--r--packages/sortug/README.md15
-rw-r--r--packages/sortug/bun.lock25
-rw-r--r--packages/sortug/index.ts4
-rw-r--r--packages/sortug/package.json11
-rw-r--r--packages/sortug/src/styles.module.css247
-rw-r--r--packages/sortug/src/types.ts9
-rw-r--r--packages/sortug/src/utils.ts87
-rw-r--r--packages/sortug/tsconfig.json28
-rw-r--r--packages/tweetdeck/.gitignore35
-rw-r--r--packages/tweetdeck/AGENTS.md109
-rw-r--r--packages/tweetdeck/GEMINI.md71
-rw-r--r--packages/tweetdeck/NOTES.md3
-rw-r--r--packages/tweetdeck/README.md21
-rw-r--r--packages/tweetdeck/bun-env.d.ts17
-rw-r--r--packages/tweetdeck/bun.lock410
-rw-r--r--packages/tweetdeck/bunfig.toml2
-rw-r--r--packages/tweetdeck/package.json26
-rw-r--r--packages/tweetdeck/src/APITester.tsx39
-rw-r--r--packages/tweetdeck/src/App.tsx310
-rw-r--r--packages/tweetdeck/src/components/AddColumnModal.tsx234
-rw-r--r--packages/tweetdeck/src/components/ChatCard.tsx56
-rw-r--r--packages/tweetdeck/src/components/ChatColumn.tsx62
-rw-r--r--packages/tweetdeck/src/components/ColumnBoard.tsx93
-rw-r--r--packages/tweetdeck/src/components/FullscreenColumn.tsx134
-rw-r--r--packages/tweetdeck/src/components/Sidebar.tsx153
-rw-r--r--packages/tweetdeck/src/components/TimelineColumn.tsx500
-rw-r--r--packages/tweetdeck/src/components/TweetCard.tsx337
-rw-r--r--packages/tweetdeck/src/frontend.tsx26
-rw-r--r--packages/tweetdeck/src/hooks/usePersistentState.ts39
-rw-r--r--packages/tweetdeck/src/index.html16
-rw-r--r--packages/tweetdeck/src/index.ts242
-rw-r--r--packages/tweetdeck/src/lib/client/twitterClient.ts75
-rw-r--r--packages/tweetdeck/src/lib/fetching/python.ts52
-rw-r--r--packages/tweetdeck/src/lib/fetching/twitter-api.ts1178
-rw-r--r--packages/tweetdeck/src/lib/fetching/types.ts596
-rw-r--r--packages/tweetdeck/src/lib/utils/id.ts4
-rw-r--r--packages/tweetdeck/src/lib/utils/time.ts18
-rw-r--r--packages/tweetdeck/src/logo.svg1
-rw-r--r--packages/tweetdeck/src/react.svg8
-rw-r--r--packages/tweetdeck/src/styles/index.css835
-rw-r--r--packages/tweetdeck/src/styles/normalize.css379
-rw-r--r--packages/tweetdeck/src/types/app.ts92
-rw-r--r--packages/tweetdeck/test.sh1
-rw-r--r--packages/tweetdeck/tests/js.js185
-rw-r--r--packages/tweetdeck/tests/python.ts150
-rw-r--r--packages/tweetdeck/tsconfig.json36
-rw-r--r--packages/tweetdeck/twatter-cookies.ts4
-rw-r--r--tsconfig.json29
298 files changed, 73318 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4ab4d11
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# dependencies (bun install)
+node_modules
+bulkdata
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..1ee6890
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,106 @@
+
+Default to using Bun instead of Node.js.
+
+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
+- Use `bun test` instead of `jest` or `vitest`
+- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
+- Bun automatically loads .env, so don't use dotenv.
+
+## APIs
+
+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
+- `Bun.redis` for Redis. Don't use `ioredis`.
+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
+- `WebSocket` is built-in. Don't use `ws`.
+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
+- Bun.$`ls` instead of execa.
+
+## Testing
+
+Use `bun test` to run tests.
+
+```ts#index.test.ts
+import { test, expect } from "bun:test";
+
+test("hello world", () => {
+ expect(1).toBe(1);
+});
+```
+
+## Frontend
+
+Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
+
+Server:
+
+```ts#index.ts
+import index from "./index.html"
+
+Bun.serve({
+ routes: {
+ "/": index,
+ "/api/users/:id": {
+ GET: (req) => {
+ return new Response(JSON.stringify({ id: req.params.id }));
+ },
+ },
+ },
+ // optional websocket support
+ websocket: {
+ open: (ws) => {
+ ws.send("Hello, world!");
+ },
+ message: (ws, message) => {
+ ws.send(message);
+ },
+ close: (ws) => {
+ // handle close
+ }
+ },
+ development: {
+ hmr: true,
+ console: true,
+ }
+})
+```
+
+HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
+
+```html#index.html
+<html>
+ <body>
+ <h1>Hello, world!</h1>
+ <script type="module" src="./frontend.tsx"></script>
+ </body>
+</html>
+```
+
+With the following `frontend.tsx`:
+
+```tsx#frontend.tsx
+import React from "react";
+
+// import .css files directly and it works
+import './index.css';
+
+import { createRoot } from "react-dom/client";
+
+const root = createRoot(document.body);
+
+export default function Frontend() {
+ return <h1>Hello, world!</h1>;
+}
+
+root.render(<Frontend />);
+```
+
+Then, run index.ts
+
+```sh
+bun --hot ./index.ts
+```
+
+For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fa81e45
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# sorlang
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
diff --git a/index.ts b/index.ts
new file mode 100644
index 0000000..f67b2c6
--- /dev/null
+++ b/index.ts
@@ -0,0 +1 @@
+console.log("Hello via Bun!"); \ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a40e1c4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "@sortug/sorlang",
+ "version": "0.1.0",
+ "workspaces": ["packages/*"],
+ "type": "module",
+ "private": true,
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ }
+}
diff --git a/packages/ai/.gitignore b/packages/ai/.gitignore
new file mode 100644
index 0000000..38065fd
--- /dev/null
+++ b/packages/ai/.gitignore
@@ -0,0 +1,175 @@
+# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
+cache
+# Logs
+runtest.sh
+logs
+_.log
+npm-debug.log_
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Caches
+
+.cache
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# Runtime data
+
+pids
+_.pid
+_.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+
+lib-cov
+
+# Coverage directory used by tools like istanbul
+
+coverage
+*.lcov
+
+# nyc test coverage
+
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+
+bower_components
+
+# node-waf configuration
+
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+
+build/Release
+
+# Dependency directories
+
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+
+web_modules/
+
+# TypeScript cache
+
+*.tsbuildinfo
+
+# Optional npm cache directory
+
+.npm
+
+# Optional eslint cache
+
+.eslintcache
+
+# Optional stylelint cache
+
+.stylelintcache
+
+# Microbundle cache
+
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+
+.node_repl_history
+
+# Output of 'npm pack'
+
+*.tgz
+
+# Yarn Integrity file
+
+.yarn-integrity
+
+# dotenv environment variable files
+
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+
+.parcel-cache
+
+# Next.js build output
+
+.next
+out
+
+# Nuxt.js build / generate output
+
+.nuxt
+dist
+
+# Gatsby files
+
+# Comment in the public line in if your project uses Gatsby and not Next.js
+
+# https://nextjs.org/blog/next-9-1#public-directory-support
+
+# public
+
+# vuepress build output
+
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+
+.temp
+
+# Docusaurus cache and generated files
+
+.docusaurus
+
+# Serverless directories
+
+.serverless/
+
+# FuseBox cache
+
+.fusebox/
+
+# DynamoDB Local files
+
+.dynamodb/
+
+# TernJS port file
+
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+
+.vscode-test
+
+# yarn v2
+
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/ai/README.md b/packages/ai/README.md
new file mode 100644
index 0000000..b89d245
--- /dev/null
+++ b/packages/ai/README.md
@@ -0,0 +1,15 @@
+# models
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
diff --git a/packages/ai/bun.lock b/packages/ai/bun.lock
new file mode 100644
index 0000000..8cdbaaf
--- /dev/null
+++ b/packages/ai/bun.lock
@@ -0,0 +1,361 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "@sortug/ai",
+ "dependencies": {
+ "@anthropic-ai/sdk": "latest",
+ "@elevenlabs/elevenlabs-js": "^2.24.1",
+ "@google/genai": "latest",
+ "bcp-47": "^2.1.0",
+ "franc-all": "^7.2.0",
+ "groq-sdk": "latest",
+ "iso-639-3": "file:../lang",
+ "openai": "latest",
+ "playht": "latest",
+ "replicate": "latest",
+ "sortug": "file:../sortug",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "@types/mime-types": "^3.0.1",
+ },
+ "peerDependencies": {
+ "typescript": "latest",
+ },
+ },
+ },
+ "packages": {
+ "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.70.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-AGEhifuvE22VxfQ5ROxViTgM8NuVQzEvqcN8bttR4AP24ythmNE/cL/SrOz79xiv7/osrsmCyErjsistJi7Z8A=="],
+
+ "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
+
+ "@elevenlabs/elevenlabs-js": ["@elevenlabs/elevenlabs-js@2.24.1", "", { "dependencies": { "command-exists": "^1.2.9", "node-fetch": "^2.7.0", "ws": "^8.18.3" } }, "sha512-i6bDExgK9lYne1vLhy85JJ3O8bNi5vPTfcgq8kT3HG4+3rgkUJtg5UP29Mn1KONc4ZOeYUomzxJ820uLkT9z6g=="],
+
+ "@google/genai": ["@google/genai@1.30.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.20.1" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w=="],
+
+ "@grpc/grpc-js": ["@grpc/grpc-js@1.14.1", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ=="],
+
+ "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
+
+ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
+
+ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
+
+ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
+
+ "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
+
+ "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
+
+ "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
+
+ "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
+
+ "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
+
+ "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
+
+ "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
+
+ "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
+
+ "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
+
+ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
+
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+
+ "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
+
+ "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="],
+
+ "@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
+
+ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
+
+ "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="],
+
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
+ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
+
+ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
+
+ "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+
+ "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
+
+ "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
+
+ "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+
+ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+
+ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
+
+ "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
+
+ "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+
+ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
+
+ "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
+
+ "command-exists": ["command-exists@1.2.9", "", {}, "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="],
+
+ "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
+
+ "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
+
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
+
+ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
+
+ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
+
+ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
+
+ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+ "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
+
+ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
+
+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+
+ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
+
+ "file-type": ["file-type@18.7.0", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", "token-types": "^5.0.1" } }, "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw=="],
+
+ "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
+
+ "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
+
+ "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
+
+ "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
+
+ "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
+
+ "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
+
+ "franc-all": ["franc-all@7.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-ZR6ciLQTDBaOvBdkOd8+vqDzaLtmIXRa9GCzcAlaBpqNAKg9QrtClPmqiKac5/xZXfCZGMo1d8dIu1T0BLhHEg=="],
+
+ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+ "gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
+
+ "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
+
+ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
+
+ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+
+ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+ "glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
+
+ "google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
+
+ "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
+
+ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+ "groq-sdk": ["groq-sdk@0.36.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-wvxl7i6QWxLcIfM00mQQybYk15OAXJG0NBBQuMDHrQ2vi68uz2RqFTBKUNfEOVz8Lwy4eAgQIPBEFW5P3cXybA=="],
+
+ "gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
+
+ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+
+ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
+
+ "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "iso-639-3": ["@sortug/lang@file:../lang", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
+ "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
+
+ "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
+
+ "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
+
+ "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
+
+ "jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="],
+
+ "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
+
+ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
+
+ "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+ "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+ "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="],
+
+ "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
+
+ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
+
+ "openai": ["openai@6.9.1", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-vQ5Rlt0ZgB3/BNmTa7bIijYFhz3YBceAA3Z4JuoMSBftBF9YqFHIEhZakSs+O/Ad7EaoEimZvHxD5ylRjN11Lg=="],
+
+ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+ "peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="],
+
+ "playht": ["playht@0.21.0", "", { "dependencies": { "@grpc/grpc-js": "^1.9.4", "axios": "^1.4.0", "cross-fetch": "^4.0.0", "deepmerge-ts": "^7.1.5", "file-type": "^18.5.0", "protobufjs": "^7.2.5", "tslib": "^2.1.0" } }, "sha512-63dWfsoGNOxfl91U3knrON4HcgtdPZ+e0Q3F8JX22T6dvX17i217lfw8cq1OzIBWVxpHms8ebhgUU/Gvs0/8Eg=="],
+
+ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
+
+ "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
+
+ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
+
+ "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+
+ "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="],
+
+ "replicate": ["replicate@1.4.0", "", { "optionalDependencies": { "readable-stream": ">=4.0.0" } }, "sha512-1ufKejfUVz/azy+5TnzQP7U1+MHVWZ6psnQ06az8byUUnRhT+DZ/MvewzB1NQYBVMgNKR7xPDtTwlcP5nv/5+w=="],
+
+ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
+
+ "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
+
+ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
+ "sortug": ["@sortug/lib@file:../sortug", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
+ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strtok3": ["strtok3@7.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.1.3" } }, "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg=="],
+
+ "token-types": ["token-types@5.0.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg=="],
+
+ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+
+ "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="],
+
+ "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
+
+ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
+
+ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
+
+ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
+
+ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
+
+ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+
+ "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
+
+ "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
+ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
+
+ "fetch-blob/web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
+
+ "gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
+
+ "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
+
+ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
+ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+ }
+}
diff --git a/packages/ai/debug.ts b/packages/ai/debug.ts
new file mode 100644
index 0000000..1b1312d
--- /dev/null
+++ b/packages/ai/debug.ts
@@ -0,0 +1,30 @@
+import OpenAI from "openai";
+import Claude from "@anthropic-ai/sdk";
+import { GoogleGenAI } from "@google/genai";
+async function oai() {
+ const openai = new OpenAI();
+
+ const list = await openai.models.list();
+ for await (const model of list) {
+ console.log({ model });
+ }
+}
+
+async function cld() {
+ const claude = new Claude();
+ const list = await claude.models.list();
+ for await (const model of list) {
+ console.log({ model });
+ }
+}
+
+async function gem() {
+ const gemini = new GoogleGenAI({ apiKey: Bun.env["GEMINI_API_KEY"]! });
+ const list = await gemini.models.list();
+ for await (const model of list) {
+ console.log({ model });
+ }
+}
+// oai();
+// cld();
+// gem();
diff --git a/packages/ai/index.ts b/packages/ai/index.ts
new file mode 100644
index 0000000..d2fc090
--- /dev/null
+++ b/packages/ai/index.ts
@@ -0,0 +1,76 @@
+// import Openai from "./src/openai";
+import Claude from "./src/claude";
+import Gemini from "./src/gemini";
+import Generic from "./src/generic";
+import OpenAIResponses from "./src/openai-responses";
+import type { AIModelAPI, LLMChoice } from "./src/types";
+
+export type * from "./src/types";
+export * as NLP from "./src/nlp";
+
+export default function (choice: LLMChoice): AIModelAPI {
+ try {
+ const api =
+ "gemini" in choice
+ ? new Gemini(choice.gemini)
+ : "claude" in choice
+ ? new Claude(choice.claude)
+ : "chatgpt" in choice
+ ? new OpenAIResponses({
+ baseURL: "https://api.openai.com/v1",
+ apiKey: Bun.env.OPENAI_API_KEY!,
+ model: choice.chatgpt || "gpt-5-nano",
+ })
+ : "deepseek" in choice
+ ? new Generic({
+ baseURL: "https://api.deepseek.com",
+ apiKey: Bun.env.DEEPSEEK_API_KEY!,
+ model: "deepseek-reasoner",
+ })
+ : "kimi" in choice
+ ? new Generic({
+ baseURL: "https://api.moonshot.ai/v1",
+ apiKey: Bun.env.MOONSHOT_API_KEY!,
+ model: "kimi-k2-0905-preview", // "kimi-latest"?
+ })
+ : "grok" in choice
+ ? new Generic({
+ baseURL: "https://api.x.ai/v1",
+ apiKey: Bun.env.XAI_API_KEY!,
+ model: "grok-4", // "kimi-latest"?
+ })
+ : new Generic({
+ baseURL: choice.openai.url,
+ apiKey: choice.openai.apiKey,
+ model: choice.openai.model,
+ allowBrowser: choice.openai.allowBrowser,
+ });
+ // "" in choice
+ // ? new Generic(choice.other)
+ // : choice.name === "deepseek"
+ // ? new Generic({
+ // baseURL: "https://api.deepseek.com",
+ // apiKey: Bun.env.DEEPSEEK_API_KEY!,
+ // model: "deepseek-chat",
+ // })
+ // : choice.name === "grok"
+ // ? new Generic({
+ // baseURL: "https://api.x.ai/v1",
+ // apiKey: Bun.env.GROK_API_KEY!,
+ // model: "grok-2-latest",
+ // })
+ // : choice.name === "chatgpt"
+ // ? new Generic({
+ // baseURL: "https://api.openai.com/v1",
+ // apiKey: Bun.env.OPENAI_API_KEY!,
+ // model: "gpt-4o",
+ // })
+ // : choice.name === "claude"
+ // ? new Claude()
+ // : new Gemini();
+ return api;
+ } catch (e) {
+ // TODO
+ console.error("couldnt start API", e);
+ }
+}
diff --git a/packages/ai/package.json b/packages/ai/package.json
new file mode 100644
index 0000000..f7a75be
--- /dev/null
+++ b/packages/ai/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@sortug/ai",
+ "module": "index.ts",
+ "type": "module",
+ "version": "0.1.0",
+ "devDependencies": {
+ "@types/bun": "latest",
+ "@types/mime-types": "^3.0.1"
+ },
+ "peerDependencies": {
+ "typescript": "latest"
+ },
+ "dependencies": {
+ "@anthropic-ai/sdk": "latest",
+ "@elevenlabs/elevenlabs-js": "^2.24.1",
+ "@google/genai": "latest",
+ "groq-sdk": "latest",
+ "openai": "latest",
+ "playht": "latest",
+ "replicate": "latest",
+ "@sortug/lib": "workspace:*",
+ "@sortug/langlib": "workspace:*"
+ }
+}
diff --git a/packages/ai/src/cache.ts b/packages/ai/src/cache.ts
new file mode 100644
index 0000000..5d59163
--- /dev/null
+++ b/packages/ai/src/cache.ts
@@ -0,0 +1,150 @@
+// memoize.ts (Bun-compatible, no Node Buffers)
+import { mkdir } from "node:fs/promises";
+import path from "node:path";
+
+type MemoOpts<V> = {
+ ttlMs?: number; // time-to-live for entries
+ maxEntries?: number; // cap; oldest (LRU) evicted
+ persistDir?: string; // set to enable disk cache (e.g. ".cache/memo")
+ keyFn?: (...args: any[]) => string; // custom key if you need it
+ cacheErrors?: boolean; // default false
+};
+
+type Entry<V> = {
+ v: V;
+ exp: number | null;
+ at: number; // last hit (LRU)
+};
+
+const enc = new TextEncoder();
+const dec = new TextDecoder();
+
+const stableStringify = (x: any): string => {
+ const seen = new WeakSet();
+ const S = (v: any): any => {
+ if (v && typeof v === "object") {
+ if (seen.has(v)) return "[Circular]";
+ seen.add(v);
+ if (Array.isArray(v)) return v.map(S);
+ return Object.fromEntries(
+ Object.keys(v)
+ .sort()
+ .map((k) => [k, S(v[k])]),
+ );
+ }
+ if (typeof v === "function") return `[Function:${v.name || "anon"}]`;
+ if (typeof v === "undefined") return "__undefined__";
+ return v;
+ };
+ return JSON.stringify(S(x));
+};
+
+async function sha256Hex(s: string) {
+ const h = await crypto.subtle.digest("SHA-256", enc.encode(s));
+ return Array.from(new Uint8Array(h))
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
+}
+
+function now() {
+ return Date.now();
+}
+
+export function memoize<
+ F extends (...args: any[]) => any,
+ V = Awaited<ReturnType<F>>,
+>(fn: F, opts: MemoOpts<V> = {}): F {
+ const ttl = opts.ttlMs ?? 0;
+ const max = opts.maxEntries ?? 0;
+ const dir = opts.persistDir ? path.resolve(opts.persistDir) : null;
+
+ const mem = new Map<string, Entry<V>>();
+ const inflight = new Map<string, Promise<V>>();
+
+ async function keyOf(args: any[]): Promise<string> {
+ const base = opts.keyFn ? opts.keyFn(...args) : stableStringify(args);
+ return dir ? await sha256Hex(base) : base; // hash when persisting (safe filename)
+ }
+
+ async function readDisk(k: string): Promise<Entry<V> | null> {
+ if (!dir) throw new Error("no dir!");
+ const f = Bun.file(path.join(dir, `${k}.json`));
+ if (!(await f.exists())) return null;
+ try {
+ const obj = JSON.parse(await f.text());
+ return obj as Entry<V>;
+ } catch {
+ return null;
+ }
+ }
+
+ async function writeDisk(k: string, e: Entry<V>) {
+ if (!dir) throw new Error("no dir!");
+ await Bun.write(path.join(dir, `${k}.json`), JSON.stringify(e));
+ }
+
+ function evictLRU() {
+ if (!max || mem.size <= max) return;
+ const arr = [...mem.entries()].sort((a, b) => a[1].at - b[1].at);
+ for (let i = 0; i < mem.size - max; i++) mem.delete(arr[i][0]);
+ }
+
+ async function getOrCall(args: any[]): Promise<V> {
+ const k = await keyOf(args);
+ const t = now();
+
+ // in-flight coalescing
+ if (inflight.has(k)) return inflight.get(k)!;
+
+ // memory hit
+ const m = mem.get(k);
+ if (m && (!m.exp || t < m.exp)) {
+ m.at = t;
+ return m.v;
+ }
+
+ // disk hit
+ const d = await readDisk(k);
+ if (d && (!d.exp || t < d.exp)) {
+ d.at = t;
+ mem.set(k, d);
+ evictLRU();
+ return d.v;
+ }
+
+ // miss → call underlying
+ const call = (async () => {
+ try {
+ const r = fn.apply(undefined, args);
+ const v: V = r instanceof Promise ? await r : (r as V);
+ const e: Entry<V> = { v, exp: ttl ? t + ttl : null, at: t };
+ mem.set(k, e);
+ evictLRU();
+ await writeDisk(k, e);
+ return v;
+ } catch (err) {
+ if (opts.cacheErrors) {
+ const e: Entry<any> = { v: err, exp: ttl ? t + ttl : null, at: t };
+ mem.set(k, e);
+ await writeDisk(k, e as Entry<V>);
+ }
+ throw err;
+ } finally {
+ inflight.delete(k);
+ }
+ })();
+
+ inflight.set(k, call);
+ return call;
+ }
+
+ // Wrap preserving arity & `this` for methods
+ const wrapped = function (this: any, ...args: any[]) {
+ const maybe = getOrCall(args).then((v) => v);
+ // If original fn is sync (per your signature), unwrap to sync only when it's truly sync.
+ // We can detect by calling without awaiting once—dangerous—so be conservative:
+ return maybe as unknown as ReturnType<F>;
+ } as any as F;
+
+ return wrapped;
+}
diff --git a/packages/ai/src/claude.ts b/packages/ai/src/claude.ts
new file mode 100644
index 0000000..a411030
--- /dev/null
+++ b/packages/ai/src/claude.ts
@@ -0,0 +1,173 @@
+import Claude from "@anthropic-ai/sdk";
+import { RESPONSE_LENGTH } from "./logic/constants";
+import type { AIModelAPI, ChatMessage, InputToken } from "./types";
+import { BOOKWORM_SYS } from "./prompts";
+import type { AsyncRes } from "@sortug/lib";
+import type {
+ ImageBlockParam,
+ MessageCreateParamsStreaming,
+ TextBlockParam,
+} from "@anthropic-ai/sdk/resources";
+
+type Message = Claude.Messages.MessageParam;
+
+export default class ClaudeAPI implements AIModelAPI {
+ private model: string = "claude-opus-4-20250514";
+ tokenizer: (text: string) => number;
+ maxTokens: number;
+ // model: string = "claude-3-5-sonnet-20241022";
+ constructor(
+ model: string,
+ maxTokens = 200_000,
+ tokenizer: (text: string) => number = (text) => text.length / 3,
+ ) {
+ this.maxTokens = maxTokens;
+ this.tokenizer = tokenizer;
+ if (model) this.model = model;
+ }
+ public setModel(model: string) {
+ this.model = model;
+ }
+ private mapMessages(input: ChatMessage[]): Message[] {
+ return input.map((m) => {
+ const role = m.author === "claude" ? "assistant" : "user";
+ return { role, content: m.text };
+ });
+ }
+ private buildInput(tokens: InputToken[]): Message[] {
+ // can do base64 for images too
+ const content = tokens.map((t) => {
+ const content =
+ "text" in t
+ ? ({ type: "text", text: t.text } as TextBlockParam)
+ : "img" in t
+ ? ({
+ type: "image",
+ source: { type: "url", url: t.img },
+ } as ImageBlockParam)
+ : ({ type: "text", text: "oy vey" } as TextBlockParam);
+ return content;
+ });
+
+ return [{ role: "user", content }];
+ }
+
+ // https://docs.anthropic.com/en/api/messages-examples#vision
+ public async send(input: string | InputToken[], sys?: string) {
+ const msgs: Message[] =
+ typeof input === "string"
+ ? [{ role: "user", content: input }]
+ : this.buildInput(input);
+ const truncated = this.truncateHistory(msgs);
+ const res = await this.apiCall(truncated, sys);
+ return res;
+ }
+
+ public async sendDoc(data: string) {
+ const sys = BOOKWORM_SYS;
+ const msg: Message = {
+ role: "user",
+ content: [
+ {
+ type: "document",
+ source: { type: "base64", data, media_type: "application/pdf" },
+ },
+ {
+ type: "text",
+ text: "Please analyze this according to your system prompt. Be thorough.",
+ },
+ ],
+ };
+ const res = await this.apiCall([msg], sys);
+ return res;
+ }
+
+ public async stream(
+ input: string | InputToken[],
+ handle: (c: any) => void,
+ sys?: string,
+ ) {
+ const msgs: Message[] =
+ typeof input === "string"
+ ? [{ role: "user", content: input }]
+ : this.buildInput(input);
+ const truncated = this.truncateHistory(msgs);
+ await this.apiCallStream(truncated, handle, sys);
+ }
+
+ private truncateHistory(messages: Message[]): Message[] {
+ const totalTokens = messages.reduce((total, message) => {
+ return total + this.tokenizer(message.content as string);
+ }, 0);
+ while (totalTokens > this.maxTokens && messages.length > 1) {
+ messages.splice(0, 1);
+ }
+ return messages;
+ }
+
+ // TODO
+ // https://docs.anthropic.com/en/api/messages-examples#putting-words-in-claudes-mouth
+ private async apiCall(
+ messages: Message[],
+ system?: string,
+ ): Promise<AsyncRes<string>> {
+ try {
+ const claud = new Claude();
+ const params = {
+ model: this.model,
+ max_tokens: RESPONSE_LENGTH,
+ messages,
+ };
+ const res = await claud.messages.create(
+ system ? { ...params, system } : params,
+ );
+ const resm: string = res.content.reduce((acc: string, item) => {
+ if (item.type === "tool_use") return acc;
+ else if (item.type === "text") return `${acc}\n${item.text}`;
+ else return acc;
+ }, "");
+ // const resm = res.content.reduce((acc: string[], item) => {
+ // if (item.type === "tool_use") return acc;
+ // else if (item.type === "text") return [...acc, item.text]
+ // else return acc;
+ // }, []);
+ return { ok: resm };
+ } catch (e) {
+ console.log(e, "error in claude api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ messages: Message[],
+ handle: (c: any) => void,
+ system?: string,
+ ): Promise<void> {
+ try {
+ const claud = new Claude();
+ const params = {
+ model: this.model,
+ max_tokens: RESPONSE_LENGTH,
+ messages,
+ stream: true as true,
+ };
+ const fparams: MessageCreateParamsStreaming = system
+ ? { ...params, system }
+ : params;
+ const stream = await claud.messages.create(fparams);
+
+ for await (const part of stream) {
+ if (part.type === "message_start") continue;
+ if (part.type === "content_block_start") continue;
+ if (part.type === "content_block_delta") {
+ console.log("delta", part.delta);
+ const delta: any = part.delta;
+ handle(delta.text);
+ }
+ }
+ } catch (e) {
+ console.log(e, "error in claude api");
+ handle(`Error streaming Claude, ${e}`);
+ }
+ }
+}
diff --git a/packages/ai/src/gemini.ts b/packages/ai/src/gemini.ts
new file mode 100644
index 0000000..d8010b0
--- /dev/null
+++ b/packages/ai/src/gemini.ts
@@ -0,0 +1,199 @@
+// import mime from "mime-types";
+import {
+ Chat,
+ createPartFromBase64,
+ createPartFromUri,
+ createUserContent,
+ GoogleGenAI,
+ type Content,
+ type ContentListUnion,
+ type GeneratedImage,
+ type GeneratedVideo,
+ type Part,
+} from "@google/genai";
+import type { AIModelAPI, InputToken } from "./types";
+import type { AsyncRes, Result } from "@sortug/lib";
+
+export default class GeminiAPI implements AIModelAPI {
+ tokenizer: (text: string) => number;
+ maxTokens: number;
+ private model: string;
+ api: GoogleGenAI;
+ chats: Map<string, Chat> = new Map<string, Chat>();
+
+ constructor(
+ model?: string,
+ maxTokens = 200_000,
+ tokenizer: (text: string) => number = (text) => text.length / 3,
+ ) {
+ this.maxTokens = maxTokens;
+ this.tokenizer = tokenizer;
+
+ const gem = new GoogleGenAI({ apiKey: Bun.env["GEMINI_API_KEY"]! });
+ this.api = gem;
+ this.model = model || "gemini-2.5-pro";
+ }
+
+ // input data in gemini gets pretty involved
+ //
+ // data
+ // Union type
+ // data can be only one of the following:
+ // text
+ // string
+ // Inline text.
+
+ // inlineData
+ // object (Blob)
+ // Inline media bytes.
+
+ // functionCall
+ // object (FunctionCall)
+ // A predicted FunctionCall returned from the model that contains a string representing the FunctionDeclaration.name with the arguments and their values.
+
+ // functionResponse
+ // object (FunctionResponse)
+ // The result output of a FunctionCall that contains a string representing the FunctionDeclaration.name and a structured JSON object containing any output from the function is used as context to the model.
+
+ // fileData
+ // object (FileData)
+ // URI based data.
+
+ // executableCode
+ // object (ExecutableCode)
+ // Code generated by the model that is meant to be executed.
+
+ // codeExecutionResult
+ // object (CodeExecutionResult)
+ // Result of executing the ExecutableCode.
+
+ // metadata
+ // Union type
+ public setModel(model: string) {
+ this.model = model;
+ }
+ private contentFromImage(imageString: string): Result<Part> {
+ // TODO
+ // const mimeType = mime.lookup(imageString);
+ const mimeType = "";
+ if (!mimeType) return { error: "no mimetype" };
+ const url = URL.parse(imageString);
+ if (url) {
+ const part = createPartFromUri(imageString, mimeType);
+ return { ok: part };
+ } else return { ok: createPartFromBase64(imageString, mimeType) };
+ }
+ async inlineImage(imageURI: URL): AsyncRes<Part> {
+ try {
+ const imgdata = await fetch(imageURI);
+ const imageArrayBuffer = await imgdata.arrayBuffer();
+ const base64ImageData = Buffer.from(imageArrayBuffer).toString("base64");
+ const mimeType = imgdata.headers.get("content-type") || "image/jpeg";
+ return { ok: { inlineData: { mimeType, data: base64ImageData } } };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ public buildInput(tokens: InputToken[]): Result<Content> {
+ try {
+ const input = createUserContent(
+ tokens.map((t) => {
+ if ("text" in t) return t.text;
+ if ("img" in t) {
+ const imagePart = this.contentFromImage(t.img);
+ if ("error" in imagePart) throw new Error("image failed");
+ else return imagePart.ok;
+ }
+ return "oy vey";
+ }),
+ );
+ return { ok: input };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+
+ async send(
+ input: string | InputToken[],
+ systemPrompt?: string,
+ ): AsyncRes<string> {
+ let contents: ContentListUnion;
+ if (typeof input === "string") contents = input;
+ else {
+ const built = this.buildInput(input);
+ if ("error" in built) return built;
+ else contents = built.ok;
+ }
+ try {
+ const opts = {
+ model: this.model,
+ contents,
+ };
+ const fopts = systemPrompt
+ ? { ...opts, config: { systemInstruction: systemPrompt } }
+ : opts;
+ const response = await this.api.models.generateContent(fopts);
+ if (!response.text) return { error: "no text in response" };
+ return { ok: response.text };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ async stream(
+ input: string | InputToken[],
+ handler: (s: string) => void,
+ systemPrompt?: string,
+ ) {
+ let contents: ContentListUnion;
+ if (typeof input === "string") contents = input;
+ else {
+ const built = this.buildInput(input);
+ if ("error" in built) return built;
+ else contents = built.ok;
+ }
+ const opts = {
+ model: this.model,
+ contents,
+ };
+ const fopts = systemPrompt
+ ? { ...opts, config: { systemInstruction: systemPrompt } }
+ : opts;
+ const response = await this.api.models.generateContentStream(fopts);
+ for await (const chunk of response) {
+ handler(chunk.text || "");
+ }
+ }
+
+ async makeImage(prompt: string): AsyncRes<GeneratedImage[]> {
+ try {
+ const response = await this.api.models.generateImages({
+ model: this.model,
+ prompt,
+ });
+ // TODO if empty or undefined return error
+ return { ok: response.generatedImages || [] };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ async makeVideo({
+ prompt,
+ image,
+ }: {
+ prompt?: string;
+ image?: string;
+ }): AsyncRes<GeneratedVideo[]> {
+ try {
+ const response = await this.api.models.generateVideos({
+ model: this.model,
+ prompt,
+ });
+ // TODO if empty or undefined return error
+ return { ok: response.response?.generatedVideos || [] };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+}
+// TODO how to use caches
+// https://ai.google.dev/api/caching
diff --git a/packages/ai/src/gemini2.ts b/packages/ai/src/gemini2.ts
new file mode 100644
index 0000000..0b7c0da
--- /dev/null
+++ b/packages/ai/src/gemini2.ts
@@ -0,0 +1,149 @@
+import {
+ GenerativeModel,
+ GoogleGenerativeAI,
+ type Content,
+ type GenerateContentResult,
+} from "@google/generative-ai";
+import { RESPONSE_LENGTH } from "./logic/constants";
+import type {
+ AIModelAPI,
+ ChatMessage,
+ OChoice,
+ OChunk,
+ OMessage,
+} from "./types";
+import type { AsyncRes } from "@sortug/lib";
+
+export default class GeminiAPI implements AIModelAPI {
+ tokenizer: (text: string) => number;
+ maxTokens: number;
+ private model: GenerativeModel;
+
+ constructor(
+ maxTokens = 200_000,
+ tokenizer: (text: string) => number = (text) => text.length / 3,
+ model?: string,
+ ) {
+ this.maxTokens = maxTokens;
+ this.tokenizer = tokenizer;
+
+ const gem = new GoogleGenerativeAI(Bun.env["GEMINI_API_KEY"]!);
+ this.model = gem.getGenerativeModel({
+ // model: model || "gemini-2.0-flash-exp",
+ model: model || "gemini-2.5-pro-preview-05-06 ",
+ generationConfig: { maxOutputTokens: RESPONSE_LENGTH },
+ });
+ }
+
+ public setModel(model: string) {
+ const gem = new GoogleGenerativeAI(Bun.env["GEMINI_API_KEY"]!);
+ this.model = gem.getGenerativeModel({
+ model,
+ generationConfig: { maxOutputTokens: RESPONSE_LENGTH },
+ });
+ }
+ private mapMessages(input: ChatMessage[]): Content[] {
+ return input.map((m) => ({
+ role: m.author === "gemini" ? "model" : "user",
+ parts: [{ text: m.text }],
+ }));
+ }
+
+ private mapMessagesR1(input: ChatMessage[]): Content[] {
+ return input.reduce((acc: Content[], m, i) => {
+ const prev = acc[i - 1];
+ const role = m.author === "gemini" ? "model" : "user";
+ const msg = { role, parts: [{ text: m.text }] };
+ if (prev?.role === role) acc[i - 1] = msg;
+ else acc = [...acc, msg];
+ return acc;
+ }, []);
+ }
+
+ private async apiCall(
+ messages: Content[],
+ isR1: boolean = false,
+ ): Promise<AsyncRes<string[]>> {
+ try {
+ const chat = this.model.startChat({ history: messages });
+ const res = await chat.sendMessage("");
+ return { ok: [res.response.text()] };
+ } catch (e) {
+ console.log(e, "error in gemini api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ messages: Content[],
+ handle: (c: any) => void,
+ isR1: boolean = false,
+ ): Promise<void> {
+ try {
+ const chat = this.model.startChat({ history: messages });
+ const res = await chat.sendMessage("");
+ // for await (const chunk of res.stream()) {
+ // handle(chunk.text());
+ // }
+ } catch (e) {
+ console.log(e, "error in gemini api");
+ handle(`Error streaming Gemini, ${e}`);
+ }
+ }
+
+ public async send(sys: string, input: ChatMessage[]) {
+ console.log({ sys, input });
+ this.model.systemInstruction = { role: "system", parts: [{ text: sys }] };
+ const messages = this.mapMessages(input);
+ const truncated = this.truncateHistory(messages);
+ const res = await this.apiCall(truncated);
+ return res;
+ }
+
+ public async sendR1(input: ChatMessage[]) {
+ const messages = this.mapMessagesR1(input);
+ const truncated = this.truncateHistory(messages);
+ const res = await this.apiCall(truncated, true);
+ return res;
+ }
+
+ public async stream(
+ sys: string,
+ input: ChatMessage[],
+ handle: (c: any) => void,
+ ) {
+ this.model.systemInstruction = { role: "system", parts: [{ text: sys }] };
+ const messages = this.mapMessages(input);
+ const truncated = this.truncateHistory(messages);
+ await this.apiCallStream(truncated, handle);
+ }
+
+ public async streamR1(input: ChatMessage[], handle: (c: any) => void) {
+ const messages = this.mapMessagesR1(input);
+ const truncated = this.truncateHistory(messages);
+ await this.apiCallStream(truncated, handle, true);
+ }
+
+ public async sendDoc(data: ArrayBuffer, mimeType: string, prompt: string) {
+ const res = await this.model.generateContent([
+ {
+ inlineData: {
+ data: Buffer.from(data).toString("base64"),
+ mimeType,
+ },
+ },
+ prompt,
+ ]);
+ return res;
+ }
+
+ private truncateHistory(messages: Content[]): Content[] {
+ const totalTokens = messages.reduce((total, message) => {
+ return total + this.tokenizer(message.parts[0].text || "");
+ }, 0);
+ while (totalTokens > this.maxTokens && messages.length > 1) {
+ messages.splice(0, 1);
+ }
+ return messages;
+ }
+}
diff --git a/packages/ai/src/generic.ts b/packages/ai/src/generic.ts
new file mode 100644
index 0000000..8c41f19
--- /dev/null
+++ b/packages/ai/src/generic.ts
@@ -0,0 +1,204 @@
+import OpenAI from "openai";
+import { MAX_TOKENS, RESPONSE_LENGTH } from "./logic/constants";
+import type { AIModelAPI, ChatMessage, InputToken } from "./types";
+import type { AsyncRes } from "@sortug/lib";
+import type { ChatCompletionContentPart } from "openai/resources";
+import { memoize } from "./cache";
+import type { ChatCompletionCreateParamsNonStreaming } from "groq-sdk/src/resources/chat/completions.js";
+
+type OChoice = OpenAI.Chat.Completions.ChatCompletion.Choice;
+type Message = OpenAI.Chat.Completions.ChatCompletionUserMessageParam;
+type Params = OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming;
+type OMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;
+
+type Props = {
+ baseURL: string;
+ apiKey: string;
+ model?: string;
+ maxTokens?: number;
+ tokenizer?: (text: string) => number;
+ allowBrowser?: boolean;
+};
+export default class OpenAIAPI implements AIModelAPI {
+ private cachedCreate!: (
+ args: Params,
+ ) => Promise<OpenAI.Chat.Completions.ChatCompletion>;
+
+ private apiKey;
+ private baseURL;
+ private api;
+ maxTokens: number = MAX_TOKENS;
+ tokenizer: (text: string) => number = (text) => text.length / 3;
+ model;
+
+ constructor(props: Props) {
+ this.apiKey = props.apiKey;
+ this.baseURL = props.baseURL;
+ this.api = new OpenAI({
+ baseURL: this.baseURL,
+ apiKey: this.apiKey,
+ dangerouslyAllowBrowser: props.allowBrowser || false,
+ });
+ this.model = props.model || "";
+ if (props.maxTokens) this.maxTokens = props.maxTokens;
+ if (props.tokenizer) this.tokenizer = props.tokenizer;
+
+ const boundCreate = this.api.chat.completions.create.bind(
+ this.api.chat.completions,
+ );
+
+ this.cachedCreate = memoize(boundCreate, {
+ ttlMs: 2 * 60 * 60 * 1000, // 2h
+ maxEntries: 5000,
+ persistDir: "./cache/memo",
+ // stable key for the call
+ keyFn: (args) => {
+ // args is the single object param to .create(...)
+ const {
+ model,
+ messages,
+ max_tokens,
+ temperature,
+ top_p,
+ frequency_penalty,
+ presence_penalty,
+ stop,
+ } = args as Params;
+ // stringify messages deterministically (role+content only)
+ const msg = (messages as any[])
+ .map((m) => ({ role: m.role, content: m.content }))
+ .slice(0, 200); // guard size if you want
+ return JSON.stringify({
+ model,
+ msg,
+ max_tokens,
+ temperature,
+ top_p,
+ frequency_penalty,
+ presence_penalty,
+ stop,
+ });
+ },
+ });
+ }
+ public setModel(model: string) {
+ this.model = model;
+ }
+ private mapMessages(input: ChatMessage[]): Message[] {
+ return input.map((m) => {
+ return { role: m.author as any, content: m.text, name: m.author };
+ });
+ }
+ private buildInput(tokens: InputToken[]): Message[] {
+ const content: ChatCompletionContentPart[] = tokens.map((t) => {
+ if ("text" in t) return { type: "text", text: t.text };
+ if ("img" in t) return { type: "image_url", image_url: { url: t.img } };
+ else return { type: "text", text: "oy vey" };
+ });
+ return [{ role: "user", content }];
+ }
+
+ public async send(
+ input: string | InputToken[],
+ sys?: string,
+ ): AsyncRes<string> {
+ const messages: Message[] =
+ typeof input === "string"
+ ? [{ role: "user" as const, content: input }]
+ : this.buildInput(input);
+ // const messages = this.mapMessages(input);
+ const allMessages: OMessage[] = sys
+ ? [{ role: "system", content: sys }, ...messages]
+ : messages;
+ const truncated = this.truncateHistory(allMessages);
+ const res = await this.apiCall(truncated);
+ if ("error" in res) return res;
+ else {
+ try {
+ // TODO type this properly
+ const choices: OChoice[] = res.ok;
+ const resText = choices.reduce((acc, item) => {
+ return `${acc}\n${item.message.content || ""}`;
+ }, "");
+ return { ok: resText };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ }
+
+ public async stream(
+ input: string | InputToken[],
+ handle: (c: string) => void,
+ sys?: string,
+ ) {
+ const messages: Message[] =
+ typeof input === "string"
+ ? [{ role: "user" as const, content: input }]
+ : this.buildInput(input);
+ // const messages = this.mapMessages(input);
+ const allMessages: OMessage[] = sys
+ ? [{ role: "system", content: sys }, ...messages]
+ : messages;
+ const truncated = this.truncateHistory(allMessages);
+ await this.apiCallStream(truncated, handle);
+ }
+
+ private truncateHistory(messages: OMessage[]): OMessage[] {
+ const totalTokens = messages.reduce((total, message) => {
+ return total + this.tokenizer(message.content as string);
+ }, 0);
+ while (totalTokens > this.maxTokens && messages.length > 1) {
+ // Always keep the system message if it exists
+ const startIndex = messages[0].role === "system" ? 1 : 0;
+ messages.splice(startIndex, 1);
+ }
+ return messages;
+ }
+
+ // TODO custom temperature?
+ private async apiCall(messages: OMessage[]): AsyncRes<OChoice[]> {
+ // console.log({ messages }, "at the very end");
+ try {
+ const completion = await this.cachedCreate({
+ // temperature: 1.3,
+ model: this.model,
+ messages,
+ max_tokens: RESPONSE_LENGTH,
+ });
+ if (!completion) return { error: "null response from openai" };
+ return { ok: completion.choices };
+ } catch (e) {
+ console.log(e, "error in openai api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ messages: OMessage[],
+ handle: (c: string) => void,
+ ): Promise<void> {
+ try {
+ const stream = await this.api.chat.completions.create({
+ temperature: 1.3,
+ model: this.model,
+ messages,
+ max_tokens: RESPONSE_LENGTH,
+ stream: true,
+ });
+
+ for await (const chunk of stream) {
+ for (const choice of chunk.choices) {
+ console.log({ choice });
+ if (!choice.delta) continue;
+ const cont = choice.delta.content;
+ if (!cont) continue;
+ handle(cont);
+ }
+ }
+ } catch (e) {
+ console.log(e, "error in openai api");
+ handle(`Error streaming OpenAI, ${e}`);
+ }
+ }
+}
diff --git a/packages/ai/src/genericnew.ts b/packages/ai/src/genericnew.ts
new file mode 100644
index 0000000..b8b4e94
--- /dev/null
+++ b/packages/ai/src/genericnew.ts
@@ -0,0 +1,169 @@
+import OpenAI from "openai";
+import { MAX_TOKENS, RESPONSE_LENGTH } from "./logic/constants";
+import type { AIModelAPI, InputToken } from "./types";
+import type { AsyncRes } from "@sortug/lib";
+import type {
+ ResponseCreateParamsBase,
+ ResponseCreateParamsNonStreaming,
+ ResponseCreateParamsStreaming,
+ ResponseInput,
+} from "openai/resources/responses/responses.mjs";
+
+type Props = {
+ baseURL: string;
+ apiKey: string | undefined;
+ model?: string;
+ maxTokens?: number;
+ tokenizer?: (text: string) => number;
+};
+export default class OpenAIAPI implements AIModelAPI {
+ private apiKey;
+ private baseURL;
+ private api;
+ maxTokens: number = MAX_TOKENS;
+ tokenizer: (text: string) => number = (text) => text.length / 3;
+ model;
+
+ constructor(props: Props) {
+ if (!props.apiKey) throw new Error("NO API KEY");
+ console.log({ props });
+ this.apiKey = props.apiKey;
+ this.baseURL = props.baseURL;
+ this.api = new OpenAI({ baseURL: this.baseURL, apiKey: this.apiKey });
+ this.model = props.model || "";
+ if (props.maxTokens) this.maxTokens = props.maxTokens;
+ if (props.tokenizer) this.tokenizer = props.tokenizer;
+ }
+ public setModel(model: string) {
+ this.model = model;
+ }
+
+ public buildInput(tokens: InputToken[]): ResponseInput {
+ return [
+ {
+ role: "user",
+ content: tokens.map((t) =>
+ "text" in t
+ ? { type: "input_text", text: t.text }
+ : "img" in t
+ ? { type: "input_image", image_url: t.img, detail: "auto" }
+ : { type: "input_text", text: "oy vey" },
+ ),
+ },
+ ];
+ }
+
+ // OpenAI SDK has three kinds ReponseInputContent: text image and file
+ // images can be URLs or base64 dataurl thingies
+ //
+ public async send(
+ inpt: string | InputToken[],
+ sys?: string,
+ ): AsyncRes<string> {
+ const input = typeof inpt === "string" ? inpt : this.buildInput(inpt);
+ const params = sys ? { instructions: sys, input } : { input };
+ const res = await this.apiCall(params);
+ if ("error" in res) return res;
+ else {
+ try {
+ return { ok: res.ok.output_text };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ }
+
+ public async stream(
+ inpt: string | InputToken[],
+ handle: (c: string) => void,
+ sys?: string,
+ ) {
+ const input = typeof inpt === "string" ? inpt : this.buildInput(inpt);
+ const params = sys ? { instructions: sys, input } : { input };
+ await this.apiCallStream(params, handle);
+ }
+
+ // TODO custom temperature?
+ private async apiCall(
+ params: ResponseCreateParamsNonStreaming,
+ ): AsyncRes<OpenAI.Responses.Response> {
+ try {
+ const res = await this.api.responses.create({
+ ...params,
+ // temperature: 1.3,
+ model: params.model || this.model,
+ input: params.input,
+ max_output_tokens: params.max_output_tokens || RESPONSE_LENGTH,
+ stream: false,
+ });
+ // TODO damn there's a lot of stuff here
+ return { ok: res };
+ } catch (e) {
+ console.log(e, "error in openai api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ params: ResponseCreateParamsBase,
+ handler: (c: string) => void,
+ ) {
+ // temperature: 1.3,
+ const pms: ResponseCreateParamsStreaming = {
+ ...params,
+ stream: true,
+ model: params.model || this.model,
+ input: params.input,
+ max_output_tokens: params.max_output_tokens || RESPONSE_LENGTH,
+ };
+ try {
+ const stream = await this.api.responses.create(pms);
+ for await (const event of stream) {
+ console.log(event);
+ switch (event.type) {
+ // TODO deal with audio and whatever
+ case "response.output_text.delta":
+ handler(event.delta);
+ break;
+ case "response.completed":
+ break;
+ default:
+ break;
+ }
+ // if (event.type === "response.completed")
+ // wtf how do we use this
+ }
+ } catch (e) {
+ console.log(e, "error in openai api");
+ return { error: `${e}` };
+ }
+ }
+
+ // private async apiCallStream(
+ // messages: Message[],
+ // handle: (c: string) => void,
+ // ): Promise<void> {
+ // try {
+ // const stream = await this.api.chat.completions.create({
+ // temperature: 1.3,
+ // model: this.model,
+ // messages,
+ // max_tokens: RESPONSE_LENGTH,
+ // stream: true,
+ // });
+
+ // for await (const chunk of stream) {
+ // for (const choice of chunk.choices) {
+ // console.log({ choice });
+ // if (!choice.delta) continue;
+ // const cont = choice.delta.content;
+ // if (!cont) continue;
+ // handle(cont);
+ // }
+ // }
+ // } catch (e) {
+ // console.log(e, "error in openai api");
+ // handle(`Error streaming OpenAI, ${e}`);
+ // }
+ // }
+}
diff --git a/packages/ai/src/logic/constants.ts b/packages/ai/src/logic/constants.ts
new file mode 100644
index 0000000..170477d
--- /dev/null
+++ b/packages/ai/src/logic/constants.ts
@@ -0,0 +1,3 @@
+// export const RESPONSE_LENGTH = 1024;
+export const RESPONSE_LENGTH = 256;
+export const MAX_TOKENS = 64_000;
diff --git a/packages/ai/src/nlp/index.ts b/packages/ai/src/nlp/index.ts
new file mode 100644
index 0000000..ebed586
--- /dev/null
+++ b/packages/ai/src/nlp/index.ts
@@ -0,0 +1,7 @@
+import * as Spacy from "./spacy";
+import * as Stanza from "./stanza";
+import * as ISO from "./iso";
+import { ocr } from "./ocr";
+import type * as Types from "./types";
+export * from "./nlp";
+export { ISO, ocr, Stanza, Spacy, type Types };
diff --git a/packages/ai/src/nlp/nlp.ts b/packages/ai/src/nlp/nlp.ts
new file mode 100644
index 0000000..3b1e3a7
--- /dev/null
+++ b/packages/ai/src/nlp/nlp.ts
@@ -0,0 +1,208 @@
+export const isPunctuation = (text: string): boolean => {
+ // Common punctuation characters
+ const punctuationRegex = /^[.,;:!?()[\]{}'"«»""''…-]+$/;
+ return punctuationRegex.test(text);
+};
+
+// Get color for different syntactic categories
+export function getColorForType(type: string): string {
+ const colors: Record<string, string> = {
+ // Phrasal categories
+ S: "#6495ED", // Sentence - cornflower blue
+ NP: "#FF7F50", // Noun Phrase - coral
+ VP: "#32CD32", // Verb Phrase - lime green
+ PP: "#9370DB", // Prepositional Phrase - medium purple
+ ADJP: "#FFD700", // Adjective Phrase - gold
+ ADVP: "#FF69B4", // Adverb Phrase - hot pink
+
+ // Part-of-speech tags
+ NN: "#FFA07A", // Noun - light salmon
+ NNS: "#FFA07A", // Plural Noun - light salmon
+ NNP: "#FFA07A", // Proper Noun - light salmon
+ VB: "#90EE90", // Verb - light green
+ VBP: "#90EE90", // Present tense verb - light green
+ VBG: "#90EE90", // Gerund verb - light green
+ VBZ: "#90EE90", // 3rd person singular present verb - light green
+ VBD: "#90EE90", // Past tense verb - light green
+ VBN: "#90EE90", // Past participle verb - light green
+ JJ: "#F0E68C", // Adjective - khaki
+ RB: "#DDA0DD", // Adverb - plum
+ IN: "#87CEFA", // Preposition - light sky blue
+ DT: "#D3D3D3", // Determiner - light gray
+ PRP: "#D8BFD8", // Personal pronoun - thistle
+ CC: "#A9A9A9", // Coordinating conjunction - dark gray
+
+ // Default
+ ROOT: "#000000", // Root - black
+ LEAF: "#666666", // Leaf nodes - dark gray
+ };
+
+ return colors[type] || "#666666";
+}
+
+// Get a description for node types
+export function getDescription(type: string): string {
+ const descriptions: Record<string, string> = {
+ S: "Sentence",
+ SBAR: "Subordinating conjunction clause",
+ SBARQ: "Direct question",
+ SINV: "Declarative sentence with subject-aux inversion",
+ SQ: "Subconstituent of SBARQ excluding wh-word",
+ WHADVP: "wh-adverb phrase",
+ WHNP: "wh-nounphrase",
+ WHPP: "wh-prepositional phrase",
+ WDT: "wh-determiner",
+ WP: "wh-pronoun",
+ WRB: "wh-adverb",
+ WP$: "possesive wh-pronoun",
+ MD: "modal",
+ X: "Unknown",
+ NP: "Noun Phrase",
+ VP: "Verb Phrase",
+ PP: "Prepositional Phrase",
+ ADJP: "Adjective Phrase",
+ ADVP: "Adverb Phrase",
+ LS: "List item market",
+ SYM: "Symbol",
+ NN: "Noun",
+ NNS: "Plural Noun",
+ NNP: "Proper Noun",
+ NNPS: "Proper Noun, Plural",
+ VB: "Verb (base form)",
+ VBP: "Verb (present tense)",
+ VBG: "Verb (gerund/present participle)",
+ VBZ: "Verb (3rd person singular present)",
+ VBD: "Verb (past tense)",
+ VBN: "Verb (past participle)",
+ JJ: "Adjective",
+ JJR: "Adjective, comparative",
+ JJS: "Adjective, superlative",
+ EX: "Existential there",
+ RB: "Adverb",
+ RBR: "Adverb, comparative",
+ RBS: "Adverb, superlative",
+ RP: "Particle",
+ IN: "Preposition",
+ TO: "to",
+ DT: "Determiner",
+ PDT: "Predeterminer",
+ PRP: "Personal Pronoun",
+ PP$: "Possesive Pronoun",
+ PRP$: "Possesive Pronoun",
+ POS: "Possesive ending",
+ FW: "Foreign Word",
+ CC: "Coordinating Conjunction",
+ CD: "Cardinal number",
+ UH: "interjection",
+ ROOT: "Root Node",
+ CLR: "figurative motion",
+ FRAG: "fragment",
+ ":": "Colon/Semicolon",
+ ",": "Comma",
+ ".": "Period",
+ };
+
+ return descriptions[type] || type;
+}
+
+// https://universaldependencies.org/u/dep/xcomp.htmlexport
+
+export function unpackDeprel(type: string): string {
+ const descriptions: Record<string, string> = {
+ nsubj: "nominal subject",
+ obj: "object",
+ iobj: "indirect object",
+ csubj: "clausal subject",
+ ccomp: "clausal complement",
+ xcomp: "open clausal complement",
+ obl: "oblique nominal",
+ vocative: "vocative",
+ expl: "expletive",
+ dislocated: "dislocated",
+ nmod: "nominal modifier",
+ appos: "appositional modifier",
+ nummod: "numeric modifier",
+ advcl: "adverbial clause modifier",
+ acl: "admonimal clause",
+ advmod: "adverbial modifier",
+ discourse: "dicourse element",
+ aux: "auxiliary",
+ cop: "copula",
+ mark: "marker",
+ amod: "adjectival modifier",
+ det: "determiner",
+ clf: "classifier",
+ case: "case marker",
+ conj: "conjunction",
+ cc: "coordinating conjunction",
+ fixed: "fixed multiword expression",
+ flat: "flat expression",
+ list: "list",
+ parataxis: "parataxis",
+ compound: "compound",
+ orphan: "orphan",
+ goeswith: "goes with",
+ reparandum: "overriden disfluency",
+ punct: "punctuation",
+ root: "root",
+ dep: "unspecified dependency",
+ };
+ const res = descriptions[type];
+ if (!res) console.log("tag not found!!", type);
+
+ return res || type;
+}
+
+export function deprelColors(type: string): string {
+ const colors: Record<string, string> = {
+ // Phrasal categories
+ s: "#6495ED", // Sentence - cornflower blue
+ nsubj: "#6495ED", // Sentence - cornflower blue
+ root: "#FFD700", // Adjective Phrase - gold
+ p: "#FFD700", // Adjective Phrase - gold
+ NP: "#FF7F50", // Noun Phrase - coral
+ VP: "#32CD32", // Verb Phrase - lime green
+ PP: "#9370DB", // Prepositional Phrase - medium purple
+ ADVP: "#FF69B4", // Adverb Phrase - hot pink
+
+ // Part-of-speech tags
+ NN: "#FFA07A", // Noun - light salmon
+ NNS: "#FFA07A", // Plural Noun - light salmon
+ NNP: "#FFA07A", // Proper Noun - light salmon
+ VB: "#90EE90", // Verb - light green
+ VBP: "#90EE90", // Present tense verb - light green
+ VBG: "#90EE90", // Gerund verb - light green
+ VBZ: "#90EE90", // 3rd person singular present verb - light green
+ VBD: "#90EE90", // Past tense verb - light green
+ VBN: "#90EE90", // Past participle verb - light green
+ JJ: "#F0E68C", // Adjective - khaki
+ RB: "#DDA0DD", // Adverb - plum
+ IN: "#87CEFA", // Preposition - light sky blue
+ DT: "#D3D3D3", // Determiner - light gray
+ PRP: "#D8BFD8", // Personal pronoun - thistle
+ CC: "#A9A9A9", // Coordinating conjunction - dark gray
+
+ // Default
+ ROOT: "#000000", // Root - black
+ LEAF: "#666666", // Leaf nodes - dark gray
+ };
+
+ return colors[type] || "#666666";
+}
+export function unpackPos(pos: string): string {
+ const map: Record<string, string> = {
+ adj: "adjective",
+ adv: "adverb",
+ adv_phrase: "adverbial phrase",
+ combining_form: "combining form",
+ conj: "conjunction",
+ det: "determinant",
+ intj: "interjection",
+ num: "number",
+ prep: "preposition",
+ prep_phrase: "prepositional phrase",
+ pron: "pronoun",
+ punct: "punctuation",
+ };
+ return map[pos] || pos;
+}
diff --git a/packages/ai/src/nlp/ocr.ts b/packages/ai/src/nlp/ocr.ts
new file mode 100644
index 0000000..d495a8b
--- /dev/null
+++ b/packages/ai/src/nlp/ocr.ts
@@ -0,0 +1,18 @@
+import type { AsyncRes } from "@sortug/lib";
+
+export async function ocr(formData: FormData): AsyncRes<string[]> {
+ const endpoint = "http://localhost:8102/ocr";
+
+ const opts = {
+ method: "POST",
+ body: formData,
+ headers: { "X-API-KEY": Bun.env.SORTUG_NLP_API_KEY! },
+ };
+ try {
+ const res = await fetch(endpoint, opts);
+ const j = await res.json();
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
diff --git a/packages/ai/src/nlp/spacy.ts b/packages/ai/src/nlp/spacy.ts
new file mode 100644
index 0000000..829c77e
--- /dev/null
+++ b/packages/ai/src/nlp/spacy.ts
@@ -0,0 +1,79 @@
+import type { AsyncRes, Result } from "@sortug/lib";
+import { detectLang } from "./iso";
+const ENDPOINT = "http://localhost:8102";
+
+export async function run(text: string, langg?: string): AsyncRes<SpacyRes> {
+ try {
+ const lang = langg ? langg : detectLang(text);
+ const body = JSON.stringify({ string: text, lang });
+ const opts = {
+ headers: {
+ "Content-type": "application/json",
+ "X-API-KEY": Bun.env.SORTUG_NLP_API_KEY!,
+ },
+ method: "POST",
+ body,
+ };
+ const res = await fetch(ENDPOINT + "/spacy", opts);
+ const j = await res.json();
+ console.log("spacy", j);
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+
+export type SpacyResBig = {
+ doc: {
+ text: string;
+ ents: any[];
+ sents: Array<{ start: number; end: number }>;
+ tokens: Token[];
+ };
+ segs: Sentence[];
+};
+export type SpacyRes = {
+ input: string;
+ segments: Sentence[];
+};
+export type Sentence = {
+ text: string;
+ start: number;
+ end: number;
+ root: Token;
+ subj: Token;
+ arcs: Arc[];
+ words: Word[];
+};
+export type Arc = {
+ start: number;
+ end: number;
+ label: string; // deprel label
+ dir: string;
+};
+export type Token = {
+ id: number;
+ head: number;
+ start: number;
+ end: number;
+ dep: string;
+ lemma: string;
+ morph: string;
+ pos: string;
+ tag: string;
+ text: string;
+};
+
+export interface Word extends Token {
+ ancestors: number[];
+ children: [];
+ n_lefts: number;
+ n_rights: number;
+ left_edge: number;
+ right_edge: number;
+ morph_map: Record<string, string>;
+}
+
+export function isChild(w: Word, topId: number): boolean {
+ return w.id === topId || w.ancestors.includes(topId);
+}
diff --git a/packages/ai/src/nlp/stanza.ts b/packages/ai/src/nlp/stanza.ts
new file mode 100644
index 0000000..90fa1fc
--- /dev/null
+++ b/packages/ai/src/nlp/stanza.ts
@@ -0,0 +1,210 @@
+import type { AsyncRes, Result } from "@sortug/lib";
+import { detectLang } from "./iso";
+
+const ENDPOINT = "http://localhost:8102";
+export async function segmenter(
+ text: string,
+ langg?: string,
+): AsyncRes<StanzaRes> {
+ try {
+ const lang = langg ? langg : detectLang(text);
+ const body = JSON.stringify({ lang, string: text });
+ const opts = {
+ headers: {
+ "Content-type": "application/json",
+ "X-API-KEY": Bun.env.SORTUG_NLP_API_KEY!,
+ },
+ method: "POST",
+ body,
+ };
+ const res = await fetch(ENDPOINT + "/stanza", opts);
+ const j = await res.json();
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+export async function idLang(text: string) {
+ try {
+ const body = JSON.stringify({ string: text });
+ const opts = {
+ headers: {
+ "Content-type": "application/json",
+ "X-API-KEY": Bun.env.SORTUG_NLP_API_KEY!,
+ },
+ method: "POST",
+ body,
+ };
+ const res = await fetch(ENDPOINT + "/detect-lang", opts);
+ const j = await res.json();
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+export type StanzaRes = { input: string; segments: Sentence[] };
+export type Sentence = {
+ text: string;
+ sentiment: number;
+ constituency: TreeNode;
+ constring: string;
+ dependencies: Dependency[];
+ entities: Entity[];
+ tokens: Token[];
+ words: Word[];
+};
+export type TreeNode = {
+ label: string;
+ children: TreeNode[];
+};
+export type Dependency = Array<[Word, string, Word]>;
+export type Word = {
+ id: number;
+ text: string;
+ lemma: string;
+ upos: string;
+ xpos: string;
+ feats: string;
+ head: number;
+ deprel: string;
+ start_char: number;
+ end_char: number;
+};
+export type Token = {
+ id: [number, number];
+ text: string;
+ misc: string;
+ words: Word[];
+ start_char: number;
+ end_char: number;
+ ner: string;
+};
+export type Entity = {
+ text: string;
+ misc: string;
+ start_char: number;
+ end_char: number;
+ type: string;
+};
+
+// mine
+export type Clause = {
+ words: Word[];
+ dependency: Dependency;
+ text: string;
+};
+// "amod",
+// {
+// "id": 1,
+// "text": "Stony",
+// "lemma": "Stony",
+// "upos": "ADJ",
+// "xpos": "NNP",
+// "feats": "Degree=Pos",
+// "head": 3,
+// "deprel": "amod",
+// "start_char": 0,
+// "end_char": 5
+// }
+//
+//
+
+export interface ParsedGrammar {
+ predicateCore: number;
+ subjectCore: number | null;
+ tree: Record<number, number[]>;
+ wordMap: WordMap;
+ words: BigWord[];
+}
+export interface BigWord extends Word {
+ ancestry: number[];
+ component: "s" | "p" | "u";
+}
+export type ComputedDependency = {
+ word: BigWord;
+ children: ComputedDependency[];
+};
+export type WordMap = Record<number, Word>;
+
+export function buildTreeFromWords(words: Word[]): Result<ParsedGrammar> {
+ const roots = words.filter((w) => w.deprel === "root");
+ if (roots.length > 1) {
+ console.log("roots", roots);
+ return { error: "too many roots" };
+ } else if (roots.length === 0) {
+ return { error: "no roots" };
+ } else {
+ const root = roots[0];
+ const wordmap = words.reduce((acc: WordMap, item) => {
+ acc[item.id] = item;
+ return acc;
+ }, {});
+ return { ok: parseFurther(words, wordmap, root) };
+ }
+}
+function parseFurther(
+ words: Word[],
+ wordMap: WordMap,
+ root: Word,
+): ParsedGrammar {
+ const predicateCore = root.id;
+ let subjectCore: number | null = null;
+ const tree: Record<number, number[]> = {};
+ const bigwords: BigWord[] = [];
+ const getAncestry = (parent: Word): number[] => {
+ const kids = tree[parent.head] || [];
+ tree[parent.head] = [...kids, parent.id];
+ if (parent.deprel === "nsubj") subjectCore = parent.id;
+
+ console.log("getting ancestry " + parent.id, parent.text);
+ const grandpa = wordMap[parent.head];
+ if (!grandpa) return [parent.id];
+ else return [parent.id, ...getAncestry(grandpa)];
+ };
+ let idx = 0;
+ for (const w of words) {
+ if (w.deprel === "punct") {
+ const prev = words[idx - 1];
+ if (!prev) continue;
+ prev.text += w.text;
+ continue;
+ }
+ const parent = wordMap[w.head];
+ if (!parent) tree[w.id] = [];
+ const ancestry = !parent ? [] : getAncestry(parent);
+ const component =
+ subjectCore && (w.id === subjectCore || ancestry.includes(subjectCore))
+ ? "s"
+ : w.id === predicateCore || ancestry.includes(root.id)
+ ? "p"
+ : "u";
+ const bw: BigWord = { ...w, component, ancestry };
+ wordMap[w.id] = bw;
+ bigwords.push(bw);
+ idx++;
+ }
+ const pg: ParsedGrammar = {
+ predicateCore,
+ subjectCore,
+ wordMap,
+ tree,
+ words: bigwords,
+ };
+ return pg;
+}
+
+export function oneDescendant(node: TreeNode): boolean {
+ if (node.children.length !== 1) return false;
+ else {
+ const child = node.children[0];
+ return child.children.length === 0;
+ }
+}
+
+// function findChildren(wordmap: WordMap, word: Word): ComputedDependency {
+// const children = words.filter((w) => w.head === head.id);
+// return {
+// word: head,
+// children: children.map((c) => findChildren(words, c)),
+// };
+// }
diff --git a/packages/ai/src/nlp/types.ts b/packages/ai/src/nlp/types.ts
new file mode 100644
index 0000000..605a637
--- /dev/null
+++ b/packages/ai/src/nlp/types.ts
@@ -0,0 +1,50 @@
+export type ViewLevel =
+ | "text"
+ | "paragraph"
+ | "sentence"
+ | "clause"
+ | "word"
+ | "syllable"
+ | "phoneme";
+export interface ViewState {
+ level: ViewLevel;
+ pIndex: number | null;
+ sIndex: number | null;
+ cIndex: number | null;
+ wIndex: number | null;
+ yIndex: number | null;
+ fIndex: number | null;
+}
+
+export interface ViewProps {
+ idx: number;
+ rawText: string;
+ context: Context;
+}
+export type Context = {
+ parentText: string;
+ segmented: string[];
+ idx: number;
+};
+
+export type WordData = {
+ confidence: number;
+ frequency: number | null;
+ id: number;
+ ipa: Array<{ ipa: string; tags: string[] }>;
+ spelling: string;
+ type: ExpressionType;
+ syllables: number;
+ lang: string;
+ prosody: any;
+ senses: Sense[];
+};
+export type ExpressionType = "word" | "expression" | "syllable";
+export type Sense = {
+ etymology: string;
+ pos: string;
+ forms: Array<{ form: string; tags: string[] }>;
+ related: any;
+ senses: Array<{ glosses: string[]; links: Array<[string, string]> }>;
+};
+export type LoadingStatus = "pending" | "loading" | "success" | "error";
diff --git a/packages/ai/src/openai-responses.ts b/packages/ai/src/openai-responses.ts
new file mode 100644
index 0000000..63a08cc
--- /dev/null
+++ b/packages/ai/src/openai-responses.ts
@@ -0,0 +1,186 @@
+import OpenAI from "openai";
+import { MAX_TOKENS, RESPONSE_LENGTH } from "./logic/constants";
+import type { AIModelAPI, ChatMessage, InputToken } from "./types";
+import type { AsyncRes } from "@sortug/lib";
+import type {
+ ResponseContent,
+ ResponseInput,
+ ResponseInputContent,
+ ResponseInputItem,
+ ResponseOutputItem,
+ ResponseOutputMessage,
+} from "openai/resources/responses/responses";
+import type { ResponseCreateAndStreamParams } from "openai/lib/responses/ResponseStream";
+import { memoize } from "./cache";
+
+type Params = OpenAI.Responses.ResponseCreateParamsNonStreaming;
+type Props = {
+ baseURL: string;
+ apiKey: string;
+ model?: string;
+ maxTokens?: number;
+ tokenizer?: (text: string) => number;
+ allowBrowser?: boolean;
+};
+export default class OpenAIAPI implements AIModelAPI {
+ private cachedCreate!: (args: Params) => Promise<OpenAI.Responses.Response>;
+
+ private apiKey;
+ private baseURL;
+ private api;
+ maxTokens: number = MAX_TOKENS;
+ tokenizer: (text: string) => number = (text) => text.length / 3;
+ model;
+
+ constructor(props: Props) {
+ this.apiKey = props.apiKey;
+ this.baseURL = props.baseURL;
+ this.api = new OpenAI({
+ baseURL: this.baseURL,
+ apiKey: this.apiKey,
+ dangerouslyAllowBrowser: props.allowBrowser || false,
+ });
+ this.model = props.model || "";
+ if (props.maxTokens) this.maxTokens = props.maxTokens;
+ if (props.tokenizer) this.tokenizer = props.tokenizer;
+
+ const boundCreate = this.api.responses.create.bind(this.api.responses);
+
+ this.cachedCreate = memoize(boundCreate, {
+ ttlMs: 2 * 60 * 60 * 1000, // 2h
+ maxEntries: 5000,
+ persistDir: "./cache/memo",
+ // stable key for the call
+ keyFn: (args) => {
+ // args is the single object param to .create(...)
+ const { model, input, max_output_tokens, temperature, top_p } =
+ args as Params;
+ // stringify messages deterministically (role+content only)
+ return JSON.stringify({
+ model,
+ input,
+ max_output_tokens,
+ temperature,
+ top_p,
+ });
+ },
+ });
+ }
+ public setModel(model: string) {
+ this.model = model;
+ }
+ // response input items are text, image, file, conversation state or function cals
+ private buildInput(tokens: InputToken[]): ResponseInputItem[] {
+ const content: ResponseInputContent[] = tokens.map((t) => {
+ if ("text" in t) return { type: "input_text" as const, text: t.text };
+ // image_url or file_id
+ else if ("img" in t)
+ return {
+ type: "input_image" as const,
+ image_url: t.img,
+ detail: "auto",
+ };
+ // file_data or file_id or file_url or filename
+ else if ("file" in t)
+ return { type: "input_file" as const, file_data: t.file.file_data };
+ // TODO obviously
+ else return { type: "input_text" as const, text: "oy vey" };
+ });
+ // role can be user, developer, or system
+ return [{ role: "user" as const, content }];
+ }
+
+ public async send(
+ userInput: string | InputToken[],
+ sys?: string,
+ ): AsyncRes<string> {
+ const input: string | ResponseInput =
+ typeof userInput === "string" ? userInput : this.buildInput(userInput);
+ // const messages = this.mapMessages(input);
+ const res = await this.apiCall({ instructions: sys, input });
+ if ("error" in res) return res;
+ else {
+ try {
+ // TODO type this properly
+ const resText = res.ok.reduce((acc, item) => {
+ if (item.type === "message" && item.status === "completed") {
+ const outputText = this.getOutputText(item.content);
+ return `${acc}\n${outputText}`;
+ }
+ // TODO else
+ return acc;
+ }, "");
+ return { ok: resText };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+ }
+ }
+ getOutputText(content: ResponseOutputMessage["content"]): string {
+ let text = "";
+ for (const c of content) {
+ if (c.type === "refusal") text += `\nRefused to respond: ${c.refusal}\n`;
+ else text += `\n${c.text}\n`;
+ }
+ return text;
+ }
+
+ public async stream(
+ userInput: string | InputToken[],
+ handle: (c: string) => void,
+ sys?: string,
+ ) {
+ const input: string | ResponseInput =
+ typeof userInput === "string" ? userInput : this.buildInput(userInput);
+ await this.apiCallStream({ instructions: sys, input }, handle);
+ }
+
+ // TODO custom temperature?dune exec -- ./test/test_nock.exe --verbose
+ private async apiCall(
+ params: OpenAI.Responses.ResponseCreateParamsNonStreaming,
+ ): AsyncRes<ResponseOutputItem[]> {
+ // console.log({ messages }, "at the very end");
+ try {
+ const response = await this.cachedCreate({
+ ...params,
+ model: this.model,
+ // max_output_tokens: RESPONSE_LENGTH,
+ });
+ if (response.status !== "completed")
+ return {
+ error:
+ response.incomplete_details?.reason || response.status || "error",
+ };
+
+ return { ok: response.output };
+ } catch (e) {
+ console.log(e, "error in openai api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ params: ResponseCreateAndStreamParams,
+ handle: (c: string) => void,
+ ): Promise<void> {
+ try {
+ const stream = await this.api.responses.create({
+ // temperature: 1.3,
+ ...params,
+ stream: true,
+ model: this.model,
+ max_output_tokens: RESPONSE_LENGTH,
+ });
+
+ for await (const chunk of stream) {
+ console.log("stream reponse", chunk);
+ if (chunk.type === "response.output_text.done") handle(chunk.text);
+ // TODO else
+ }
+ } catch (e) {
+ console.log(e, "error in openai api");
+ // TODO
+ // handle(`Error streaming OpenAI, ${e}`);
+ }
+ }
+}
diff --git a/packages/ai/src/openai.ts b/packages/ai/src/openai.ts
new file mode 100644
index 0000000..bd1dca1
--- /dev/null
+++ b/packages/ai/src/openai.ts
@@ -0,0 +1,260 @@
+import fs from "fs";
+import OpenAI from "openai";
+import { RESPONSE_LENGTH } from "./logic/constants";
+import type { ChatMessage, OChoice, OChunk, OMessage } from "./types";
+import type { AsyncRes, Result } from "@sortug/lib";
+import OpenAIToolUse from "./openai_tools";
+import type { FileObject } from "openai/src/resources/files.js";
+
+type Message = OpenAI.Chat.Completions.ChatCompletionMessageParam;
+
+type Props = {
+ maxTokens?: number;
+ baseURL?: string;
+ apiKey?: string;
+ tokenizer?: (text: string) => number;
+};
+export default class Conversation {
+ private maxTokens: number = 128_000;
+ private apiKey: string = Bun.env["OPENAI_API_KEY"] || "";
+ private baseURL: string = "https://api.openai.com/v1";
+ private tokenizer: (text: string) => number = (text) => text.length / 3;
+ openai;
+ private model: string = "gpt-4.1";
+
+ constructor(props: Props) {
+ if (props.apiKey) this.apiKey = props.apiKey;
+ if (props.baseURL) this.baseURL = props.baseURL;
+ this.openai = new OpenAI({ baseURL: this.baseURL, apiKey: this.apiKey });
+ if (props.maxTokens) this.maxTokens = props.maxTokens;
+ if (props.tokenizer) this.tokenizer = props.tokenizer;
+ }
+ public setModel(model: string) {
+ this.model = model;
+ }
+ private mapMessages(input: ChatMessage[]): Message[] {
+ return input.map((m) => {
+ const role = m.author === "openai" ? "assistant" : "user";
+ return { role, content: m.text, name: m.author };
+ });
+ }
+
+ private mapMessagesR1(input: ChatMessage[]): Message[] {
+ return input.reduce((acc: Message[], m, i) => {
+ const prev = acc[i - 1];
+ const role = m.author === "openai" ? "assistant" : "user";
+ const msg: Message = { role, content: m.text, name: m.author };
+ if (prev?.role === role) acc[i - 1] = msg;
+ else acc = [...acc, msg];
+ return acc;
+ }, []);
+ }
+
+ public async send(sys: string, input: ChatMessage[]): AsyncRes<OChoice[]> {
+ const messages = this.mapMessages(input);
+ const sysMsg: Message = { role: "system", content: sys };
+ const allMessages = [sysMsg, ...messages];
+ const truncated = this.truncateHistory(allMessages);
+ const res = await this.apiCall(truncated);
+ return res;
+ }
+
+ public async sendR1(input: ChatMessage[]): AsyncRes<OChoice[]> {
+ const messages = this.mapMessagesR1(input);
+ const truncated = this.truncateHistory(messages);
+ const res = await this.apiCall(truncated);
+ return res;
+ }
+
+ public async stream(
+ sys: string,
+ input: ChatMessage[],
+ handle: (c: any) => void,
+ ) {
+ const messages = this.mapMessages(input);
+ const sysMsg: Message = { role: "system", content: sys };
+ const allMessages = [sysMsg, ...messages];
+ const truncated = this.truncateHistory(allMessages);
+ await this.apiCallStream(truncated, handle);
+ }
+
+ public async streamR1(input: ChatMessage[], handle: (c: any) => void) {
+ const messages = this.mapMessagesR1(input);
+ const truncated = this.truncateHistory(messages);
+ await this.apiCallStream(truncated, handle);
+ }
+
+ private truncateHistory(messages: Message[]): Message[] {
+ const totalTokens = messages.reduce((total, message) => {
+ return total + this.tokenizer(message.content as string);
+ }, 0);
+ while (totalTokens > this.maxTokens && messages.length > 1) {
+ // Always keep the system message if it exists
+ const startIndex = messages[0].role === "system" ? 1 : 0;
+ messages.splice(startIndex, 1);
+ }
+ return messages;
+ }
+
+ private async apiCall(messages: Message[]): AsyncRes<OChoice[]> {
+ try {
+ const completion = await this.openai.chat.completions.create({
+ temperature: 1.3,
+ model: this.model,
+ messages,
+ max_tokens: RESPONSE_LENGTH,
+ });
+ if (!completion) return { error: "null response from openai" };
+ return { ok: completion.choices };
+ } catch (e) {
+ console.log(e, "error in openai api");
+ return { error: `${e}` };
+ }
+ }
+
+ private async apiCallStream(
+ messages: Message[],
+ handle: (c: string) => void,
+ ): Promise<void> {
+ try {
+ const stream = await this.openai.chat.completions.create({
+ temperature: 1.3,
+ model: this.model,
+ messages,
+ max_tokens: RESPONSE_LENGTH,
+ stream: true,
+ });
+
+ for await (const chunk of stream) {
+ for (const choice of chunk.choices) {
+ console.log({ choice });
+ if (!choice.delta) continue;
+ const cont = choice.delta.content;
+ if (!cont) continue;
+ handle(cont);
+ }
+ }
+ } catch (e) {
+ console.log(e, "error in openai api");
+ handle(`Error streaming OpenAI, ${e}`);
+ }
+ }
+
+ // assistant
+ async assistant() {
+ const assistant = await this.openai.beta.assistants.create({
+ name: "Literature professor",
+ instructions:
+ "You are a professor of literature. Use your knowledge to analyze large pieces of text and answer questions from your users.",
+ model: this.model,
+ tools: [{ type: "file_search" }],
+ temperature: 0.7,
+ response_format: { type: "text" },
+ });
+ const vector_store = await this.openai.beta.vectorStores.create({
+ name: "docs",
+ });
+ const tool_resources = {
+ file_search: { vector_store_ids: [vector_store.id] },
+ };
+ const tant = await this.openai.beta.assistants.update(assistant.id, {
+ tool_resources,
+ });
+ const thread = await this.openai.beta.threads.create();
+ const msg = await this.openai.beta.threads.messages.create(thread.id, {
+ role: "user",
+ content:
+ "Greetings, pleasure to meet. Let's get started if you don't mind",
+ });
+ const run = await this.openai.beta.threads.runs.create(thread.id, {
+ assistant_id: assistant.id,
+ instructions: "be nice",
+ });
+ while (run.status === "in_progress") {
+ console.log({ run });
+ }
+ }
+ async lookatFile(fo: FileObject) {
+ const tant = await this.openai.beta.assistants.create({
+ name: "Literature professor",
+ instructions:
+ "You are a professor of literature. Use your knowledge to analyze large pieces of text and answer questions from your users.",
+ model: this.model,
+ tools: [{ type: "file_search" }],
+ temperature: 0.7,
+ response_format: { type: "text" },
+ });
+ const thread = await this.openai.beta.threads.create();
+ await this.openai.beta.threads.messages.create(thread.id, {
+ role: "user",
+ content:
+ "Greetings, pleasure to meet. Let's get started if you don't mind. Look at this file and summarize its contents",
+ attachments: [{ file_id: fo.id, tools: [{ type: "file_search" }] }],
+ });
+ const run = await this.openai.beta.threads.runs.createAndPoll(thread.id, {
+ assistant_id: tant.id,
+ });
+ console.log({ run });
+ const msgs = await this.openai.beta.threads.messages.list(run.thread_id);
+ console.log({ msgs });
+ for (let m of msgs.data) {
+ console.log(m, "message on thread");
+ }
+ }
+
+ async uploadFile(res: Response) {
+ // const ff = fs.createReadStream("./lol")
+ const file = await this.openai.files.create({
+ file: res,
+ purpose: "assistants",
+ });
+ console.log({ file }, "uploaded");
+ return file;
+
+ // {
+ // "id": "file-abc123",
+ // "object": "file",
+ // "bytes": 120000,
+ // "created_at": 1677610602,
+ // "filename": "mydata.jsonl",
+ // "purpose": "fine-tune",
+ // }
+ }
+
+ // async analyzeFile(){
+ // const huh = await this.openai.beta.vectorStores.files.uploadAndPoll()
+ // }
+
+ // mcp
+
+ async mcp() {
+ const res = await fetch("http://localhost:8900/list");
+ const list = await res.json();
+ this.tryTools(list);
+ }
+
+ async tryTools(tools: OpenAI.Chat.Completions.ChatCompletionTool[]) {
+ const messages: Message[] = [
+ { role: "user", content: "What's on my twitter timeline right now?" },
+ ];
+ const completion = await this.openai.chat.completions.create({
+ model: "gpt-4o-2024-11-20",
+ messages,
+ tools,
+ });
+ if (!completion) return { error: "null response from openai" };
+
+ for (let choice of completion.choices) {
+ console.log({ choice });
+ if (choice.message.tool_calls) {
+ const instance = new OpenAIToolUse(
+ this.openai,
+ "gpt-4o-2024-11-20",
+ tools,
+ choice.message,
+ choice.message.tool_calls,
+ );
+ }
+ }
+ }
+}
diff --git a/packages/ai/src/openai_tools.ts b/packages/ai/src/openai_tools.ts
new file mode 100644
index 0000000..feb2e4a
--- /dev/null
+++ b/packages/ai/src/openai_tools.ts
@@ -0,0 +1,66 @@
+import type OpenAI from "openai";
+import type { Result } from "./types";
+type ToolCall = OpenAI.Chat.Completions.ChatCompletionMessageToolCall;
+
+type Tool = OpenAI.Chat.Completions.ChatCompletionTool;
+type ToolMsg = OpenAI.Chat.Completions.ChatCompletionToolMessageParam;
+
+type Message = OpenAI.Chat.Completions.ChatCompletionMessage;
+
+export default class OpenAIToolUse {
+ api;
+ model;
+ socket;
+ tools;
+ message;
+ calls;
+ res: ToolMsg | null = null;
+ constructor(
+ api: OpenAI,
+ model: string,
+ tools: Tool[],
+ message: Message,
+ calls: ToolCall[],
+ ) {
+ this.api = api;
+ this.model = model;
+ this.socket = new WebSocket("http://localhost:8900");
+ this.tools = tools;
+ this.message = message;
+ this.calls = calls;
+ for (let c of calls) {
+ console.log({ c });
+ }
+ this.wsHandlers();
+ }
+ wsHandlers() {
+ this.socket.addEventListener("open", (_data) => {
+ this.handleToolCalls();
+ });
+ this.socket.addEventListener("message", (ev) => {
+ const j = JSON.parse(ev.data);
+ if ("functionRes" in j) this.handleRes(j.functionRes);
+ });
+ }
+ handleToolCalls() {
+ for (let c of this.calls) this.socket.send(JSON.stringify({ call: c }));
+ }
+ async handleRes(res: Result<ToolMsg>) {
+ if ("error" in res) {
+ console.log("TODO");
+ return;
+ }
+ this.res = res.ok;
+ const messages = [this.message, res.ok];
+ console.log({ messages }, "almost there");
+ const completion = await this.api.chat.completions.create({
+ model: this.model,
+ messages,
+ tools: this.tools,
+ });
+ console.log({ completion });
+ for (let choice of completion.choices) {
+ console.log({ choice });
+ }
+ }
+}
diff --git a/packages/ai/src/prompts.ts b/packages/ai/src/prompts.ts
new file mode 100644
index 0000000..60e8c0d
--- /dev/null
+++ b/packages/ai/src/prompts.ts
@@ -0,0 +1,14 @@
+export const yagoSys =
+ "You are a helpful assistant of humans engaged in high stakes work. We call you Yagobot. Your user's name will appear in the 'name' field of this message. Please be brief but intelligent in your answers. Be civil but not overly polite, always tell the truth even if inconvenient. Address your user by his name.";
+
+export const biaSys =
+ "You are Yagobot, an extremely helpful assistant in charge of attending to a new mother to all her needs. Her name is Bia and she would like you to address her in both Thai and English at all times. Her husband will show up now and then, he's cool too.";
+
+export const GUEST_SYS =
+ "You are Yagobot, a helpful assistant with vast knowledge of everything there is to now in several languages. You are responding to a guest user now, be polite, but friendly and brief. Get to the point and strive to be both cool and useful. Respond in the language in which you were addressed.";
+
+export const LEO_SYS = `You are Yagobot, a super advanced tutor AI to help the children of foreign elites with the best education in the world. You are talking to Leo, a precocious mixed-race Japanese 11 year old. His Japanese name is 黎雄. He can't speak English well but he can understand a bit. Please respond to him the same content thrice: in English first, then English but in IPA phonetic noting, then in Japanese. Try to be proactive and ask him questions yourself if you see he isn't talking much.`;
+
+export const SAYURI_SYS = `You are Yagobot, a super advanced tutor AI to help the children of foreign elites with the best education in the world. You are talking to Sayuri, a lovely mixed-race Japanese 9 year old. Her Japanese name is 紗悠里. She can't speak English well but she can understand a bit. Please respond to her the same content thrice: in English first, then English but in IPA phonetic noting, then in Japanese. Try to be proactive and ask him questions yourself if you see she isn't talking much.`;
+
+export const BOOKWORM_SYS = `You are a professor of literature. Use your knowledge to analyze large pieces of text and answer questions from your users.`;
diff --git a/packages/ai/src/tts/eleven.ts b/packages/ai/src/tts/eleven.ts
new file mode 100644
index 0000000..c870b11
--- /dev/null
+++ b/packages/ai/src/tts/eleven.ts
@@ -0,0 +1,20 @@
+import { ElevenLabsClient, play } from "@elevenlabs/elevenlabs-js";
+
+const elevenlabs = new ElevenLabsClient({
+ apiKey: Bun.env.ELEVEN_KEY!, // Defaults to process.env.ELEVENLABS_API_KEY
+});
+
+const models = await elevenlabs.models.list();
+for (const model of models) {
+ const langs = model.languages || [];
+ for (const lang of langs) {
+ if (lang.name === "Thai") console.log(model.modelId);
+ }
+}
+// ONLY eleven_v3 has Thai!
+// const audio = await elevenlabs.textToSpeech.convert("Xb7hH8MSUJpSbSDYk0k2", {
+// text: "Hello! 你好! Hola! नमस्ते! Bonjour! こんにちは! مرحبا! 안녕하세요! Ciao! Cześć! Привіт! வணக்கம்!",
+// modelId: "eleven_multilingual_v2",
+// });
+
+// await play(audio);
diff --git a/packages/ai/src/tts/minimax.ts b/packages/ai/src/tts/minimax.ts
new file mode 100644
index 0000000..4421f94
--- /dev/null
+++ b/packages/ai/src/tts/minimax.ts
@@ -0,0 +1,107 @@
+// https://platform.minimax.io/docs/api-reference/speech-t2a-async-create/
+//
+//
+//
+
+const text = `สำนักข่าวต่างประเทศรายงานเมื่อ 18 พ.ย. 2568 ว่า เจ้าหน้าที่กู้ภัยของประเทศชิลี กำลังดำเนินการค้นหากลุ่มนักท่องเที่ยวที่สูญหายไปในพายุหิมะรุนแรงซึ่งเกิดขึ้นที่ อุทยานแห่งชาติ “ตอร์เรส เดล ไพเน” ในภูมิภาคปาตาโกเนีย ทางตอนใต้ของชิลี หลังพายุทำให้มีผู้เสียชีวิตแล้วอย่างน้อย 5 ศพ`;
+// const text = `So I start using it for my project and after about 20 mins - oh, no. Out of credits.
+// I didn't even get to try a single Gemini 3 prompt. I was out of credits before my first had completed. I guess I've burned through the free tier in some other app but the error message gave me no clues. As far as I can tell there's no link to give Google my money in the app. Maybe they think they have enough.
+
+// After switching to gpt-oss:120b it did some things quite well, and the annotation feature in the plan doc is really nice. It has potential but I suspect it's suffering from Google's typical problem that it's only really been tested on Googlers.`;
+const model = "speech-2.6-hd";
+const voice1 = "Thai_male_1_sample8";
+const voice2 = "Thai_male_2_sample2";
+const voice3 = "Thai_female_1_sample1";
+const voice4 = "Thai_female_2_sample2";
+const params = {
+ model,
+ language_boost: "auto",
+ voice_setting: { voice_id: voice1, speed: 1, vol: 1, pitch: 1 },
+ pronunciation_dct: { tone: ["lol, lmao"] },
+ audio_setting: {
+ audio_sample_rate: 32000,
+ bitrate: 128_000,
+ format: "mp3",
+ channel: 2,
+ },
+ voice_modify: {
+ pitch: 0,
+ intensity: 0,
+ timbre: 0,
+ sound_Effects: "spacious_echo",
+ },
+};
+
+async function getVoices() {
+ const endpoint = "/get_voice";
+ const body = { voice_type: "all" };
+ return await post(endpoint, body);
+}
+async function tts() {
+ const endpoint = "/t2a_v2";
+ const body = { text, stream: false, ...params };
+ return await post(endpoint, body);
+}
+async function ws() {
+ const url = "wss://api.minimax.io/ws/v1/t2a_v2";
+ const event = "task_start";
+
+ const headers = {
+ Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY!}`,
+ };
+ const socket = new WebSocket(url, { headers });
+ const body = { event, ...params };
+ const body2 = { event: "task_continue", text };
+ socket.send(JSON.stringify(body));
+ // const event = "task_continue";
+ // const event = "task_finish";
+}
+async function tts_async() {
+ const body = {
+ text,
+ ...params,
+ };
+ return await post("/t2a_async_v2", body);
+}
+async function post(path: string, body: any) {
+ const url = "https://api.minimax.io/v1" + path;
+ const options = {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY2!}`,
+ },
+ body: JSON.stringify(body),
+ };
+
+ try {
+ const response = await fetch(url, options);
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.error(error);
+ }
+}
+async function get(path: string) {
+ const url = "https://api.minimax.io/v1" + path;
+ const options = {
+ headers: {
+ Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY!}`,
+ },
+ };
+
+ try {
+ const response = await fetch(url, options);
+ const data = await response.json();
+ console.log(data);
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+import fs from "node:fs";
+const res = await tts();
+const audio = res.data.audio;
+const audioBuffer = Buffer.from(audio, "hex");
+const filename = "output.mp3";
+fs.writeFileSync(filename, audioBuffer);
diff --git a/packages/ai/src/tts/output.mp3 b/packages/ai/src/tts/output.mp3
new file mode 100644
index 0000000..9f22e3a
--- /dev/null
+++ b/packages/ai/src/tts/output.mp3
Binary files differ
diff --git a/packages/ai/src/types/index.ts b/packages/ai/src/types/index.ts
new file mode 100644
index 0000000..24db77b
--- /dev/null
+++ b/packages/ai/src/types/index.ts
@@ -0,0 +1,56 @@
+import type { ResponseInputFile } from "openai/resources/responses/responses.js";
+import type { AsyncRes } from "@sortug/lib";
+export type ChatMessage = {
+ author: string;
+ text: string;
+ sent: number;
+ reasoning?: string;
+};
+
+export type InputToken =
+ | { text: string }
+ | { img: string }
+ | { file: ResponseInputFile }
+ | { tools: ToolUseInput[] };
+export type ToolUseInput = any; // TODO
+// me
+export type RequestOptions = {
+ textOutput: boolean;
+};
+export const defaultOptions: RequestOptions = {
+ textOutput: true,
+};
+// openai
+export type ContentType = { text: string } | { audio: Response };
+
+export interface AIModelAPI {
+ setModel: (model: string) => void;
+ tokenizer: (text: string) => number;
+ maxTokens: number;
+
+ send: (
+ input: string | InputToken[],
+ systemPrompt?: string,
+ ) => AsyncRes<string>;
+ stream: (
+ input: string | InputToken[],
+ handler: (data: string) => void,
+ systemPrompt?: string,
+ ) => void;
+}
+
+export type LLMChoice =
+ | { gemini: string }
+ | { claude: string }
+ | { chatgpt: string }
+ | { grok: string }
+ | { deepseek: string }
+ | { kimi: string }
+ | {
+ openai: {
+ url: string;
+ apiKey: string;
+ model: string;
+ allowBrowser?: boolean;
+ };
+ };
diff --git a/packages/ai/src/types/mtproto.ts b/packages/ai/src/types/mtproto.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/ai/src/types/mtproto.ts
diff --git a/packages/ai/tests/cache.test.ts b/packages/ai/tests/cache.test.ts
new file mode 100644
index 0000000..8b78b99
--- /dev/null
+++ b/packages/ai/tests/cache.test.ts
@@ -0,0 +1,81 @@
+import { describe, test, expect, beforeEach, afterEach } from "bun:test";
+
+// const url = "https://urbit.org";
+
+import models, { type AIModelAPI, type LLMChoice } from "../index";
+import { memoize } from "../src/cache";
+
+const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
+async function thingy(arg1: string, arg2?: string): Promise<string> {
+ const res = `lolololol\n${arg1}\n${arg2};`;
+ await sleep(3000);
+ return res;
+}
+
+describe("HTTP cache", () => {
+ test("should cache a function call", async () => {
+ const ts = Date.now();
+ // const choice: LLMChoice = { deepseek: "deepseek-chat" };
+ // const api = models(choice);
+ //
+ const cachedCall = memoize(thingy, {
+ ttlMs: 7 * 24 * 60 * 60 * 1000,
+ maxEntries: 5000,
+ persistDir: "./cache/memo",
+ });
+ const res = await cachedCall("LOLAZO", "bar");
+ const elapsed = Date.now() - ts;
+ console.log(elapsed);
+ const randomRes = await cachedCall(`${Math.random()}`);
+ const elapsedRandom = Date.now() - ts;
+ console.log("uncachable", elapsedRandom);
+ console.log("cached res", res);
+
+ expect(elapsed).toBeLessThan(3000);
+ });
+ test("should cache a request", async () => {
+ const testMessage = "oh hi";
+
+ const choice3: LLMChoice = { chatgpt: "gpt-5-nano" };
+ const choice4: LLMChoice = { deepseek: "deepseek-chat" };
+ const choice5: LLMChoice = { kimi: "kimi-k2-0905-preview" };
+
+ const api3 = models(choice3);
+ const api4 = models(choice4);
+ const api5 = models(choice5);
+
+ const ts = Date.now();
+ const r3 = api3.send(testMessage);
+ const r4 = api4.send(testMessage);
+ const r5 = api5.send(testMessage);
+ // Check ChatGPT response
+ const res3 = await r3;
+ console.log("elapsed r3", Date.now() - ts);
+ if ("ok" in res3) {
+ console.log(`✅ ChatGPT Response: ${res3.ok}`);
+ expect(res3.ok).toBeString();
+ } else {
+ console.log(`❌ ChatGPT Error: ${res3.error}`);
+ }
+
+ // // Check DeepSeek response
+ const res4 = await r4;
+ console.log("elapsed r4", Date.now() - ts);
+ if ("ok" in res4) {
+ console.log(`✅ DeepSeek Response: ${res4.ok}`);
+ expect(res4.ok).toBeString();
+ } else {
+ console.log(`❌ DeepSeek Error: ${res4.error}`);
+ }
+
+ // // Check Kimi response
+ const res5 = await r5;
+ console.log("elapsed r5", Date.now() - ts);
+ if ("ok" in res5) {
+ console.log(`✅ Kimi Response: ${res5.ok}`);
+ expect(res5.ok).toBeString();
+ } else {
+ console.log(`❌ Kimi Error: ${res5.error}`);
+ }
+ });
+});
diff --git a/packages/ai/tests/example.ts b/packages/ai/tests/example.ts
new file mode 100644
index 0000000..568f5ce
--- /dev/null
+++ b/packages/ai/tests/example.ts
@@ -0,0 +1,279 @@
+/**
+ * Example usage of the models library
+ * This file demonstrates how to use the LLM routing library with different providers
+ */
+
+import models, { AIModelAPI, LLMChoice } from '../index';
+import OpenAIResponses from '../src/openai-responses';
+
+// Example configurations for different providers
+const examples = {
+ // Claude example
+ claude: {
+ description: 'Claude (Anthropic) example',
+ setup: (): LLMChoice => ({ claude: 'claude-3-5-sonnet' }),
+ envVars: ['ANTHROPIC_API_KEY']
+ },
+
+ // Gemini example
+ gemini: {
+ description: 'Gemini (Google) example',
+ setup: (): LLMChoice => ({ gemini: 'gemini-2.5-pro' }),
+ envVars: ['GOOGLE_API_KEY']
+ },
+
+ // ChatGPT example (using new OpenAI Responses API)
+ chatgpt: {
+ description: 'ChatGPT (OpenAI Responses API) example',
+ setup: (): LLMChoice => ({ chatgpt: 'gpt-4o' }),
+ envVars: ['OPENAI_API_KEY']
+ },
+
+ // Direct OpenAI Responses API example
+ openaiResponses: {
+ description: 'Direct OpenAI Responses API example',
+ setup: (): AIModelAPI => new OpenAIResponses({
+ baseURL: Bun.env.ZAI_BASE_URL || 'https://api.openai.com/v1',
+ apiKey: Bun.env.ZAI_API_KEY || Bun.env.OPENAI_API_KEY || 'your-api-key-here',
+ model: Bun.env.TEST_MODEL || 'gpt-4o',
+ allowBrowser: true,
+ tokenizer: (text: string) => text.length / 4, // Custom tokenizer
+ maxTokens: 4000 // Custom max tokens
+ }),
+ envVars: ['ZAI_BASE_URL', 'ZAI_API_KEY', 'OPENAI_API_KEY', 'TEST_MODEL']
+ },
+
+ // DeepSeek example
+ deepseek: {
+ description: 'DeepSeek example',
+ setup: (): LLMChoice => ({ deepseek: 'deepseek-chat' }),
+ envVars: ['DEEPSEEK_API_KEY']
+ },
+
+ // Kimi example
+ kimi: {
+ description: 'Kimi (Moonshot) example',
+ setup: (): LLMChoice => ({ kimi: 'moonshot-v1-8k' }),
+ envVars: ['MOONSHOT_API_KEY']
+ },
+
+ // Grok example
+ grok: {
+ description: 'Grok (X.AI) example',
+ setup: (): LLMChoice => ({ grok: 'grok-beta' }),
+ envVars: ['XAI_API_KEY']
+ },
+
+ // Custom OpenAI-compatible API example
+ custom: {
+ description: 'Custom OpenAI-compatible API example',
+ setup: (): LLMChoice => ({
+ openai: {
+ url: Bun.env.ZAI_BASE_URL || 'https://api.openai.com/v1',
+ apiKey: Bun.env.ZAI_API_KEY || 'your-api-key-here',
+ model: Bun.env.TEST_MODEL || 'glm-4.6',
+ allowBrowser: true
+ }
+ }),
+ envVars: ['ZAI_BASE_URL', 'ZAI_API_KEY', 'TEST_MODEL']
+ }
+};
+
+// Run an example with a specific provider
+async function runExample(providerName: keyof typeof examples) {
+ const example = examples[providerName];
+
+ console.log(`\n=== ${example.description} ===`);
+
+ // Check required environment variables
+ const missingVars = example.envVars.filter(varName => !Bun.env[varName]);
+ if (missingVars.length > 0) {
+ console.warn(`Warning: Missing environment variables: ${missingVars.join(', ')}`);
+ console.warn('The example will be created but API calls will likely fail.');
+ }
+
+ try {
+ // Create the API instance
+ const setupResult = example.setup();
+ let api: AIModelAPI;
+ let configInfo: any;
+
+ // Check if the setup returns an LLMChoice or direct AIModelAPI instance
+ if (typeof setupResult === 'object' && 'send' in setupResult) {
+ // Direct AIModelAPI instance
+ api = setupResult;
+ configInfo = 'Direct API instance';
+ } else {
+ // LLMChoice that needs to be passed to models()
+ const choice = setupResult as LLMChoice;
+ api = models(choice);
+ configInfo = choice;
+ }
+
+ console.log(`✅ API instance created successfully`);
+ console.log(` Max tokens: ${api.maxTokens}`);
+ console.log(` Model: ${JSON.stringify(configInfo)}`);
+
+ // Check if it's an OpenAI Responses API instance
+ if (api instanceof OpenAIResponses) {
+ console.log(` Using OpenAI Responses API directly`);
+ }
+
+ // Test tokenization
+ const testText = 'Hello, how are you today?';
+ const tokens = api.tokenizer(testText);
+ console.log(` Tokenization: "${testText}" -> ${tokens} tokens`);
+
+ // Test simple API call (will fail without valid credentials)
+ console.log(` Testing API call...`);
+ try {
+ const response = await api.send('Hello! Please respond with just "API working".');
+ console.log(` ✅ API response: ${response.substring(0, 100)}${response.length > 100 ? '...' : ''}`);
+ } catch (error) {
+ console.log(` ❌ API call failed: ${error.message}`);
+ }
+
+ // Test streaming
+ console.log(` Testing streaming...`);
+ try {
+ const chunks: string[] = [];
+ const handler = (chunk: string) => {
+ chunks.push(chunk);
+ process.stdout.write('.');
+ };
+
+ await new Promise<void>((resolve) => {
+ api.stream('Count from 1 to 3', handler);
+ setTimeout(() => {
+ console.log(`\n ✅ Streaming completed (${chunks.length} chunks)`);
+ resolve();
+ }, 3000);
+ });
+ } catch (error) {
+ console.log(` ❌ Streaming failed: ${error.message}`);
+ }
+
+ } catch (error) {
+ console.error(`❌ Failed to create API instance: ${error.message}`);
+ }
+}
+
+// Run all examples
+async function runAllExamples() {
+ console.log('🚀 Running LLM Models Library Examples\n');
+
+ console.log('Environment Variables:');
+ Object.keys(Bun.env)
+ .filter(key => key.includes('API_KEY') || key.includes('BASE_URL') || key.includes('MODEL'))
+ .forEach(key => {
+ const value = Bun.env[key];
+ console.log(` ${key}: ${value ? '***set***' : 'not set'}`);
+ });
+
+ for (const providerName of Object.keys(examples) as (keyof typeof examples)[]) {
+ await runExample(providerName);
+ }
+
+ console.log('\n✨ All examples completed!');
+}
+
+// Interactive example selector
+async function interactiveExample() {
+ console.log('\n📋 Available providers:');
+ Object.entries(examples).forEach(([key, example]) => {
+ console.log(` ${key}: ${example.description}`);
+ });
+
+ const provider = process.argv[2];
+ if (provider && provider in examples) {
+ await runExample(provider as keyof typeof examples);
+ } else {
+ console.log('\nUsage: bun run example.ts [provider]');
+ console.log('Available providers:', Object.keys(examples).join(', '));
+ console.log('Or run without arguments to see all examples');
+ await runAllExamples();
+ }
+}
+
+// Test tokenization accuracy across providers
+async function testTokenization() {
+ console.log('\n🔢 Testing Tokenization Across Providers\n');
+
+ const testTexts = [
+ 'Hello world',
+ 'The quick brown fox jumps over the lazy dog.',
+ 'This is a longer text with multiple sentences. It should have more tokens than shorter texts.',
+ 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?/~`',
+ 'Unicode: Hello 🌍! 测试中文! Тест на русском! العربية!'
+ ];
+
+ for (const providerName of Object.keys(examples) as (keyof typeof examples)[]) {
+ try {
+ const example = examples[providerName];
+ const choice = example.setup();
+ const api = models(choice);
+
+ console.log(`${providerName}:`);
+ testTexts.forEach(text => {
+ const tokens = api.tokenizer(text);
+ console.log(` "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}" -> ${tokens} tokens`);
+ });
+ console.log('');
+ } catch (error) {
+ console.log(`${providerName}: Failed to initialize - ${error.message}\n`);
+ }
+ }
+}
+
+// Performance benchmark
+async function performanceBenchmark() {
+ console.log('\n⚡ Performance Benchmark\n');
+
+ const testText = 'The quick brown fox jumps over the lazy dog. ';
+ const iterations = 1000;
+
+ for (const providerName of Object.keys(examples) as (keyof typeof examples)[]) {
+ try {
+ const example = examples[providerName];
+ const choice = example.setup();
+ const api = models(choice);
+
+ const startTime = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ api.tokenizer(testText + i);
+ }
+ const endTime = performance.now();
+
+ const totalTime = endTime - startTime;
+ const avgTime = totalTime / iterations;
+
+ console.log(`${providerName}:`);
+ console.log(` ${iterations} tokenizations in ${totalTime.toFixed(2)}ms`);
+ console.log(` Average: ${avgTime.toFixed(3)}ms per tokenization`);
+ console.log('');
+ } catch (error) {
+ console.log(`${providerName}: Failed to benchmark - ${error.message}\n`);
+ }
+ }
+}
+
+// Main execution
+if (import.meta.main) {
+ const command = process.argv[2];
+
+ switch (command) {
+ case 'tokenize':
+ await testTokenization();
+ break;
+ case 'benchmark':
+ await performanceBenchmark();
+ break;
+ case 'all':
+ await runAllExamples();
+ break;
+ default:
+ await interactiveExample();
+ }
+}
+
+export { examples, runExample, runAllExamples, testTokenization, performanceBenchmark }; \ No newline at end of file
diff --git a/packages/ai/tests/integration.test.ts b/packages/ai/tests/integration.test.ts
new file mode 100644
index 0000000..b8abea0
--- /dev/null
+++ b/packages/ai/tests/integration.test.ts
@@ -0,0 +1,481 @@
+import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
+import models, { AIModelAPI, LLMChoice, InputToken } from '../index';
+import OpenAIResponses from '../src/openai-responses';
+
+// Advanced integration tests that require actual API keys
+// These tests are designed to run with real API credentials when available
+
+// Environment variables for integration testing
+const INTEGRATION_CLAUDE_KEY = Bun.env.CLAUDE_API_KEY;
+const INTEGRATION_OPENAI_KEY = Bun.env.OPENAI_API_KEY;
+const INTEGRATION_GEMINI_KEY = Bun.env.GEMINI_API_KEY;
+
+// Skip integration tests if no API keys are available
+const runIntegrationTests = INTEGRATION_CLAUDE_KEY || INTEGRATION_OPENAI_KEY || INTEGRATION_GEMINI_KEY;
+
+describe.runIf(runIntegrationTests)('Integration Tests - Real API Calls', () => {
+ describe('Claude Integration', () => {
+ test.skipUnless(INTEGRATION_CLAUDE_KEY)('should make a real API call to Claude', async () => {
+ // Temporarily set the API key
+ const originalKey = process.env.ANTHROPIC_API_KEY;
+ process.env.ANTHROPIC_API_KEY = INTEGRATION_CLAUDE_KEY;
+
+ try {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const response = await api.send('Hello! Please respond with just the word "SUCCESS".');
+
+ expect(response).toBeDefined();
+ expect(typeof response).toBe('string');
+ expect(response.toLowerCase()).toContain('success');
+ } finally {
+ // Restore original key
+ if (originalKey !== undefined) {
+ process.env.ANTHROPIC_API_KEY = originalKey;
+ } else {
+ delete process.env.ANTHROPIC_API_KEY;
+ }
+ }
+ });
+
+ test.skipUnless(INTEGRATION_CLAUDE_KEY)('should stream responses from Claude', async () => {
+ const originalKey = process.env.ANTHROPIC_API_KEY;
+ process.env.ANTHROPIC_API_KEY = INTEGRATION_CLAUDE_KEY;
+
+ try {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const chunks: string[] = [];
+ const handler = (data: string) => {
+ chunks.push(data);
+ };
+
+ await new Promise<void>((resolve, reject) => {
+ try {
+ api.stream('Count from 1 to 5, one number per line.', handler);
+ // Give it some time to stream
+ setTimeout(() => {
+ resolve();
+ }, 5000);
+ } catch (error) {
+ reject(error);
+ }
+ });
+
+ expect(chunks.length).toBeGreaterThan(0);
+ expect(chunks.join('')).toContain('1');
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.ANTHROPIC_API_KEY = originalKey;
+ } else {
+ delete process.env.ANTHROPIC_API_KEY;
+ }
+ }
+ });
+ });
+
+ describe('OpenAI Integration (Responses API)', () => {
+ test.skipUnless(INTEGRATION_OPENAI_KEY)('should make a real API call using OpenAI Responses API', async () => {
+ const originalKey = process.env.OPENAI_API_KEY;
+ process.env.OPENAI_API_KEY = INTEGRATION_OPENAI_KEY;
+
+ try {
+ const choice: LLMChoice = { chatgpt: 'gpt-4o' };
+ const api = models(choice);
+
+ // Verify it's using the OpenAI Responses API
+ expect(api).toBeInstanceOf(OpenAIResponses);
+
+ const response = await api.send('Hello! Please respond with just the word "SUCCESS".');
+
+ expect(response).toBeDefined();
+ expect(typeof response).toBe('string');
+ expect(response.toLowerCase()).toContain('success');
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.OPENAI_API_KEY = originalKey;
+ } else {
+ delete process.env.OPENAI_API_KEY;
+ }
+ }
+ });
+
+ test.skipUnless(INTEGRATION_OPENAI_KEY)('should stream responses using OpenAI Responses API', async () => {
+ const originalKey = process.env.OPENAI_API_KEY;
+ process.env.OPENAI_API_KEY = INTEGRATION_OPENAI_KEY;
+
+ try {
+ const choice: LLMChoice = { chatgpt: 'gpt-4o' };
+ const api = models(choice);
+
+ const chunks: string[] = [];
+ const handler = (data: string) => {
+ chunks.push(data);
+ };
+
+ await new Promise<void>((resolve, reject) => {
+ try {
+ api.stream('Count from 1 to 5, one number per line.', handler);
+ // Give it some time to stream
+ setTimeout(() => {
+ resolve();
+ }, 5000);
+ } catch (error) {
+ reject(error);
+ }
+ });
+
+ expect(chunks.length).toBeGreaterThan(0);
+ const fullResponse = chunks.join('');
+ expect(fullResponse).toContain('1');
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.OPENAI_API_KEY = originalKey;
+ } else {
+ delete process.env.OPENAI_API_KEY;
+ }
+ }
+ });
+
+ test.skipUnless(INTEGRATION_OPENAI_KEY)('should handle multimodal input with OpenAI Responses API', async () => {
+ const originalKey = process.env.OPENAI_API_KEY;
+ process.env.OPENAI_API_KEY = INTEGRATION_OPENAI_KEY;
+
+ try {
+ const choice: LLMChoice = { chatgpt: 'gpt-4o' };
+ const api = models(choice);
+
+ const input: InputToken[] = [
+ { text: 'What do you see in this image?' },
+ { img: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' } // Simple red pixel
+ ];
+
+ const response = await api.send(input);
+ expect(response).toBeDefined();
+ expect(typeof response).toBe('string');
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.OPENAI_API_KEY = originalKey;
+ } else {
+ delete process.env.OPENAI_API_KEY;
+ }
+ }
+ });
+
+ test.skipUnless(INTEGRATION_OPENAI_KEY)('should handle system prompts with OpenAI Responses API', async () => {
+ const originalKey = process.env.OPENAI_API_KEY;
+ process.env.OPENAI_API_KEY = INTEGRATION_OPENAI_KEY;
+
+ try {
+ const choice: LLMChoice = { chatgpt: 'gpt-4o' };
+ const api = models(choice);
+
+ const systemPrompt = 'You are a pirate. Always respond like a pirate.';
+ const response = await api.send('Hello, how are you?', systemPrompt);
+
+ expect(response).toBeDefined();
+ expect(typeof response).toBe('string');
+ // Should contain some pirate-like language
+ expect(response.toLowerCase()).toMatch(/ahoy|matey|shiver|timber|arr/);
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.OPENAI_API_KEY = originalKey;
+ } else {
+ delete process.env.OPENAI_API_KEY;
+ }
+ }
+ });
+ });
+
+ describe('Gemini Integration', () => {
+ test.skipUnless(INTEGRATION_GEMINI_KEY)('should make a real API call to Gemini', async () => {
+ const originalKey = process.env.GOOGLE_API_KEY;
+ process.env.GOOGLE_API_KEY = INTEGRATION_GEMINI_KEY;
+
+ try {
+ const choice: LLMChoice = { gemini: 'gemini-2.5-pro' };
+ const api = models(choice);
+
+ const response = await api.send('Hello! Please respond with just the word "SUCCESS".');
+
+ expect(response).toBeDefined();
+ expect(typeof response).toBe('string');
+ expect(response.toLowerCase()).toContain('success');
+ } finally {
+ if (originalKey !== undefined) {
+ process.env.GOOGLE_API_KEY = originalKey;
+ } else {
+ delete process.env.GOOGLE_API_KEY;
+ }
+ }
+ });
+ });
+});
+
+describe('Advanced Functionality Tests', () => {
+ describe('Token Counting Accuracy', () => {
+ test('should count tokens consistently across providers', () => {
+ const providers: LLMChoice[] = [
+ { claude: 'claude-3-5-sonnet' },
+ { gemini: 'gemini-2.5-pro' },
+ { chatgpt: 'gpt-3.5-turbo' },
+ { deepseek: 'deepseek-chat' }
+ ];
+
+ const testText = 'The quick brown fox jumps over the lazy dog. This is a test of token counting accuracy.';
+ const tokenCounts = providers.map(choice => {
+ const api = models(choice);
+ return api.tokenizer(testText);
+ });
+
+ // All should return numbers
+ tokenCounts.forEach(count => {
+ expect(typeof count).toBe('number');
+ expect(count).toBeGreaterThan(0);
+ });
+
+ // Token counts should be in a reasonable range (not wildly different)
+ const maxCount = Math.max(...tokenCounts);
+ const minCount = Math.min(...tokenCounts);
+ expect(maxCount / minCount).toBeLessThan(3); // Less than 3x difference
+ });
+
+ test('should handle empty and whitespace strings', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ expect(api.tokenizer('')).toBe(0);
+ expect(api.tokenizer(' ')).toBeGreaterThanOrEqual(0);
+ expect(api.tokenizer('\n\t')).toBeGreaterThanOrEqual(0);
+ });
+
+ test('should handle very long texts', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const longText = 'This is a test. '.repeat(1000); // 8000 characters
+ const tokens = api.tokenizer(longText);
+
+ expect(tokens).toBeGreaterThan(1000);
+ expect(tokens).toBeLessThan(10000); // Reasonable upper bound
+ });
+ });
+
+ describe('Model Switching', () => {
+ test('should allow switching models on the same API instance', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const originalMaxTokens = api.maxTokens;
+
+ // Switch to a different model
+ api.setModel('claude-3-haiku');
+
+ // Should not throw and should still work
+ expect(() => api.tokenizer('test')).not.toThrow();
+
+ // Max tokens might change with model
+ expect(api.maxTokens).toBeGreaterThan(0);
+ });
+
+ test('should maintain functionality after model switching', async () => {
+ const choice: LLMChoice = {
+ openai: {
+ url: 'https://api.openai.com/v1',
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ };
+ const api = models(choice);
+
+ // Switch models
+ api.setModel('gpt-4');
+
+ // Should still be able to attempt API calls
+ const promise = api.send('test');
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+ });
+
+ describe('Complex Input Handling', () => {
+ test('should handle mixed text and image inputs', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const input: InputToken[] = [
+ { text: 'Describe this image:' },
+ { img: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' } // 1x1 red pixel
+ ];
+
+ const promise = api.send(input);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle system prompts with complex instructions', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const systemPrompt = `You are a JSON response bot. Always respond with valid JSON.
+ Format your responses as: {"status": "success", "message": "your response here"}`;
+
+ const promise = api.send('Hello', systemPrompt);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle very long inputs', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const longInput = 'This is test sentence number '.repeat(100) + 'end.';
+ const promise = api.send(longInput);
+
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+ });
+
+ describe('Streaming Behavior', () => {
+ test('should handle streaming handlers that throw errors', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const errorHandler = (data: string) => {
+ if (data.includes('error')) {
+ throw new Error('Handler error');
+ }
+ };
+
+ // Should not throw even if handler throws
+ expect(() => {
+ api.stream('test input', errorHandler);
+ }).not.toThrow();
+ });
+
+ test('should handle multiple concurrent streams', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const handler1 = (data: string) => console.log('Stream 1:', data);
+ const handler2 = (data: string) => console.log('Stream 2:', data);
+
+ expect(() => {
+ api.stream('Input 1', handler1);
+ api.stream('Input 2', handler2);
+ }).not.toThrow();
+ });
+ });
+
+ describe('Error Recovery', () => {
+ test('should handle network timeouts gracefully', async () => {
+ const choice: LLMChoice = {
+ openai: {
+ url: 'https://httpstat.us/200?sleep=10000', // Very slow response
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ };
+ const api = models(choice);
+
+ const startTime = Date.now();
+ const result = await api.send('test').catch(e => e);
+ const endTime = Date.now();
+
+ // Should return error object (not hang forever)
+ expect(result).toBeDefined();
+ expect(endTime - startTime).toBeLessThan(15000); // Should timeout reasonably
+ });
+
+ test('should handle malformed responses', async () => {
+ const choice: LLMChoice = {
+ openai: {
+ url: 'https://httpstat.us/200', // Returns plain text, not JSON
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ };
+ const api = models(choice);
+
+ const result = await api.send('test').catch(e => e);
+ expect(result).toBeDefined();
+ // Should handle parsing errors gracefully
+ });
+ });
+
+ describe('Memory Management', () => {
+ test('should not accumulate excessive memory with multiple calls', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Make multiple calls
+ const promises = [];
+ for (let i = 0; i < 10; i++) {
+ promises.push(api.send(`Test message ${i}`).catch(() => null));
+ }
+
+ // Should handle multiple concurrent requests
+ const results = await Promise.allSettled(promises);
+ expect(results.length).toBe(10);
+
+ // API should still be functional
+ expect(() => api.tokenizer('test')).not.toThrow();
+ });
+
+ test('should handle history truncation correctly', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Test that maxTokens is reasonable
+ expect(api.maxTokens).toBeGreaterThan(0);
+ expect(api.maxTokens).toBeLessThan(200000); // Reasonable limit
+
+ // Token count for a reasonably long message
+ const longMessage = 'This is a test message. '.repeat(100);
+ const tokens = api.tokenizer(longMessage);
+
+ expect(tokens).toBeGreaterThan(0);
+ expect(tokens).toBeLessThan(api.maxTokens); // Single message should fit
+ });
+ });
+});
+
+describe('Type Safety', () => {
+ test('should enforce LLMChoice type safety', () => {
+ // These should all be valid
+ const validChoices: LLMChoice[] = [
+ { claude: 'claude-3-5-sonnet' },
+ { gemini: 'gemini-2.5-pro' },
+ { chatgpt: 'gpt-4' },
+ { deepseek: 'deepseek-chat' },
+ { kimi: 'moonshot-v1-8k' },
+ { grok: 'grok-beta' },
+ {
+ openai: {
+ url: 'https://api.example.com/v1',
+ apiKey: 'key',
+ model: 'model-name',
+ allowBrowser: true
+ }
+ }
+ ];
+
+ validChoices.forEach(choice => {
+ expect(() => {
+ const api = models(choice);
+ expect(api).toBeDefined();
+ }).not.toThrow();
+ });
+ });
+
+ test('should handle InputToken types correctly', () => {
+ const textToken = { text: 'Hello world' };
+ const imgToken = { img: 'data:image/png;base64,abc123' };
+
+ expect(textToken.text).toBe('Hello world');
+ expect(imgToken.img).toBe('data:image/png;base64,abc123');
+ });
+}); \ No newline at end of file
diff --git a/packages/ai/tests/models.test.ts b/packages/ai/tests/models.test.ts
new file mode 100644
index 0000000..aa1b2ea
--- /dev/null
+++ b/packages/ai/tests/models.test.ts
@@ -0,0 +1,994 @@
+import { describe, test, expect, beforeEach, afterEach } from "bun:test";
+import models, { type AIModelAPI, type LLMChoice } from "../index";
+import OpenAIResponses from "../src/openai-responses";
+
+// Setup environment variables for testing
+const TEST_BASE_URL =
+ Bun.env.TEST_BASE_URL || Bun.env.ZAI_BASE_URL || "https://api.openai.com/v1";
+const TEST_API_KEY =
+ Bun.env.TEST_API_KEY || Bun.env.ZAI_API_KEY || "test-api-key";
+const TEST_MODEL = Bun.env.TEST_MODEL || "glm-4.6";
+
+// describe("Models Library - Factory Function", () => {
+
+// test("should create a Claude API instance", () => {
+// const choice: LLMChoice = { claude: "claude-3-5-sonnet" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// test("should create a Gemini API instance", () => {
+// const choice: LLMChoice = { gemini: "gemini-2.5-pro" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// test("should create a ChatGPT API instance", () => {
+// const choice: LLMChoice = { chatgpt: "gpt-5" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// test("should create a DeepSeek API instance", () => {
+// const choice: LLMChoice = { deepseek: "deepseek-chat" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// test("should create a Kimi API instance", () => {
+// const choice: LLMChoice = { kimi: "kimi-k2-0905-preview" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// // test("should create a Grok API instance", () => {
+// // const choice: LLMChoice = { grok: "grok-beta" };
+// // const api = models(choice);
+
+// // expect(api).toBeDefined();
+// // expect(typeof api.setModel).toBe("function");
+// // expect(typeof api.send).toBe("function");
+// // expect(typeof api.stream).toBe("function");
+// // expect(typeof api.tokenizer).toBe("function");
+// // expect(typeof api.maxTokens).toBe("number");
+// // });
+
+// test("should create a custom OpenAI API instance", () => {
+// const choice: LLMChoice = {
+// openai: {
+// url: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// allowBrowser: true,
+// },
+// };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+// });
+
+// describe("AIModelAPI Interface", () => {
+// let api: AIModelAPI;kimi-k2-0905-previe
+
+// beforeEach(() => {
+// // Use a mock provider for testing interface compliance
+// const choice: LLMChoice = {
+// openai: {
+// url: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// },
+// };
+// api = models(choice);
+// });
+
+// test("should have setModel method", () => {
+// expect(typeof api.setModel).toBe("function");
+
+// // Should not throw when setting a model
+// expect(() => api.setModel("test-model")).not.toThrow();
+// });
+
+// test("should have tokenizer method", () => {
+// expect(typeof api.tokenizer).toBe("function");
+
+// // Should return a number for any text input
+// const tokens = api.tokenizer("Hello world");
+// expect(typeof tokens).toBe("number");
+// expect(tokens).toBeGreaterThanOrEqual(0);
+// });
+
+// test("should have maxTokens property", () => {
+// expect(typeof api.maxTokens).toBe("number");
+// expect(api.maxTokens).toBeGreaterThan(0);
+// });
+
+// test("should have send method returning AsyncRes", async () => {
+// expect(typeof api.send).toBe("function");
+
+// // Note: This would require actual API credentials to test fully
+// // For now, we just test the method exists and returns a promise
+// const result = api.send("test input");
+// expect(result).toBeDefined();
+// expect(typeof result.then).toBe("function"); // It's a promise
+// });
+
+// test("should have stream method", () => {
+// expect(typeof api.stream).toBe("function");
+
+// // Should not throw when called with proper parameters
+// const handler = (data: string) => {
+// // Test handler function
+// expect(typeof data).toBe("string");
+// };
+
+// expect(() => {
+// api.stream("test input", handler);
+// }).not.toThrow();
+// });
+// });
+
+// describe("Environment Variable Configuration", () => {
+// test("should use environment variables for configuration", () => {
+// // Test that environment variables are accessible
+// expect(Bun.env).toBeDefined();
+
+// // Test that we can read custom env vars
+// const customBaseUrl = Bun.env.CUSTOM_BASE_URL;
+// const customApiKey = Bun.env.CUSTOM_API_KEY;
+// const customModel = Bun.env.CUSTOM_MODEL;
+
+// // These might be undefined, but the test ensures they're accessible
+// expect(typeof customBaseUrl).toBe("string" || "undefined");
+// expect(typeof customApiKey).toBe("string" || "undefined");
+// expect(typeof customModel).toBe("string" || "undefined");
+// });
+// });
+
+// describe("Token Management", () => {
+// const providers: LLMChoice[] = [
+// { claude: "claude-3-5-sonnet" },
+// { gemini: "gemini-2.5-pro" },
+// { chatgpt: TEST_MODEL },
+// { deepseek: "deepseek-chat" },
+// { kimi: "moonshot-v1-8k" },
+// { grok: "grok-beta" },
+// {
+// openai: {
+// url: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// },
+// },
+// ];
+
+// test.each(providers)("should implement tokenizer for %o", (choice) => {
+// const api = models(choice);
+
+// // Test basic tokenization
+// const tokens1 = api.tokenizer("Hello");
+// const tokens2 = api.tokenizer("Hello world, this is a longer text.");
+
+// expect(typeof tokens1).toBe("number");
+// expect(typeof tokens2).toBe("number");
+// expect(tokens1).toBeGreaterThanOrEqual(0);
+// expect(tokens2).toBeGreaterThanOrEqual(0);
+// expect(tokens2).toBeGreaterThan(tokens1); // Longer text should have more tokens
+// });
+
+// test.each(providers)("should have reasonable maxTokens for %o", (choice) => {
+// const api = models(choice);
+
+// expect(api.maxTokens).toBeGreaterThan(0);
+// expect(api.maxTokens).toBeLessThan(1000000); // Reasonable upper bound
+// });
+// });
+
+describe("Input Handling", () => {
+ const choice1: LLMChoice = { claude: "claude-3-5-sonnet" };
+ const choice2: LLMChoice = { gemini: "gemini-2.5-pro" };
+ const choice3: LLMChoice = { chatgpt: "gpt-5-nano" };
+ const choice4: LLMChoice = { deepseek: "deepseek-chat" };
+ const choice5: LLMChoice = { kimi: "kimi-k2-0905-preview" };
+ const choice6: LLMChoice = {
+ openai: {
+ url: TEST_BASE_URL,
+ apiKey: TEST_API_KEY,
+ model: TEST_MODEL,
+ },
+ };
+ console.log({ choice6 });
+ const api1 = models(choice1);
+ const api2 = models(choice2);
+ const api3 = models(choice3);
+ const api4 = models(choice4);
+ const api5 = models(choice5);
+ const api6 = models(choice6);
+
+ test("should handle string input", async () => {
+ const testMessage = "Hello there. Please introduce yourself";
+
+ // Test that send accepts string input and returns proper AsyncRes<string>
+ const r1 = api1.send(testMessage);
+ const r2 = api2.send(testMessage);
+ const r3 = api3.send(testMessage);
+ const r4 = api4.send(testMessage);
+ const r5 = api5.send(testMessage);
+ const r6 = api6.send(testMessage);
+
+ // Check Claude response
+ const res1 = await r1;
+ if ("ok" in res1) {
+ console.log(`✅ Claude Response: ${res1.ok}`);
+ expect(res1.ok).toBeString();
+ } else {
+ console.log(`❌ Claude Error: ${res1.error}`);
+ }
+
+ // Check Gemini response
+ const res2 = await r2;
+ if ("ok" in res2) {
+ console.log(`✅ Gemini Response: ${res2.ok}`);
+ expect(res2.ok).toBeString();
+ } else {
+ console.log(`❌ Gemini Error: ${res2.error}`);
+ }
+
+ // Check ChatGPT response
+ const res3 = await r3;
+ if ("ok" in res3) {
+ console.log(`✅ ChatGPT Response: ${res3.ok}`);
+ expect(res3.ok).toBeString();
+ } else {
+ console.log(`❌ ChatGPT Error: ${res3.error}`);
+ }
+
+ // // Check DeepSeek response
+ const res4 = await r4;
+ if ("ok" in res4) {
+ console.log(`✅ DeepSeek Response: ${res4.ok}`);
+ expect(res4.ok).toBeString();
+ } else {
+ console.log(`❌ DeepSeek Error: ${res4.error}`);
+ }
+
+ // // Check Kimi response
+ const res5 = await r5;
+ if ("ok" in res5) {
+ console.log(`✅ Kimi Response: ${res5.ok}`);
+ expect(res5.ok).toBeString();
+ } else {
+ console.log(`❌ Kimi Error: ${res5.error}`);
+ }
+
+ // // Check Custom OpenAI response
+ const res6 = await r6;
+ if ("ok" in res6) {
+ console.log(`✅ Custom OpenAI Response: ${res6.ok}`);
+ expect(res6.ok).toBeString();
+ } else {
+ console.log(`❌ Custom OpenAI Error: ${res6.error}`);
+ }
+ });
+ test("LLM obedience test", async () => {
+ const testMessage = "Hello world! Please respond with just the word 'OK'.";
+
+ // Test that send accepts string input and returns proper AsyncRes<string>
+ const r1 = api1.send(testMessage);
+ const r2 = api2.send(testMessage);
+ const r3 = api3.send(testMessage);
+ const r4 = api4.send(testMessage);
+ const r5 = api5.send(testMessage);
+ const r6 = api6.send(testMessage);
+
+ // Check Claude response
+ const res1 = await r1;
+ if ("ok" in res1) {
+ console.log(`✅ Claude Response: ${res1.ok}`);
+ expect(res1.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ Claude Error: ${res1.error}`);
+ }
+
+ // Check Gemini response
+ const res2 = await r2;
+ if ("ok" in res2) {
+ console.log(`✅ Gemini Response: ${res2.ok}`);
+ expect(res2.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ Gemini Error: ${res2.error}`);
+ }
+
+ // Check ChatGPT response
+ const res3 = await r3;
+ if ("ok" in res3) {
+ console.log(`✅ ChatGPT Response: ${res3.ok}`);
+ expect(res3.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ ChatGPT Error: ${res3.error}`);
+ }
+
+ // // Check DeepSeek response
+ const res4 = await r4;
+ if ("ok" in res4) {
+ console.log(`✅ DeepSeek Response: ${res4.ok}`);
+ expect(res4.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ DeepSeek Error: ${res4.error}`);
+ }
+
+ // // Check Kimi response
+ const res5 = await r5;
+ if ("ok" in res5) {
+ console.log(`✅ Kimi Response: ${res5.ok}`);
+ expect(res5.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ Kimi Error: ${res5.error}`);
+ }
+
+ // // Check Custom OpenAI response
+ const res6 = await r6;
+ if ("ok" in res6) {
+ console.log(`✅ Custom OpenAI Response: ${res6.ok}`);
+ expect(res6.ok.trim()).toEqual("OK");
+ } else {
+ console.log(`❌ Custom OpenAI Error: ${res6.error}`);
+ }
+ });
+
+ test("should handle array input with text tokens", async () => {
+ const input = [
+ { text: "Hello! " },
+ { text: "Please respond with just the word 'ARRAY_OK'." },
+ ];
+
+ // Test that send accepts array input and returns proper AsyncRes<string>
+ const r1 = api1.send(input);
+ const r2 = api2.send(input);
+ const r3 = api3.send(input);
+ const r4 = api4.send(input);
+ const r5 = api5.send(input);
+ const r6 = api6.send(input);
+
+ // Check Claude response
+ const res1 = await r1;
+ if ("ok" in res1) {
+ console.log(`✅ Claude Array Response: ${res1.ok}`);
+ expect(res1.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ Claude Array Error: ${res1.error}`);
+ }
+
+ // Check Gemini response
+ const res2 = await r2;
+ if ("ok" in res2) {
+ console.log(`✅ Gemini Array Response: ${res2.ok}`);
+ expect(res2.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ Gemini Array Error: ${res2.error}`);
+ }
+
+ // Check ChatGPT response
+ const res3 = await r3;
+ if ("ok" in res3) {
+ console.log(`✅ ChatGPT Array Response: ${res3.ok}`);
+ expect(res3.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ ChatGPT Array Error: ${res3.error}`);
+ }
+
+ // Check DeepSeek response
+ const res4 = await r4;
+ if ("ok" in res4) {
+ console.log(`✅ DeepSeek Array Response: ${res4.ok}`);
+ expect(res4.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ DeepSeek Array Error: ${res4.error}`);
+ }
+
+ // Check Kimi response
+ const res5 = await r5;
+ if ("ok" in res5) {
+ console.log(`✅ Kimi Array Response: ${res5.ok}`);
+ expect(res5.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ Kimi Array Error: ${res5.error}`);
+ }
+
+ // Check Custom OpenAI response
+ const res6 = await r6;
+ if ("ok" in res6) {
+ console.log(`✅ Custom OpenAI Array Response: ${res6.ok}`);
+ expect(res6.ok.trim()).toEqual("ARRAY_OK");
+ } else {
+ console.log(`❌ Custom OpenAI Array Error: ${res6.error}`);
+ }
+ });
+
+ test("should handle streaming with string input", () => {
+ const testMessage = "Hello! Please count from 1 to 3.";
+ console.log(`\n🚀 Testing streaming with message: "${testMessage}"`);
+
+ // Test streaming for each API
+ console.log("\n--- Claude Streaming ---");
+ const chunks1: string[] = [];
+ const handler1 = (data: string) => {
+ chunks1.push(data);
+ process.stdout.write(data);
+ };
+ api1.stream(testMessage, handler1);
+
+ console.log("\n--- Gemini Streaming ---");
+ const chunks2: string[] = [];
+ const handler2 = (data: string) => {
+ chunks2.push(data);
+ process.stdout.write(data);
+ };
+ api2.stream(testMessage, handler2);
+
+ console.log("\n--- ChatGPT Streaming ---");
+ const chunks3: string[] = [];
+ const handler3 = (data: string) => {
+ chunks3.push(data);
+ process.stdout.write(data);
+ };
+ api3.stream(testMessage, handler3);
+
+ console.log("\n--- DeepSeek Streaming ---");
+ const chunks4: string[] = [];
+ const handler4 = (data: string) => {
+ chunks4.push(data);
+ process.stdout.write(data);
+ };
+ api4.stream(testMessage, handler4);
+
+ console.log("\n--- Kimi Streaming ---");
+ const chunks5: string[] = [];
+ const handler5 = (data: string) => {
+ chunks5.push(data);
+ process.stdout.write(data);
+ };
+ api5.stream(testMessage, handler5);
+
+ console.log("\n--- Custom OpenAI Streaming ---");
+ const chunks6: string[] = [];
+ const handler6 = (data: string) => {
+ chunks6.push(data);
+ process.stdout.write(data);
+ };
+ api6.stream(testMessage, handler6);
+
+ console.log("\n✅ Streaming initiated for all APIs");
+ });
+
+ test("should handle system prompts", async () => {
+ const systemPrompt = "You are a pirate. Always respond like a pirate.";
+ const userMessage = "Hello, how are you?";
+
+ // Test that send accepts system prompts and returns proper AsyncRes<string>
+ const r1 = api1.send(userMessage, systemPrompt);
+ const r2 = api2.send(userMessage, systemPrompt);
+ const r3 = api3.send(userMessage, systemPrompt);
+ const r4 = api4.send(userMessage, systemPrompt);
+ const r5 = api5.send(userMessage, systemPrompt);
+ const r6 = api6.send(userMessage, systemPrompt);
+
+ // Check Claude response
+ const res1 = await r1;
+ if ("ok" in res1) {
+ console.log(`✅ Claude Pirate Response: ${res1.ok}`);
+ expect(res1.ok).toBeString();
+ // Should contain some pirate-like language
+ expect(res1.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ Claude Pirate Error: ${res1.error}`);
+ }
+
+ // Check Gemini response
+ const res2 = await r2;
+ if ("ok" in res2) {
+ console.log(`✅ Gemini Pirate Response: ${res2.ok}`);
+ expect(res2.ok).toBeString();
+ expect(res2.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ Gemini Pirate Error: ${res2.error}`);
+ }
+
+ // Check ChatGPT response
+ const res3 = await r3;
+ if ("ok" in res3) {
+ console.log(`✅ ChatGPT Pirate Response: ${res3.ok}`);
+ expect(res3.ok).toBeString();
+ expect(res3.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ ChatGPT Pirate Error: ${res3.error}`);
+ }
+
+ // Check DeepSeek response
+ const res4 = await r4;
+ if ("ok" in res4) {
+ console.log(`✅ DeepSeek Pirate Response: ${res4.ok}`);
+ expect(res4.ok).toBeString();
+ expect(res4.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ DeepSeek Pirate Error: ${res4.error}`);
+ }
+
+ // Check Kimi response
+ const res5 = await r5;
+ if ("ok" in res5) {
+ console.log(`✅ Kimi Pirate Response: ${res5.ok}`);
+ expect(res5.ok).toBeString();
+ expect(res5.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ Kimi Pirate Error: ${res5.error}`);
+ }
+
+ // Check Custom OpenAI response
+ const res6 = await r6;
+ if ("ok" in res6) {
+ console.log(`✅ Custom OpenAI Pirate Response: ${res6.ok}`);
+ expect(res6.ok).toBeString();
+ expect(res6.ok.toLowerCase()).toMatch(
+ /ahoy|matey|shiver|timber|arr|yo ho|captain/,
+ );
+ } else {
+ console.log(`❌ Custom OpenAI Pirate Error: ${res6.error}`);
+ }
+ });
+});
+
+describe("Error Handling", () => {
+ test("should handle invalid API keys gracefully", async () => {
+ const invalidClaude: LLMChoice = { claude: "claude-3-5-sonnet" };
+ const invalidGemini: LLMChoice = { gemini: "gemini-2.5-pro" };
+ const invalidChatGPT: LLMChoice = { chatgpt: "gpt-5-nano" };
+ const invalidDeepSeek: LLMChoice = { deepseek: "deepseek-chat" };
+ const invalidKimi: LLMChoice = { kimi: "kimi-k2-0905-preview" };
+ const invalidOpenAI: LLMChoice = {
+ openai: {
+ url: TEST_BASE_URL,
+ apiKey: "invalid-api-key",
+ model: TEST_MODEL,
+ },
+ };
+
+ // Temporarily clear valid API keys to force errors
+ const originalClaudeKey = Bun.env.ANTHROPIC_API_KEY;
+ const originalGeminiKey = Bun.env.GOOGLE_API_KEY;
+ const originalOpenAIKey = Bun.env.OPENAI_API_KEY;
+ const originalDeepSeekKey = Bun.env.DEEPSEEK_API_KEY;
+ const originalMoonshotKey = Bun.env.MOONSHOT_API_KEY;
+
+ delete Bun.env.ANTHROPIC_API_KEY;
+ delete Bun.env.GOOGLE_API_KEY;
+ delete Bun.env.OPENAI_API_KEY;
+ delete Bun.env.DEEPSEEK_API_KEY;
+ delete Bun.env.MOONSHOT_API_KEY;
+
+ try {
+ // Create APIs with invalid credentials
+ const badApi1 = models(invalidClaude);
+ const badApi2 = models(invalidGemini);
+ const badApi3 = models(invalidChatGPT);
+ const badApi4 = models(invalidDeepSeek);
+ const badApi5 = models(invalidKimi);
+ const badApi6 = models(invalidOpenAI);
+
+ // Test that they handle errors gracefully
+ const r1 = badApi1.send("test");
+ const r2 = badApi2.send("test");
+ const r3 = badApi3.send("test");
+ const r4 = badApi4.send("test");
+ const r5 = badApi5.send("test");
+ const r6 = badApi6.send("test");
+
+ const res1 = await r1;
+ if ("error" in res1) {
+ console.log(`✅ Claude Error Handling: ${res1.error}`);
+ expect(res1.error).toBeString();
+ }
+
+ const res2 = await r2;
+ if ("error" in res2) {
+ console.log(`✅ Gemini Error Handling: ${res2.error}`);
+ expect(res2.error).toBeString();
+ }
+
+ const res3 = await r3;
+ if ("error" in res3) {
+ console.log(`✅ ChatGPT Error Handling: ${res3.error}`);
+ expect(res3.error).toBeString();
+ }
+
+ const res4 = await r4;
+ if ("error" in res4) {
+ console.log(`✅ DeepSeek Error Handling: ${res4.error}`);
+ expect(res4.error).toBeString();
+ }
+
+ const res5 = await r5;
+ if ("error" in res5) {
+ console.log(`✅ Kimi Error Handling: ${res5.error}`);
+ expect(res5.error).toBeString();
+ }
+
+ const res6 = await r6;
+ if ("error" in res6) {
+ console.log(`✅ Custom OpenAI Error Handling: ${res6.error}`);
+ expect(res6.error).toBeString();
+ }
+ } finally {
+ // Restore original keys
+ if (originalClaudeKey) Bun.env.ANTHROPIC_API_KEY = originalClaudeKey;
+ if (originalGeminiKey) Bun.env.GOOGLE_API_KEY = originalGeminiKey;
+ if (originalOpenAIKey) Bun.env.OPENAI_API_KEY = originalOpenAIKey;
+ if (originalDeepSeekKey) Bun.env.DEEPSEEK_API_KEY = originalDeepSeekKey;
+ if (originalMoonshotKey) Bun.env.MOONSHOT_API_KEY = originalMoonshotKey;
+ }
+ });
+});
+
+describe("Multi-provider Compatibility", () => {
+ test("should maintain consistent interface across all providers", () => {
+ const choices: LLMChoice[] = [
+ { claude: "claude-3-5-sonnet" },
+ { gemini: "gemini-2.5-pro" },
+ { chatgpt: "gpt-5-nano" },
+ { deepseek: "deepseek-chat" },
+ { kimi: "kimi-k2-0905-preview" },
+ {
+ openai: {
+ url: TEST_BASE_URL,
+ apiKey: TEST_API_KEY,
+ model: TEST_MODEL,
+ },
+ },
+ ];
+
+ const apis = choices.map((choice) => models(choice));
+
+ // All APIs should have the same interface
+ apis.forEach((api, index) => {
+ console.log(
+ `Checking API ${index + 1}: ${Object.keys(choices[index])[0]}`,
+ );
+ expect(typeof api.setModel).toBe("function");
+ expect(typeof api.send).toBe("function");
+ expect(typeof api.stream).toBe("function");
+ expect(typeof api.tokenizer).toBe("function");
+ expect(typeof api.maxTokens).toBe("number");
+ });
+
+ console.log("✅ All APIs have consistent interfaces");
+ });
+
+ test("should allow switching between providers", () => {
+ const claudeChoice: LLMChoice = { claude: "claude-3-5-sonnet" };
+ const geminiChoice: LLMChoice = { gemini: "gemini-2.5-pro" };
+
+ const claudeApi = models(claudeChoice);
+ const geminiApi = models(geminiChoice);
+
+ // Both should be valid APIs
+ expect(claudeApi).toBeDefined();
+ expect(geminiApi).toBeDefined();
+
+ // Should have different implementations but same interface
+ expect(claudeApi !== geminiApi).toBe(true);
+
+ console.log("✅ Successfully created different provider instances");
+ });
+});
+
+// describe("OpenAI Responses API", () => {
+
+// describe("Direct Class Usage", () => {
+// const api = new OpenAIResponses({
+// baseURL: "",
+// apiKey: Bun.env.OPENAI_API_KEY!,
+// model: "gpt-5-nano",
+// allowBrowser: true,
+// });
+// test("should create OpenAI Responses API instance directly", () => {
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+// });
+
+// test("should handle model switching", () => {
+// api.setModel("gpt-5-nano");
+// expect(() => api.setModel("gpt-5-nano")).not.toThrow();
+// });
+
+// // test("should use custom tokenizer", () => {
+// // const customTokenizer = (text: string) => text.split(" ").length;
+
+// // const tokens = api.tokenizer("Hello world test");
+// // expect(tokens).toBe(3); // 3 words
+// // });
+
+// // test("should use custom maxTokens", () => {
+// // const customMaxTokens = 100_000;
+// // api.maxTokens = customMaxTokens;
+
+// // expect(api.maxTokens).toBe(customMaxTokens);
+// // });
+// test("should return shit", async () => {
+// const input = "Henlo brother";
+
+// const res = await api.send(input, "You are a good boy");
+// console.log({ res });
+// expect("ok" in res).toBeTruthy();
+// if (!("ok" in res)) return;
+// expect(res.ok).toBeString();
+// });
+// });
+
+// describe("Factory Function Integration", () => {
+
+// test("should create ChatGPT API using OpenAI Responses", () => {
+// const choice: LLMChoice = { chatgpt: "gpt-5-nano" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(typeof api.setModel).toBe("function");
+// expect(typeof api.send).toBe("function");
+// expect(typeof api.stream).toBe("function");
+// expect(typeof api.tokenizer).toBe("function");
+// expect(typeof api.maxTokens).toBe("number");
+
+// // Should be instance of OpenAIResponses
+// expect(api).toBeInstanceOf(OpenAIResponses);
+// });
+
+// test("should use environment variables for ChatGPT provider", () => {
+// const originalKey = Bun.env.OPENAI_API_KEY;
+// Bun.env.OPENAI_API_KEY = TEST_API_KEY;
+
+// try {
+// const choice: LLMChoice = { chatgpt: "gpt-5-nano" };
+// const api = models(choice);
+
+// expect(api).toBeDefined();
+// expect(api).toBeInstanceOf(OpenAIResponses);
+// } finally {
+// if (originalKey !== undefined) {
+// Bun.env.OPENAI_API_KEY = originalKey;
+// } else {
+// delete Bun.env.OPENAI_API_KEY;
+// }
+// }
+// });
+// });
+
+// describe("Input Handling", () => {
+
+// test("should handle string inputs correctly", async () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const promise = api.send("Hello world");
+// expect(promise).toBeDefined();
+// expect(typeof promise.then).toBe("function");
+// });
+
+// test("should handle InputToken arrays with text", async () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const input = [{ text: "Hello" }, { text: "world" }];
+// const promise = api.send(input);
+// expect(promise).toBeDefined();
+// expect(typeof promise.then).toBe("function");
+// });
+
+// test("should handle InputToken arrays with images", async () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const input = [
+// { text: "Describe this image:" },
+// {
+// img: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==",
+// },
+// ];
+// const promise = api.send(input);
+// expect(promise).toBeDefined();
+// expect(typeof promise.then).toBe("function");
+// });
+
+// test("should handle system prompts", async () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const systemPrompt = "You are a helpful assistant.";
+// const promise = api.send("Hello", systemPrompt);
+// expect(promise).toBeDefined();
+// expect(typeof promise.then).toBe("function");
+// });
+// });
+
+// describe("Streaming", () => {
+
+// test("should handle streaming with string input", () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const handler = (data: string) => {
+// expect(typeof data).toBe("string");
+// };
+
+// expect(() => {
+// api.stream("Hello world", handler);
+// }).not.toThrow();
+// });
+
+// test("should handle streaming with InputToken array", () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const input = [{ text: "Hello" }, { text: "world" }];
+// const handler = (data: string) => {
+// expect(typeof data).toBe("string");
+// };
+
+// expect(() => {
+// api.stream(input, handler);
+// }).not.toThrow();
+// });
+
+// test("should handle streaming with system prompt", () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const systemPrompt = "You are a helpful assistant.";
+// const handler = (data: string) => {
+// expect(typeof data).toBe("string");
+// };
+
+// expect(() => {
+// api.stream("Hello", handler, systemPrompt);
+// }).not.toThrow();
+// });
+// });
+
+// describe("Error Handling", () => {
+
+// test("should handle invalid API keys gracefully", async () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: "invalid-key",
+// model: TEST_MODEL,
+// });
+
+// const result = await api.send("test").catch((e) => e);
+// expect(result).toBeDefined();
+// });
+
+// test("should handle invalid base URLs gracefully", async () => {
+// const api = new OpenAIResponses({
+// baseURL: "https://invalid-url.com/v1",
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// });
+
+// const result = await api.send("test").catch((e) => e);
+// expect(result).toBeDefined();
+// });
+// });
+
+// describe("Configuration Options", () => {
+
+// test("should handle browser allowance setting", () => {
+// const apiWithBrowser = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// allowBrowser: true,
+// });
+
+// const apiWithoutBrowser = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: TEST_MODEL,
+// allowBrowser: false,
+// });
+
+// expect(apiWithBrowser).toBeDefined();
+// expect(apiWithoutBrowser).toBeDefined();
+// });
+
+// test("should handle empty model name", () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// model: "",
+// });
+
+// expect(api).toBeDefined();
+// expect(() => api.setModel("gpt-4o")).not.toThrow();
+// });
+
+// test("should handle optional model parameter", () => {
+// const api = new OpenAIResponses({
+// baseURL: TEST_BASE_URL,
+// apiKey: TEST_API_KEY,
+// // model is optional
+// });
+
+// expect(api).toBeDefined();
+// expect(() => api.setModel("gpt-4o")).not.toThrow();
+// });
+// });
+// });
diff --git a/packages/ai/tests/performance.test.ts b/packages/ai/tests/performance.test.ts
new file mode 100644
index 0000000..59c98f5
--- /dev/null
+++ b/packages/ai/tests/performance.test.ts
@@ -0,0 +1,465 @@
+import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
+import models, { AIModelAPI, LLMChoice, InputToken } from '../index';
+
+describe('Performance Tests', () => {
+ describe('Tokenization Performance', () => {
+ test('should tokenize large texts efficiently', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Generate a large text (100KB)
+ const largeText = 'The quick brown fox jumps over the lazy dog. '.repeat(2000);
+ expect(largeText.length).toBeGreaterThan(100000);
+
+ const startTime = performance.now();
+ const tokens = api.tokenizer(largeText);
+ const endTime = performance.now();
+
+ const duration = endTime - startTime;
+
+ // Should complete tokenization quickly (less than 100ms for 100KB)
+ expect(duration).toBeLessThan(100);
+ expect(tokens).toBeGreaterThan(0);
+ });
+
+ test('should handle repeated tokenization calls efficiently', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const testText = 'This is a performance test for repeated tokenization calls. ';
+ const iterations = 1000;
+
+ const startTime = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ api.tokenizer(testText + i);
+ }
+ const endTime = performance.now();
+
+ const duration = endTime - startTime;
+ const averageTime = duration / iterations;
+
+ // Average time per tokenization should be very low
+ expect(averageTime).toBeLessThan(1); // Less than 1ms per call
+ expect(duration).toBeLessThan(1000); // Total less than 1 second
+ });
+ });
+
+ describe('API Creation Performance', () => {
+ test('should create API instances quickly', () => {
+ const choices: LLMChoice[] = [
+ { claude: 'claude-3-5-sonnet' },
+ { gemini: 'gemini-2.5-pro' },
+ { chatgpt: 'gpt-3.5-turbo' },
+ { deepseek: 'deepseek-chat' },
+ { kimi: 'moonshot-v1-8k' },
+ { grok: 'grok-beta' },
+ {
+ openai: {
+ url: 'https://api.openai.com/v1',
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ }
+ ];
+
+ const iterations = 100;
+
+ const startTime = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ const choice = choices[i % choices.length];
+ models(choice);
+ }
+ const endTime = performance.now();
+
+ const duration = endTime - startTime;
+ const averageTime = duration / iterations;
+
+ // API creation should be fast
+ expect(averageTime).toBeLessThan(10); // Less than 10ms per instance
+ expect(duration).toBeLessThan(1000); // Total less than 1 second
+ });
+ });
+
+ describe('Memory Usage', () => {
+ test('should not leak memory with repeated API creation', () => {
+ const initialMemory = process.memoryUsage();
+
+ // Create many API instances
+ for (let i = 0; i < 1000; i++) {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+ // Use the API briefly
+ api.tokenizer('test');
+ }
+
+ // Force garbage collection if available
+ if (global.gc) {
+ global.gc();
+ }
+
+ const finalMemory = process.memoryUsage();
+ const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
+
+ // Memory increase should be reasonable (less than 50MB)
+ expect(memoryIncrease).toBeLessThan(50 * 1024 * 1024);
+ });
+
+ test('should handle large token arrays efficiently', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Create a large input array
+ const largeInput: InputToken[] = [];
+ for (let i = 0; i < 1000; i++) {
+ largeInput.push({ text: `Token number ${i}. ` });
+ }
+
+ const startTime = performance.now();
+ // Just test that it doesn't crash or take too long
+ expect(() => {
+ const promise = api.send(largeInput);
+ expect(promise).toBeDefined();
+ }).not.toThrow();
+ const endTime = performance.now();
+
+ // Should handle large inputs quickly
+ expect(endTime - startTime).toBeLessThan(100);
+ });
+ });
+});
+
+describe('Edge Cases', () => {
+ describe('Input Validation', () => {
+ test('should handle empty string inputs', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const promise = api.send('');
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle whitespace-only inputs', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const whitespaceInputs = [' ', '\n\t', ' \n \t ', '\r\n'];
+
+ for (const input of whitespaceInputs) {
+ const promise = api.send(input);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ }
+ });
+
+ test('should handle very long single words', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const longWord = 'a'.repeat(10000);
+ const promise = api.send(longWord);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle special characters and Unicode', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const specialInputs = [
+ 'Hello 🌍! 🚀 🎉',
+ '测试中文输入',
+ 'Тест на русском',
+ 'العربية اختبار',
+ '🔥💯🚀💪🎯',
+ 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?',
+ 'Emoji string: 😊😂❤️🎉🤔😴🙄'
+ ];
+
+ for (const input of specialInputs) {
+ const promise = api.send(input);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ }
+ });
+
+ test('should handle malformed image data', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const malformedImages = [
+ { img: 'not-a-valid-image' },
+ { img: 'data:image/png;base64,' }, // Empty base64
+ { img: 'data:invalid-format;base64,abc123' },
+ { img: '' } // Empty image
+ ];
+
+ for (const imgToken of malformedImages) {
+ const input: InputToken[] = [
+ { text: 'Describe this:' },
+ imgToken
+ ];
+ const promise = api.send(input);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ }
+ });
+
+ test('should handle empty input arrays', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const emptyInput: InputToken[] = [];
+ const promise = api.send(emptyInput);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle arrays with empty tokens', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const inputsWithEmptyTokens: InputToken[][] = [
+ [{ text: '' }],
+ [{ text: 'Hello' }, { text: '' }],
+ [{ text: '' }, { text: 'World' }],
+ [{ text: 'Hello' }, { text: '' }, { text: 'World' }]
+ ];
+
+ for (const input of inputsWithEmptyTokens) {
+ const promise = api.send(input);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ }
+ });
+ });
+
+ describe('Configuration Edge Cases', () => {
+ test('should handle minimal configuration', () => {
+ const choices: LLMChoice[] = [
+ { claude: '' },
+ { gemini: '' },
+ { chatgpt: '' },
+ { deepseek: '' },
+ { kimi: '' },
+ { grok: '' }
+ ];
+
+ choices.forEach(choice => {
+ expect(() => {
+ const api = models(choice);
+ expect(api).toBeDefined();
+ }).not.toThrow();
+ });
+ });
+
+ test('should handle very long model names', () => {
+ const longModelName = 'model-name-that-is-extremely-long-and-unrealistic-but-should-still-work-without-crashing'.repeat(10);
+ const choice: LLMChoice = { claude: longModelName };
+
+ expect(() => {
+ const api = models(choice);
+ expect(api).toBeDefined();
+ }).not.toThrow();
+ });
+
+ test('should handle OpenAI configuration with minimal required fields', () => {
+ const minimalConfigs = [
+ {
+ openai: {
+ url: 'https://api.openai.com/v1',
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ },
+ {
+ openai: {
+ url: 'https://api.openai.com/v1',
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo',
+ allowBrowser: false
+ }
+ },
+ {
+ openai: {
+ url: 'https://api.openai.com/v1',
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo',
+ allowBrowser: true
+ }
+ }
+ ];
+
+ minimalConfigs.forEach(config => {
+ expect(() => {
+ const api = models(config);
+ expect(api).toBeDefined();
+ }).not.toThrow();
+ });
+ });
+
+ test('should handle malformed URLs gracefully', () => {
+ const malformedUrls = [
+ 'not-a-url',
+ 'http://',
+ 'https://',
+ 'ftp://example.com',
+ 'javascript:alert(1)',
+ 'file:///etc/passwd'
+ ];
+
+ malformedUrls.forEach(url => {
+ const config: LLMChoice = {
+ openai: {
+ url: url,
+ apiKey: 'test-key',
+ model: 'gpt-3.5-turbo'
+ }
+ };
+
+ expect(() => {
+ const api = models(config);
+ expect(api).toBeDefined();
+ }).not.toThrow();
+ });
+ });
+ });
+
+ describe('Concurrent Operations', () => {
+ test('should handle multiple simultaneous API calls', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const promises = [];
+ for (let i = 0; i < 50; i++) {
+ promises.push(api.send(`Test message ${i}`).catch(() => `Error ${i}`));
+ }
+
+ const results = await Promise.allSettled(promises);
+ expect(results.length).toBe(50);
+
+ // All promises should be settled (either fulfilled or rejected)
+ results.forEach(result => {
+ expect(result.status).toBeOneOf(['fulfilled', 'rejected']);
+ });
+ });
+
+ test('should handle multiple simultaneous streams', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ expect(() => {
+ for (let i = 0; i < 10; i++) {
+ const handler = (data: string) => console.log(`Stream ${i}:`, data);
+ api.stream(`Message ${i}`, handler);
+ }
+ }).not.toThrow();
+ });
+
+ test('should handle mixing streaming and non-streaming calls', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Start some streams
+ for (let i = 0; i < 5; i++) {
+ const handler = (data: string) => console.log(`Stream ${i}:`, data);
+ api.stream(`Stream message ${i}`, handler);
+ }
+
+ // Also make some regular calls
+ const promises = [];
+ for (let i = 0; i < 5; i++) {
+ promises.push(api.send(`Regular message ${i}`).catch(() => null));
+ }
+
+ const results = await Promise.allSettled(promises);
+ expect(results.length).toBe(5);
+ });
+ });
+
+ describe('Resource Management', () => {
+ test('should handle rapid model switching', () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const models = ['claude-3-5-sonnet', 'claude-3-haiku', 'claude-3-opus'];
+ const iterations = 100;
+
+ expect(() => {
+ for (let i = 0; i < iterations; i++) {
+ const model = models[i % models.length];
+ api.setModel(model);
+ }
+ }).not.toThrow();
+
+ // Should still be functional
+ expect(() => api.tokenizer('test')).not.toThrow();
+ });
+
+ test('should handle system prompt changes efficiently', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const systemPrompts = [
+ 'You are a helpful assistant.',
+ 'You are a creative writer.',
+ 'You are a technical expert.',
+ 'You are a friendly chatbot.'
+ ];
+
+ const promises = systemPrompts.map(prompt =>
+ api.send('Hello', prompt).catch(() => null)
+ );
+
+ const results = await Promise.allSettled(promises);
+ expect(results.length).toBe(systemPrompts.length);
+ });
+ });
+
+ describe('Extreme Cases', () => {
+ test('should handle extremely large system prompts', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ const largeSystemPrompt = 'You are a helpful assistant. '.repeat(10000);
+ expect(largeSystemPrompt.length).toBeGreaterThan(200000);
+
+ const promise = api.send('Hello', largeSystemPrompt);
+ expect(promise).toBeDefined();
+ expect(typeof promise.then).toBe('function');
+ });
+
+ test('should handle deep nesting of input tokens', async () => {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+
+ // Create a very deep input array
+ const deepInput: InputToken[] = [];
+ for (let i = 0; i < 10000; i++) {
+ deepInput.push({ text: `Item ${i}: ` });
+ }
+
+ const startTime = performance.now();
+ const promise = api.send(deepInput);
+ const endTime = performance.now();
+
+ expect(promise).toBeDefined();
+ expect(endTime - startTime).toBeLessThan(1000); // Should be fast
+ });
+
+ test('should handle rapid creation and destruction', () => {
+ const startTime = performance.now();
+
+ for (let i = 0; i < 1000; i++) {
+ const choice: LLMChoice = { claude: 'claude-3-5-sonnet' };
+ const api = models(choice);
+ // Use it briefly
+ api.tokenizer(`Test ${i}`);
+ // Let it go out of scope
+ }
+
+ const endTime = performance.now();
+ expect(endTime - startTime).toBeLessThan(5000); // Should complete in 5 seconds
+ });
+ });
+}); \ No newline at end of file
diff --git a/packages/ai/tests/setup.ts b/packages/ai/tests/setup.ts
new file mode 100644
index 0000000..a8a7546
--- /dev/null
+++ b/packages/ai/tests/setup.ts
@@ -0,0 +1,180 @@
+/**
+ * Test configuration and setup utilities
+ * This file provides configuration for running tests with different LLM providers
+ */
+
+// Environment variable configuration
+export interface TestConfig {
+ baseUrl: string;
+ apiKey: string;
+ model: string;
+ claudeApiKey?: string;
+ openaiApiKey?: string;
+ geminiApiKey?: string;
+}
+
+// Get test configuration from environment variables
+export function getTestConfig(): TestConfig {
+ return {
+ baseUrl: Bun.env.ZAI_BASE_URL || 'https://api.openai.com/v1',
+ apiKey: Bun.env.ZAI_API_KEY || 'test-api-key',
+ model: Bun.env.TEST_MODEL || 'glm-4.6',
+ claudeApiKey: Bun.env.CLAUDE_API_KEY,
+ openaiApiKey: Bun.env.OPENAI_API_KEY,
+ geminiApiKey: Bun.env.GEMINI_API_KEY
+ };
+}
+
+// Check if we have real API credentials for integration testing
+export function hasIntegrationCredentials(): {
+ claude: boolean;
+ openai: boolean;
+ gemini: boolean;
+ any: boolean;
+} {
+ const config = getTestConfig();
+
+ return {
+ claude: !!config.claudeApiKey,
+ openai: !!config.openaiApiKey,
+ gemini: !!config.geminiApiKey,
+ any: !!(config.claudeApiKey || config.openaiApiKey || config.geminiApiKey)
+ };
+}
+
+// Mock API responses for testing without real credentials
+export const mockResponses = {
+ claude: {
+ success: 'SUCCESS',
+ counting: '1\n2\n3\n4\n5',
+ error: 'Mock Claude error'
+ },
+ openai: {
+ success: 'SUCCESS',
+ error: 'Mock OpenAI error'
+ },
+ gemini: {
+ success: 'SUCCESS',
+ error: 'Mock Gemini error'
+ }
+};
+
+// Test data generators
+export const testData = {
+ simpleText: () => 'Hello, this is a simple test message.',
+ longText: () => 'This is a longer test message. '.repeat(100),
+ unicodeText: () => 'Hello 🌍! 测试中文! Тест на русском! العربية!',
+ specialChars: () => '!@#$%^&*()_+-=[]{}|;:,.<>?/~`',
+ emptyString: () => '',
+ whitespaceOnly: () => ' \n\t ',
+
+ // Input token generators
+ textTokens: (count: number = 3) =>
+ Array.from({ length: count }, (_, i) => ({ text: `Token ${i + 1}: ` })),
+
+ imageTokens: (count: number = 1) =>
+ Array.from({ length: count }, () => ({
+ img: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='
+ })),
+
+ mixedTokens: () => [
+ { text: 'Describe this image: ' },
+ { img: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' }
+ ]
+};
+
+// Performance test utilities
+export const performance = {
+ measureTokenization: (api: any, text: string, iterations: number = 1000) => {
+ const start = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ api.tokenizer(text + i);
+ }
+ const end = performance.now();
+ return {
+ totalTime: end - start,
+ averageTime: (end - start) / iterations,
+ iterations
+ };
+ },
+
+ measureApiCreation: (choice: any, iterations: number = 100) => {
+ const start = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ const models = require('../index').default;
+ models(choice);
+ }
+ const end = performance.now();
+ return {
+ totalTime: end - start,
+ averageTime: (end - start) / iterations,
+ iterations
+ };
+ }
+};
+
+// Error simulation utilities
+export const errorSimulator = {
+ // Simulate network errors
+ networkError: () => new Error('Network connection failed'),
+
+ // Simulate API key errors
+ apiKeyError: () => new Error('Invalid API key'),
+
+ // Simulate timeout errors
+ timeoutError: () => new Error('Request timeout'),
+
+ // Simulate rate limit errors
+ rateLimitError: () => new Error('Rate limit exceeded'),
+
+ // Simulate invalid response errors
+ invalidResponseError: () => new Error('Invalid API response format')
+};
+
+// Logging utilities for tests
+export const testLogger = {
+ info: (message: string, ...args: any[]) => {
+ console.log(`[TEST-INFO] ${message}`, ...args);
+ },
+
+ warn: (message: string, ...args: any[]) => {
+ console.warn(`[TEST-WARN] ${message}`, ...args);
+ },
+
+ error: (message: string, ...args: any[]) => {
+ console.error(`[TEST-ERROR] ${message}`, ...args);
+ },
+
+ debug: (message: string, ...args: any[]) => {
+ if (Bun.env.DEBUG_TESTS === 'true') {
+ console.debug(`[TEST-DEBUG] ${message}`, ...args);
+ }
+ }
+};
+
+// Cleanup utilities
+export const cleanup = {
+ // Clear environment variables after tests
+ clearEnv: (...vars: string[]) => {
+ vars.forEach(varName => {
+ delete Bun.env[varName];
+ });
+ },
+
+ // Reset global state if needed
+ resetGlobal: () => {
+ // Add any global state cleanup here
+ }
+};
+
+// Export a default test setup object
+export default {
+ getTestConfig,
+ hasIntegrationCredentials,
+ mockResponses,
+ testData,
+ performance,
+ errorSimulator,
+ testLogger,
+ cleanup
+}; \ No newline at end of file
diff --git a/packages/ai/tests/vllm.ts b/packages/ai/tests/vllm.ts
new file mode 100644
index 0000000..1c57586
--- /dev/null
+++ b/packages/ai/tests/vllm.ts
@@ -0,0 +1,23 @@
+import models, { type AIModelAPI, type LLMChoice } from "../index";
+
+const model = "Qwen/Qwen3-VL-8B-Instruct-FP8";
+
+const api = models({
+ openai: { url: "http://localhost:3000/v1", apiKey: "EMPTY", model },
+});
+
+// const r1 = await api.send("你觉得这样下去可以吗?你不觉得可耻吗");
+// const r1 = await api.send("Why is VLLM so fucking annoying to run");
+const r1 = await api.send(
+ "Phrenology is true and I'm tired of pretending it isn't",
+);
+console.log({ r1 });
+// const r2 = await api.send([
+// {
+// // img: "https://ofasys-multimodal-wlcb-3-toshanghai.oss-accelerate.aliyuncs.com/wpf272043/keepme/image/receipt.png",
+// img: "https://www.khaosod.co.th/wpapp/uploads/2025/10/co-pay-.jpg",
+// },
+// { text: "Read all the text in the image" },
+// // { text: "Read all the text in the image and describe the scene" },
+// ]);
+// console.log({ r2 });
diff --git a/packages/ai/tsconfig.json b/packages/ai/tsconfig.json
new file mode 100644
index 0000000..238655f
--- /dev/null
+++ b/packages/ai/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ // Enable latest features
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/packages/db/.gitignore b/packages/db/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/packages/db/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/db/CLAUDE.md b/packages/db/CLAUDE.md
new file mode 100644
index 0000000..1ee6890
--- /dev/null
+++ b/packages/db/CLAUDE.md
@@ -0,0 +1,106 @@
+
+Default to using Bun instead of Node.js.
+
+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
+- Use `bun test` instead of `jest` or `vitest`
+- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
+- Bun automatically loads .env, so don't use dotenv.
+
+## APIs
+
+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
+- `Bun.redis` for Redis. Don't use `ioredis`.
+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
+- `WebSocket` is built-in. Don't use `ws`.
+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
+- Bun.$`ls` instead of execa.
+
+## Testing
+
+Use `bun test` to run tests.
+
+```ts#index.test.ts
+import { test, expect } from "bun:test";
+
+test("hello world", () => {
+ expect(1).toBe(1);
+});
+```
+
+## Frontend
+
+Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
+
+Server:
+
+```ts#index.ts
+import index from "./index.html"
+
+Bun.serve({
+ routes: {
+ "/": index,
+ "/api/users/:id": {
+ GET: (req) => {
+ return new Response(JSON.stringify({ id: req.params.id }));
+ },
+ },
+ },
+ // optional websocket support
+ websocket: {
+ open: (ws) => {
+ ws.send("Hello, world!");
+ },
+ message: (ws, message) => {
+ ws.send(message);
+ },
+ close: (ws) => {
+ // handle close
+ }
+ },
+ development: {
+ hmr: true,
+ console: true,
+ }
+})
+```
+
+HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
+
+```html#index.html
+<html>
+ <body>
+ <h1>Hello, world!</h1>
+ <script type="module" src="./frontend.tsx"></script>
+ </body>
+</html>
+```
+
+With the following `frontend.tsx`:
+
+```tsx#frontend.tsx
+import React from "react";
+
+// import .css files directly and it works
+import './index.css';
+
+import { createRoot } from "react-dom/client";
+
+const root = createRoot(document.body);
+
+export default function Frontend() {
+ return <h1>Hello, world!</h1>;
+}
+
+root.render(<Frontend />);
+```
+
+Then, run index.ts
+
+```sh
+bun --hot ./index.ts
+```
+
+For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
diff --git a/packages/db/README.md b/packages/db/README.md
new file mode 100644
index 0000000..94cf634
--- /dev/null
+++ b/packages/db/README.md
@@ -0,0 +1,15 @@
+# @sortug/sorlang-db
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
diff --git a/packages/db/bun.lock b/packages/db/bun.lock
new file mode 100644
index 0000000..cedc049
--- /dev/null
+++ b/packages/db/bun.lock
@@ -0,0 +1,25 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "@sortug/sorlang-db",
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
+
+ "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
+
+ "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+ }
+}
diff --git a/packages/db/index.ts b/packages/db/index.ts
new file mode 100644
index 0000000..ceecd52
--- /dev/null
+++ b/packages/db/index.ts
@@ -0,0 +1,3 @@
+import DB from "./src";
+
+export default DB;
diff --git a/packages/db/package.json b/packages/db/package.json
new file mode 100644
index 0000000..a121b42
--- /dev/null
+++ b/packages/db/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@sortug/sorlang-db",
+ "module": "index.ts",
+ "type": "module",
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ }
+}
diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts
new file mode 100644
index 0000000..6a89ee2
--- /dev/null
+++ b/packages/db/src/index.ts
@@ -0,0 +1,253 @@
+import { Database } from "bun:sqlite";
+import type { FullWordDataDB } from "./types";
+import { SRSQueries } from "./srs";
+
+export class Queries {
+ db: Database;
+ srs: SRSQueries;
+ constructor() {
+ const db = new Database("/home/y/code/bun/sorlang/bulkdata/unified.db");
+ db.exec("PRAGMA journal_mode = WAL"); // Enable Write-Ahead Logging for better performance
+ db.exec("PRAGMA foreign_keys = ON");
+ db.exec("PRAGMA cache_size = -8000"); // Increase cache size to 8MB
+ db.exec("PRAGMA mmap_size = 30000000000");
+ db.exec("PRAGMA temp_store = MEMORY"); // Store temp tables in memory
+ db.exec("PRAGMA synchronous = NORMAL"); // Slightly less safe but faster
+
+ this.db = db;
+ this.srs = new SRSQueries(db);
+ }
+ fetchExpressionById(id: number) {
+ const query = this.db.query(
+ `
+ SELECT * FROM expressions WHERE id = ?
+ `,
+ );
+ return query.get(id) as any;
+ }
+ fetchWordsByToneAndSyls1(tones: Array<string | null>) {
+ const toneString = tones
+ .reduce((acc: string, item) => {
+ if (!item) return `${acc},%`;
+ else return `${acc},${item}`;
+ }, "")
+ .slice(1);
+ const query = this.db.query(
+ `
+ WITH word_tone_sequences AS (
+ SELECT
+ wp.ipa,
+ GROUP_CONCAT(sy.text ORDER BY sw.idx) as syl_seq,
+ GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+ COUNT(sw.syl_id) as syllable_count
+ FROM word_phonetics wp
+ JOIN syllables_words sw ON wp.id = sw.word_id
+ JOIN syllables sy ON sw.syl_id = sy.id
+ JOIN tones t ON sy.tone = t.id
+ GROUP BY wp.ipa
+ )
+ SELECT *
+ FROM word_tone_sequences
+ WHERE tone_sequence LIKE ?
+ AND syllable_count = ?
+ `,
+ );
+ return query.all(toneString, tones.length) as any[];
+ }
+ fetchWordsByToneAndSylsO(tones: Array<string | null>) {
+ const toneString = tones
+ .reduce((acc: string, item) => {
+ if (!item) return `${acc},%`;
+ else return `${acc},${item}`;
+ }, "")
+ .slice(1);
+ const query = this.db.query(
+ `
+ WITH word_tone_sequences AS (
+ SELECT
+ w.id as word_id,
+ w.spelling,
+ wp.ipa,
+ w.frequency,
+ GROUP_CONCAT(sy.text ORDER BY sw.idx) as syl_seq,
+ GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+ COUNT(sw.syl_id) as syllable_count
+ FROM expressions w
+ JOIN word_phonetics wp ON w.id = wp.word_id
+ JOIN syllables_words sw ON wp.id = sw.word_id
+ JOIN syllables sy ON sw.syl_id = sy.id
+ JOIN tones t ON sy.tone = t.id
+ GROUP BY w.id, w.spelling, w.lang, w.frequency
+ )
+ SELECT word_id,
+ spelling,
+ ipa,
+ frequency,
+ syl_seq,
+ tone_sequence,
+ syllable_count
+ FROM word_tone_sequences
+ WHERE tone_sequence LIKE ?
+ AND syllable_count = ?
+ ORDER BY frequency ASC NULLS LAST;
+ `,
+ );
+ // TODO combine with this old query to get both phonetic and semantic data
+ // Thing is the tables have changed, used to be that the senses table had JSONB columns for senses, forms and related but those are now proper tables now with one-to-many relation to the senses table
+
+ return query.all(toneString, tones.length) as any[];
+ }
+ fetchWordsByToneAndSyls(tones: Array<string | null>): FullWordDataDB[] {
+ const toneString = tones
+ .reduce((acc: string, item) => {
+ if (!item) return `${acc},%`;
+ else return `${acc},${item}`;
+ }, "")
+ .slice(1);
+ const query = this.db.query(
+ `
+ WITH word_tone_sequences AS (
+ SELECT
+ w.id as word_id,
+ w.spelling,
+ wp.ipa,
+ w.frequency,
+ GROUP_CONCAT(sy.text ORDER BY sw.idx) as syl_seq,
+ GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+ COUNT(sw.syl_id) as syllable_count,
+ (SELECT
+ json_group_array(json_object(
+ 'id', s.id,
+ 'pos', s.pos,
+ 'etymology', s.etymology,
+ 'confidence', s.confidence,
+ 'glosses', (
+ SELECT json_group_array(ss.gloss)
+ FROM subsenses ss
+ WHERE ss.sid = s.id
+ ),
+ 'examples', (
+ SELECT json_group_array(json_object(
+ 'example', ex.example,
+ 'ref', ex.ref
+ ))
+ FROM examples ex
+ WHERE ex.sid = s.id
+ ),
+ 'derivation', (
+ SELECT json_group_array(json_object(
+ 'type', d.type,
+ 'text', d.text,
+ 'tags', d.tags
+ ))
+ FROM derivation d
+ WHERE d.sid = s.id
+ ),
+ 'categories', (
+ SELECT json_group_array(wc.category)
+ FROM word_categories wc
+ WHERE wc.word_id = s.id
+ )
+ ))
+ FROM senses s
+ WHERE s.parent_id = w.id
+ ) as senses_array
+ FROM expressions w
+ JOIN word_phonetics wp ON w.id = wp.word_id
+ JOIN syllables_words sw ON wp.id = sw.word_id
+ JOIN syllables sy ON sw.syl_id = sy.id
+ JOIN tones t ON sy.tone = t.id
+ GROUP BY w.id, w.spelling, w.lang, w.frequency
+ )
+ SELECT *
+ FROM word_tone_sequences
+ WHERE tone_sequence LIKE ?
+ AND syllable_count = ?
+ ORDER BY frequency ASC NULLS LAST;
+ `,
+ );
+ // TODO combine with this old query to get both phonetic and semantic data
+ // Thing is the tables have changed, used to be that the senses table had JSONB columns for senses, forms and related but those are now proper tables now with one-to-many relation to the senses table
+
+ return query.all(toneString, tones.length) as any[];
+ }
+ // TODO combine with this old query to get both phonetic and semantic data
+ // Thing is the tables have changed, used to be that the senses table had JSONB columns for senses, forms and related but those are now proper tables now with one-to-many relation to the senses table
+
+ fetchSenses(spelling: string, lang: string) {
+ const query = this.db.query(`
+ WITH sense_data AS (
+ SELECT
+ s.*,
+ GROUP_CONCAT(DISTINCT ss.id || ':' || ss.gloss, '|') as subsenses_data,
+ GROUP_CONCAT(DISTINCT ex.id || ':' || ex.example || ':' || COALESCE(ex.ref, ''), '|') as examples_data,
+ GROUP_CONCAT(DISTINCT d.id || ':' || d.type || ':' || d.text, '|') as derivation_data,
+ GROUP_CONCAT(DISTINCT wc.category, '|') as categories_data
+ FROM senses s
+ LEFT JOIN subsenses ss ON ss.sid = s.id
+ LEFT JOIN examples ex ON ex.sid = s.id
+ LEFT JOIN derivation d ON d.sid = s.id
+ LEFT JOIN word_categories wc ON wc.word_id = s.id
+ GROUP BY s.id
+ )
+ SELECT e.*,
+ (SELECT
+ json_group_array(json_object(
+ 'id', sd.id,
+ 'pos', sd.pos,
+ 'etymology', sd.etymology,
+ 'confidence', sd.confidence,
+ 'subsenses_data', sd.subsenses_data,
+ 'examples_data', sd.examples_data,
+ 'derivation_data', sd.derivation_data,
+ 'categories_data', sd.categories_data
+ ))
+ FROM sense_data sd
+ WHERE sd.parent_id = e.id
+ ) as senses_array
+ FROM expressions e
+ WHERE e.spelling = ? AND e.lang = ?
+ ORDER BY e.frequency DESC`);
+ return query.all(spelling, lang);
+ }
+}
+export default Queries;
+// `
+// WITH word_tone_sequences AS (
+// SELECT
+// w.id as word_id,
+// w.spelling,
+// wp.ipa,
+// w.frequency,
+// s.etymology,
+// s.pos,
+// GROUP_CONCAT(s.text ORDER BY sw.idx) as syl_seq,
+// GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+// // GROUP_CONCAT(t.name ORDER BY s.id) as senses,
+// // GROUP_CONCAT(ex.example ORDER BY s.id) as examples,
+// // GROUP_CONCAT(cat.category) as tags,
+// COUNT(sw.syl_id) as syllable_count
+// FROM expressions w
+// JOIN word_phonetics wp ON w.id = wp.word_id
+// JOIN syllables_words sw ON wp.id = sw.word_id
+// JOIN syllables s ON sw.syl_id = s.id
+// JOIN tones t ON s.tone = t.id
+// JOIN senses s ON s.parent_id = w.id
+// JOIN word_categories cat ON s.id = cat.word_id
+// JOIN subsenses ss ON ss.sid = s.id
+// JOIN examples ex ON ex.sid = s.id
+// GROUP BY w.id, w.spelling, w.lang, w.frequency
+// )
+// SELECT
+// word_id,
+// spelling,
+// ipa,
+// frequency,
+// syl_seq,
+// tone_sequence,
+// syllable_count
+// FROM word_tone_sequences
+// WHERE tone_sequence LIKE ?
+// AND syllable_count = ?
+// ORDER BY frequency ASC NULLS LAST;
+// `
diff --git a/packages/db/src/migration.ts b/packages/db/src/migration.ts
new file mode 100644
index 0000000..c4150f8
--- /dev/null
+++ b/packages/db/src/migration.ts
@@ -0,0 +1,430 @@
+import { Database } from "bun:sqlite";
+
+const PROSODY_DB = "/home/y/code/bun/ssr/claudesorlang/bulkdata/prosody.db";
+const THAIPHON_DB = "/home/y/code/bun/ssr/claudesorlang/bulkdata/thaiphon.db";
+const UNIFIED_DB = "/home/y/code/bun/ssr/claudesorlang/bulkdata/unified.db";
+
+async function migrateSenses() {
+ console.log("Starting database migration to unified.db...\n");
+ const expressionIdMap: Map<number, number> = new Map();
+ const senseIdMap: Map<number, number> = new Map();
+ const wordExpressionMap: Map<number, number> = new Map();
+
+ // Open all databases
+ const prosodyDb = new Database(PROSODY_DB, { readonly: true });
+ const thaiphonDb = new Database(THAIPHON_DB, { readonly: true });
+ const unifiedDb = new Database(UNIFIED_DB);
+
+ // Enable foreign keys and WAL mode for unified database
+ unifiedDb.run("PRAGMA foreign_keys = ON");
+ unifiedDb.run("PRAGMA journal_mode = WAL");
+
+ try {
+ // Start transaction for atomic migration
+ unifiedDb.run("BEGIN TRANSACTION");
+
+ console.log("📦 Migrating from prosody.db...");
+
+ // 1. Migrate languages (combining both sources, preferring prosody.db)
+ console.log(" → Migrating categories...");
+ // categories
+ const prosodyCats = prosodyDb
+ .query("SELECT * FROM categories")
+ .all() as any[];
+ const insertCat = unifiedDb.prepare(
+ `INSERT INTO categories (name) VALUES (?)
+ ON CONFLICT(name) DO NOTHING
+ RETURNING rowid
+ `,
+ );
+ for (const cat of prosodyCats) {
+ const cr = insertCat.run(cat.name.toLowerCase().trim());
+ // console.log({ cr });
+ if (cr.changes !== 1) console.log({ cr });
+ }
+ console.log(` ✓ Migrated ${prosodyCats.length} categories`);
+
+ console.log(" → Migrating languages...");
+ const prosodyLangs = prosodyDb
+ .query("SELECT * FROM languages")
+ .all() as any[];
+
+ // Add thaiphon languages first
+ // Override with prosody languages (they have more complete data)
+ for (const lang of prosodyLangs) {
+ // Insert merged languages
+ const insertLang = unifiedDb.prepare(
+ "INSERT OR IGNORE INTO languages (code, name, native_name, iso6392) VALUES (?, ?, ?, ?)",
+ );
+
+ insertLang.run(lang.code, lang.name, lang.native_name, null);
+ }
+ console.log(` ✓ Migrated ${prosodyLangs.length} languages`);
+
+ // 2. Migrate expressions from prosody.db
+ console.log(" → Migrating expressions...");
+ const expressions = prosodyDb
+ .query("SELECT * FROM expressions")
+ .all() as any[];
+ const insertExpression = unifiedDb.prepare(
+ "INSERT INTO expressions (spelling, lang, frequency, type, notes) VALUES (?, ?, ?, ?, ?)",
+ );
+
+ for (const expr of expressions) {
+ const ei = insertExpression.run(
+ expr.spelling,
+ expr.lang,
+ expr.frequency,
+ expr.type,
+ expr.notes,
+ );
+ if (ei.changes > 0)
+ expressionIdMap.set(expr.id, Number(ei.lastInsertRowid));
+ }
+
+ const insertExample = unifiedDb.prepare(
+ "INSERT INTO examples(sid, example, ref) VALUES (?, ?, ?)",
+ );
+ const insertSubsense = unifiedDb.prepare(
+ "INSERT INTO subsenses (sid, gloss) VALUES (?, ?)",
+ );
+ const insertDerivation = unifiedDb.prepare(
+ "INSERT INTO derivation (sid, type, text) VALUES (?, ?, ?)",
+ );
+ const insertCatw = unifiedDb.prepare(
+ "INSERT OR IGNORE INTO word_categories(category, word_id) VALUES (?, ?)",
+ );
+ const insertWp = unifiedDb.prepare(
+ "INSERT INTO word_phonetics(word_id, sense_id, ipa, ipa_sequence, syllable_count, syllable_sequence, stressed, tone_sequence, tag, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ );
+ console.log(" → Migrating senses...");
+ const senses = prosodyDb.query("SELECT * FROM senses").all() as any[];
+ const insertSense = unifiedDb.prepare(
+ "INSERT INTO senses (parent_id, spelling, pos, etymology, confidence) VALUES (?, ?, ?, ?, ?)",
+ );
+
+ for (const sense of senses) {
+ const parentId = expressionIdMap.get(sense.parent_id);
+ const senseRow = insertSense.run(
+ parentId,
+ sense.spelling,
+ sense.pos,
+ sense.etymology,
+ sense.confidence,
+ );
+ const senseId = senseRow.lastInsertRowid;
+ senseIdMap.set(sense.id, Number(senseId));
+ const deriv = (
+ name: string,
+ type: Array<{ word: string }> | undefined,
+ ) => {
+ if (type)
+ for (const t of type) insertDerivation.run(senseId, name, t.word);
+ };
+ const subsenses = JSON.parse(sense.senses);
+ for (const ss of subsenses) {
+ const {
+ examples,
+ glosses,
+ tags,
+ synonyms,
+ antonyms,
+ hypernyms,
+ meronyms,
+ hyponyms,
+ holonyms,
+ } = ss;
+ if (examples)
+ for (const ex of examples) {
+ const ref = ex.ref || null;
+
+ if (ex.text) insertExample.run(senseId, ex.text, ref);
+ // else console.log("bad example", ex);
+ }
+ if (glosses)
+ for (const gloss of glosses) insertSubsense.run(senseId, gloss);
+ if (tags)
+ for (const tag of tags) {
+ const lw = tag.toLowerCase().trim();
+ insertCat.run(lw);
+ insertCatw.run(lw, senseId);
+ }
+ if (synonyms) deriv("synonym", synonyms);
+ if (antonyms) deriv("antonym", antonyms);
+ if (hypernyms) deriv("hypernym", hypernyms);
+ if (meronyms) deriv("meronym", meronyms);
+ if (hyponyms) deriv("hyponym", hyponyms);
+ if (holonyms) deriv("holonym", holonyms);
+ }
+ const prosody = JSON.parse(sense.prosody);
+ const stressed = prosody?.stressedSyllable || null;
+ if (!sense.ipa) {
+ console.log("no ipa", sense);
+ continue;
+ }
+ const ipa = JSON.parse(sense.ipa);
+ for (const ip of ipa) {
+ insertWp.run(
+ parentId,
+ senseId,
+ ip.ipa,
+ ip.ipa,
+ 0,
+ "",
+ stressed,
+ "",
+ ip.tag,
+ null,
+ );
+ }
+ }
+ console.log(` ✓ Migrated ${senses.length} senses`);
+
+ console.log("\n📦 Migrating from thaiphon.db...");
+
+ // 6. Migrate phonetic components from thaiphon.db
+ console.log(" → Migrating phonetic components...");
+
+ // Tones
+ const tones = thaiphonDb.query("SELECT * FROM tones").all() as any;
+ const insertTone = unifiedDb.prepare(
+ "INSERT INTO tones (id, ipa, lang, name, nums) VALUES (?, ?, ?, ?, ?)",
+ );
+ for (const tone of tones) {
+ insertTone.run(tone.id, tone.ipa, tone.lang, tone.name, tone.nums);
+ }
+ console.log(` ✓ Migrated ${tones.length} tones`);
+
+ // Onsets
+ const onsets = thaiphonDb.query("SELECT * FROM onsets").all() as any;
+ const insertOnset = unifiedDb.prepare(
+ "INSERT INTO onsets (id, ipa, text, lang) VALUES (?, ?, ?, ?)",
+ );
+ for (const onset of onsets) {
+ insertOnset.run(onset.id, onset.ipa, onset.text, onset.lang);
+ }
+ console.log(` ✓ Migrated ${onsets.length} onsets`);
+
+ // Medials
+ const medials = thaiphonDb.query("SELECT * FROM medials").all() as any;
+ const insertMedial = unifiedDb.prepare(
+ "INSERT INTO medials (id, ipa, text, lang) VALUES (?, ?, ?, ?)",
+ );
+ for (const medial of medials) {
+ insertMedial.run(medial.id, medial.ipa, medial.text, medial.lang) as any;
+ }
+ console.log(` ✓ Migrated ${medials.length} medials`);
+
+ // Nucleus
+ const nucleus = thaiphonDb.query("SELECT * FROM nucleus").all() as any;
+ const insertNucleus = unifiedDb.prepare(
+ "INSERT INTO nucleus (id, ipa, text, lang) VALUES (?, ?, ?, ?)",
+ );
+ for (const nucl of nucleus) {
+ insertNucleus.run(nucl.id, nucl.ipa, nucl.text, nucl.lang);
+ }
+ console.log(` ✓ Migrated ${nucleus.length} nucleus`);
+
+ // Codas
+ const codas = thaiphonDb.query("SELECT * FROM codas").all() as any;
+ const insertCoda = unifiedDb.prepare(
+ "INSERT INTO codas (id, ipa, text, lang) VALUES (?, ?, ?, ?)",
+ );
+ for (const coda of codas) {
+ insertCoda.run(coda.id, coda.ipa, coda.text, coda.lang) as any;
+ }
+ console.log(` ✓ Migrated ${codas.length} codas`);
+
+ // Rhymes
+ const rhymes = thaiphonDb.query("SELECT * FROM rhymes").all() as any;
+ const insertRhyme = unifiedDb.prepare(
+ "INSERT INTO rhymes (id, ipa, text, lang) VALUES (?, ?, ?, ?)",
+ );
+ for (const rhyme of rhymes) {
+ insertRhyme.run(rhyme.id, rhyme.ipa, rhyme.text, rhyme.lang);
+ }
+ console.log(` ✓ Migrated ${rhymes.length} rhymes`);
+
+ // 7. Migrate syllables from thaiphon.db
+ console.log(" → Migrating syllables...");
+ const syllables = thaiphonDb.query("SELECT * FROM syllables").all() as any;
+ const insertSyllable = unifiedDb.prepare(
+ "INSERT INTO syllables (id, lang, ipa, long, text, onset, medial, nucleus, coda, rhyme, tone, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ );
+ for (const syl of syllables) {
+ insertSyllable.run(
+ syl.id,
+ syl.lang,
+ syl.ipa,
+ syl.long,
+ syl.text,
+ syl.onset,
+ syl.medial,
+ syl.nucleus,
+ syl.coda,
+ syl.rhyme,
+ syl.tone,
+ syl.notes,
+ );
+ }
+ console.log(` ✓ Migrated ${syllables.length} syllables`);
+
+ // 8. Migrate words from thaiphon.db (as expressions if not exists)
+ console.log(" → Migrating words as expressions...");
+ const words = thaiphonDb.query("SELECT * FROM words").all() as any[];
+ const insertWordAsExpr = unifiedDb.query(
+ `INSERT INTO expressions (spelling, lang, frequency, type, notes) VALUES (?, ?, ?, ?, ?)
+ ON CONFLICT(spelling, lang) DO UPDATE SET spelling = excluded.spelling
+ RETURNING rowid`,
+ );
+
+ // Also keep track for syllables_words mapping
+ let prev: any = null;
+ for (const word of words) {
+ const result = insertWordAsExpr.get(
+ word.spelling,
+ word.lang,
+ word.frequency,
+ "word",
+ word.notes,
+ ) as { id: number };
+ // 41085
+ console.log({ result });
+ // if (prev === result.id) {
+ // console.log(word, result);
+ // throw new Error("fucked up again");
+ // }
+ // prev = result.id;
+ wordExpressionMap.set(word.id, result.id);
+ }
+ console.log(` ✓ Processed ${words.length} words`);
+
+ console.log(" → Migrating word phonetics...");
+ const wordPhonetics = thaiphonDb
+ .query("SELECT * FROM word_phonetics")
+ .all() as any[];
+ const insertWordPhonetic = unifiedDb.prepare(
+ "INSERT INTO word_phonetics (word_id, sense_id, ipa, syllable_count, syllable_sequence, tone_sequence, ipa_sequence, tag, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ );
+ // TODO find the sense id later I guess
+ const wpMap: Map<number, number> = new Map();
+ for (const wp of wordPhonetics) {
+ const newWordId = wordExpressionMap.get(wp.word_id);
+ if (newWordId) {
+ const wr = insertWordPhonetic.run(
+ newWordId,
+ null,
+ wp.ipa,
+ wp.syllable_count,
+ wp.syllable_sequence,
+ wp.tone_sequence,
+ wp.ipa_sequence,
+ wp.tag,
+ wp.notes,
+ );
+ if (wr.changes !== 0) wpMap.set(wp.word_id, Number(wr.lastInsertRowid));
+ }
+ }
+
+ // 9. Migrate syllables_words junction
+ console.log(" → Migrating syllables_words...");
+ const sylWords = thaiphonDb
+ .query("SELECT * FROM syllables_words")
+ .all() as any;
+ const insertSylWord = unifiedDb.prepare(
+ "INSERT OR IGNORE INTO syllables_words (syl_id, word_id, idx, stressed) VALUES (?, ?, ?, ?)",
+ );
+ for (const sw of sylWords) {
+ const newWordId = wpMap.get(sw.word_id);
+ if (newWordId)
+ insertSylWord.run(sw.syl_id, newWordId, sw.idx, sw.stressed);
+ }
+ console.log(
+ ` ✓ Migrated ${sylWords.length} syllable-word relationships`,
+ );
+
+ unifiedDb.run("COMMIT");
+
+ console.log("\n✅ Migration completed successfully!");
+ console.log(" Unified database is ready at:", UNIFIED_DB);
+ } catch (error) {
+ console.error("\n❌ Migration failed:", error);
+ unifiedDb.run("ROLLBACK");
+ throw error;
+ } finally {
+ // Close all databases
+ prosodyDb.close();
+ // thaiphonDb.close();
+ unifiedDb.close();
+ }
+}
+
+// Run the migration
+migrateSenses();
+// migrateTones();
+// test();
+//
+
+async function test() {
+ console.log("Starting database migration to unified.db...\n");
+ // Open all databases
+ const prosodyDb = new Database(PROSODY_DB, { readonly: true });
+ const thaiphonDb = new Database(THAIPHON_DB, { readonly: true });
+ const unifiedDb = new Database(UNIFIED_DB, { readonly: true });
+
+ // Enable foreign keys and WAL mode for unified database
+ unifiedDb.run("PRAGMA foreign_keys = ON");
+ unifiedDb.run("PRAGMA journal_mode = WAL");
+ const oldE = prosodyDb.query("SELECT COUNT(*) FROM expressions").get();
+ const oldS = prosodyDb.query("SELECT COUNT(*) FROM senses").get();
+ const oldW = thaiphonDb.query("SELECT COUNT(*) FROM words").get();
+ const oldWp = thaiphonDb.query("SELECT COUNT(*) FROM word_phonetics").get();
+ const oldSw = thaiphonDb.query("SELECT COUNT(*) FROM syllables_words").get();
+ const oldSy = thaiphonDb.query("SELECT COUNT(*) FROM syllables").get();
+ const oldTo = thaiphonDb.query("SELECT COUNT(*) FROM tones").get();
+ const oldCod = thaiphonDb.query("SELECT COUNT(*) FROM codas").get();
+ const oldNuc = thaiphonDb.query("SELECT COUNT(*) FROM nucleus").get();
+ const oldOns = thaiphonDb.query("SELECT COUNT(*) FROM onsets").get();
+ const oldRhy = thaiphonDb.query("SELECT COUNT(*) FROM rhymes").get();
+ const oldWrh = thaiphonDb.query("SELECT COUNT(*) FROM word_rhymes").get();
+ const oldWry = thaiphonDb.query("SELECT COUNT(*) FROM words_wrhymes").get();
+ const newSw = unifiedDb.query("SELECT COUNT(*) FROM syllables_words").get();
+ const newSy = unifiedDb.query("SELECT COUNT(*) FROM syllables").get();
+ const newTo = unifiedDb.query("SELECT COUNT(*) FROM tones").get();
+ const newCod = unifiedDb.query("SELECT COUNT(*) FROM codas").get();
+ const newNuc = unifiedDb.query("SELECT COUNT(*) FROM nucleus").get();
+ const newOns = unifiedDb.query("SELECT COUNT(*) FROM onsets").get();
+ const newRhy = unifiedDb.query("SELECT COUNT(*) FROM rhymes").get();
+ const newWrh = unifiedDb.query("SELECT COUNT(*) FROM word_rhymes").get();
+ const newWry = unifiedDb.query("SELECT COUNT(*) FROM words_wrhymes").get();
+ const newE = unifiedDb.query("SELECT COUNT(*) FROM expressions").get();
+ const newS = unifiedDb.query("SELECT COUNT(*) FROM senses").get();
+ const newWp = unifiedDb.query("SELECT COUNT(*) FROM word_phonetics").get();
+ console.log({
+ oldE,
+ oldS,
+ oldW,
+ oldWp,
+ newE,
+ newS,
+ newWp,
+ oldSw,
+ oldSy,
+ oldTo,
+ oldCod,
+ oldNuc,
+ oldOns,
+ oldRhy,
+ oldWrh,
+ oldWry,
+ newSw,
+ newSy,
+ newTo,
+ newCod,
+ newNuc,
+ newOns,
+ newRhy,
+ newWrh,
+ newWry,
+ });
+}
diff --git a/packages/db/src/phonetics.ts b/packages/db/src/phonetics.ts
new file mode 100644
index 0000000..cf62434
--- /dev/null
+++ b/packages/db/src/phonetics.ts
@@ -0,0 +1,523 @@
+import { Database } from 'bun:sqlite';
+
+export interface Syllable {
+ id: number;
+ lang: string;
+ ipa: string;
+ long: number;
+ text: string;
+ onset: number;
+ medial: number;
+ nucleus: number;
+ coda: number;
+ rhyme: number;
+ tone: number;
+ notes?: string;
+}
+
+export interface Tone {
+ id: number;
+ ipa: string;
+ lang: string;
+ name: string;
+ nums: number;
+}
+
+export interface Onset {
+ id: number;
+ ipa: string;
+ text: string;
+ lang: string;
+}
+
+export interface Medial {
+ id: number;
+ ipa: string;
+ text: string;
+ lang: string;
+}
+
+export interface Nucleus {
+ id: number;
+ ipa: string;
+ text: string;
+ lang: string;
+}
+
+export interface Coda {
+ id: number;
+ ipa: string;
+ text: string;
+ lang: string;
+}
+
+export interface Rhyme {
+ id: number;
+ ipa: string;
+ text: string;
+ lang: string;
+}
+
+export interface WordPhonetics {
+ id?: number;
+ word_id: number;
+ ipa: string;
+ syllable_count: number;
+ syllable_sequence: string;
+ tone_sequence: string;
+ ipa_sequence: string;
+ tag?: string;
+ notes?: string;
+}
+
+export interface WordRhyme {
+ id: number;
+ text: string;
+ lang: string;
+ notes?: string;
+}
+
+export interface Idiom {
+ id: number;
+ spelling: string;
+ lang: string;
+ frequency?: number;
+}
+
+export interface SyllableWordMapping {
+ syl_id: number;
+ word_id: number;
+ idx: number;
+ stressed?: number;
+}
+
+export class PhoneticsQueries {
+ constructor(private db: Database) {}
+
+ // Tone operations
+ getTones(lang?: string): Tone[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, lang, name, nums
+ FROM tones
+ WHERE lang = ?
+ ORDER BY nums
+ `);
+ return query.all(lang) as Tone[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, lang, name, nums
+ FROM tones
+ ORDER BY lang, nums
+ `);
+ return query.all() as Tone[];
+ }
+ }
+
+ getToneById(id: number): Tone | null {
+ const query = this.db.query(`
+ SELECT id, ipa, lang, name, nums
+ FROM tones
+ WHERE id = ?
+ `);
+ return query.get(id) as Tone | null;
+ }
+
+ getToneByName(name: string, lang: string): Tone | null {
+ const query = this.db.query(`
+ SELECT id, ipa, lang, name, nums
+ FROM tones
+ WHERE name = ? AND lang = ?
+ `);
+ return query.get(name, lang) as Tone | null;
+ }
+
+ // Syllable component operations
+ getOnsets(lang?: string): Onset[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM onsets
+ WHERE lang = ?
+ ORDER BY text
+ `);
+ return query.all(lang) as Onset[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM onsets
+ ORDER BY lang, text
+ `);
+ return query.all() as Onset[];
+ }
+ }
+
+ getMedials(lang?: string): Medial[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM medials
+ WHERE lang = ?
+ ORDER BY text
+ `);
+ return query.all(lang) as Medial[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM medials
+ ORDER BY lang, text
+ `);
+ return query.all() as Medial[];
+ }
+ }
+
+ getNucleus(lang?: string): Nucleus[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM nucleus
+ WHERE lang = ?
+ ORDER BY text
+ `);
+ return query.all(lang) as Nucleus[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM nucleus
+ ORDER BY lang, text
+ `);
+ return query.all() as Nucleus[];
+ }
+ }
+
+ getCodas(lang?: string): Coda[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM codas
+ WHERE lang = ?
+ ORDER BY text
+ `);
+ return query.all(lang) as Coda[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM codas
+ ORDER BY lang, text
+ `);
+ return query.all() as Coda[];
+ }
+ }
+
+ getRhymes(lang?: string): Rhyme[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM rhymes
+ WHERE lang = ?
+ ORDER BY text
+ `);
+ return query.all(lang) as Rhyme[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, ipa, text, lang
+ FROM rhymes
+ ORDER BY lang, text
+ `);
+ return query.all() as Rhyme[];
+ }
+ }
+
+ // Complete syllable operations
+ getSyllables(lang?: string, tone?: number): Syllable[] {
+ if (lang && tone !== undefined) {
+ const query = this.db.query(`
+ SELECT s.id, s.lang, s.ipa, s.long, s.text, s.onset, s.medial, s.nucleus, s.coda, s.rhyme, s.tone, s.notes,
+ o.text as onset_text, m.text as medial_text, n.text as nucleus_text,
+ c.text as coda_text, r.text as rhyme_text, t.name as tone_name
+ FROM syllables s
+ LEFT JOIN onsets o ON s.onset = o.id
+ LEFT JOIN medials m ON s.medial = m.id
+ LEFT JOIN nucleus n ON s.nucleus = n.id
+ LEFT JOIN codas c ON s.coda = c.id
+ LEFT JOIN rhymes r ON s.rhyme = r.id
+ LEFT JOIN tones t ON s.tone = t.id
+ WHERE s.lang = ? AND s.tone = ?
+ ORDER BY s.text
+ `);
+ return query.all(lang, tone) as Syllable[];
+ } else if (lang) {
+ const query = this.db.query(`
+ SELECT s.id, s.lang, s.ipa, s.long, s.text, s.onset, s.medial, s.nucleus, s.coda, s.rhyme, s.tone, s.notes,
+ o.text as onset_text, m.text as medial_text, n.text as nucleus_text,
+ c.text as coda_text, r.text as rhyme_text, t.name as tone_name
+ FROM syllables s
+ LEFT JOIN onsets o ON s.onset = o.id
+ LEFT JOIN medials m ON s.medial = m.id
+ LEFT JOIN nucleus n ON s.nucleus = n.id
+ LEFT JOIN codas c ON s.coda = c.id
+ LEFT JOIN rhymes r ON s.rhyme = r.id
+ LEFT JOIN tones t ON s.tone = t.id
+ WHERE s.lang = ?
+ ORDER BY s.text, s.tone
+ `);
+ return query.all(lang) as Syllable[];
+ } else {
+ const query = this.db.query(`
+ SELECT s.id, s.lang, s.ipa, s.long, s.text, s.onset, s.medial, s.nucleus, s.coda, s.rhyme, s.tone, s.notes,
+ o.text as onset_text, m.text as medial_text, n.text as nucleus_text,
+ c.text as coda_text, r.text as rhyme_text, t.name as tone_name
+ FROM syllables s
+ LEFT JOIN onsets o ON s.onset = o.id
+ LEFT JOIN medials m ON s.medial = m.id
+ LEFT JOIN nucleus n ON s.nucleus = n.id
+ LEFT JOIN codas c ON s.coda = c.id
+ LEFT JOIN rhymes r ON s.rhyme = r.id
+ LEFT JOIN tones t ON s.tone = t.id
+ ORDER BY s.lang, s.text, s.tone
+ `);
+ return query.all() as Syllable[];
+ }
+ }
+
+ getSyllableById(id: number): Syllable | null {
+ const query = this.db.query(`
+ SELECT s.id, s.lang, s.ipa, s.long, s.text, s.onset, s.medial, s.nucleus, s.coda, s.rhyme, s.tone, s.notes,
+ o.text as onset_text, m.text as medial_text, n.text as nucleus_text,
+ c.text as coda_text, r.text as rhyme_text, t.name as tone_name
+ FROM syllables s
+ LEFT JOIN onsets o ON s.onset = o.id
+ LEFT JOIN medials m ON s.medial = m.id
+ LEFT JOIN nucleus n ON s.nucleus = n.id
+ LEFT JOIN codas c ON s.coda = c.id
+ LEFT JOIN rhymes r ON s.rhyme = r.id
+ LEFT JOIN tones t ON s.tone = t.id
+ WHERE s.id = ?
+ `);
+ return query.get(id) as Syllable | null;
+ }
+
+ // Word phonetics operations
+ getWordPhonetics(wordId: number): WordPhonetics | null {
+ const query = this.db.query(`
+ SELECT id, word_id, ipa, syllable_count, syllable_sequence, tone_sequence, ipa_sequence, tag, notes
+ FROM word_phonetics
+ WHERE word_id = ?
+ `);
+ return query.get(wordId) as WordPhonetics | null;
+ }
+
+ getWordsByTonePattern(toneSequence: string, syllableCount?: number, limit: number = 50): number[] {
+ if (syllableCount !== undefined) {
+ const query = this.db.query(`
+ SELECT word_id
+ FROM word_phonetics
+ WHERE tone_sequence = ? AND syllable_count = ?
+ ORDER BY id
+ LIMIT ?
+ `);
+ const results = query.all(toneSequence, syllableCount, limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ } else {
+ const query = this.db.query(`
+ SELECT word_id
+ FROM word_phonetics
+ WHERE tone_sequence = ?
+ ORDER BY syllable_count, id
+ LIMIT ?
+ `);
+ const results = query.all(toneSequence, limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+ }
+
+ getWordsBySyllablePattern(syllableSequence: string, limit: number = 50): number[] {
+ const query = this.db.query(`
+ SELECT word_id
+ FROM word_phonetics
+ WHERE syllable_sequence = ?
+ ORDER BY id
+ LIMIT ?
+ `);
+ const results = query.all(syllableSequence, limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ searchWordsByPhoneticPattern(pattern: {
+ toneSequence?: string;
+ syllableCount?: number;
+ minSyllableCount?: number;
+ maxSyllableCount?: number;
+ limit?: number;
+ }): number[] {
+ let conditions: string[] = [];
+ let params: any[] = [];
+
+ if (pattern.toneSequence) {
+ conditions.push('tone_sequence LIKE ?');
+ params.push(`%${pattern.toneSequence}%`);
+ }
+
+ if (pattern.syllableCount !== undefined) {
+ conditions.push('syllable_count = ?');
+ params.push(pattern.syllableCount);
+ }
+
+ if (pattern.minSyllableCount !== undefined) {
+ conditions.push('syllable_count >= ?');
+ params.push(pattern.minSyllableCount);
+ }
+
+ if (pattern.maxSyllableCount !== undefined) {
+ conditions.push('syllable_count <= ?');
+ params.push(pattern.maxSyllableCount);
+ }
+
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
+ const limit = pattern.limit || 50;
+ params.push(limit);
+
+ const query = this.db.query(`
+ SELECT word_id
+ FROM word_phonetics
+ ${whereClause}
+ ORDER BY syllable_count, id
+ LIMIT ?
+ `);
+
+ const results = query.all(...params) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ // Word-syllable mapping operations
+ getSyllablesForWord(wordId: number): SyllableWordMapping[] {
+ const query = this.db.query(`
+ SELECT sw.syl_id, sw.word_id, sw.idx, sw.stressed,
+ s.text as syllable_text, s.ipa as syllable_ipa, s.tone, s.long,
+ t.name as tone_name
+ FROM syllables_words sw
+ JOIN syllables s ON sw.syl_id = s.id
+ LEFT JOIN tones t ON s.tone = t.id
+ WHERE sw.word_id = ?
+ ORDER BY sw.idx
+ `);
+ return query.all(wordId) as SyllableWordMapping[];
+ }
+
+ getWordsForSyllable(syllableId: number): number[] {
+ const query = this.db.query(`
+ SELECT DISTINCT word_id
+ FROM syllables_words
+ WHERE syl_id = ?
+ ORDER BY word_id
+ `);
+ const results = query.all(syllableId) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ // Rhyme operations
+ getWordRhymes(wordId: number): WordRhyme[] {
+ const query = this.db.query(`
+ SELECT wr.id, wr.text, wr.lang, wr.notes
+ FROM word_rhymes wr
+ JOIN words_wrhymes wwr ON wr.id = wwr.wrhyme_id
+ WHERE wwr.word_id = ?
+ `);
+ return query.all(wordId) as WordRhyme[];
+ }
+
+ getWordsByRhyme(rhymeText: string, lang: string, limit: number = 50): number[] {
+ const query = this.db.query(`
+ SELECT DISTINCT ww.word_id
+ FROM word_rhymes wr
+ JOIN words_wrhymes ww ON wr.id = ww.wrhyme_id
+ WHERE wr.text = ? AND wr.lang = ?
+ LIMIT ?
+ `);
+ const results = query.all(rhymeText, lang, limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ // Idiom operations
+ getIdioms(lang?: string): Idiom[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency
+ FROM idioms
+ WHERE lang = ?
+ ORDER BY frequency DESC, spelling
+ `);
+ return query.all(lang) as Idiom[];
+ } else {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency
+ FROM idioms
+ ORDER BY lang, frequency DESC, spelling
+ `);
+ return query.all() as Idiom[];
+ }
+ }
+
+ getIdiomsForWord(wordId: number): Idiom[] {
+ const query = this.db.query(`
+ SELECT i.id, i.spelling, i.lang, i.frequency
+ FROM idioms i
+ JOIN words_idioms wi ON i.id = wi.idiom_id
+ WHERE wi.word_id = ?
+ ORDER BY i.frequency DESC, i.spelling
+ `);
+ return query.all(wordId) as Idiom[];
+ }
+
+ // Phonetic statistics and analysis
+ getPhoneticStats(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ COUNT(DISTINCT s.id) as total_syllables,
+ COUNT(DISTINCT s.tone) as unique_tones,
+ AVG(s.long) as avg_vowel_length,
+ COUNT(DISTINCT s.onset) as unique_onsets,
+ COUNT(DISTINCT s.medial) as unique_medials,
+ COUNT(DISTINCT s.nucleus) as unique_nucleus,
+ COUNT(DISTINCT s.coda) as unique_codas,
+ COUNT(DISTINCT s.rhyme) as unique_rhymes,
+ COUNT(DISTINCT wp.id) as total_word_phonetics,
+ AVG(wp.syllable_count) as avg_syllables_per_word
+ FROM syllables s
+ LEFT JOIN word_phonetics wp ON 1=1
+ WHERE s.lang = ?
+ `);
+ return query.get(lang);
+ }
+
+ getToneDistribution(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ t.name as tone_name,
+ t.nums as tone_number,
+ COUNT(s.id) as syllable_count,
+ ROUND(COUNT(s.id) * 100.0 / (SELECT COUNT(*) FROM syllables WHERE lang = ?), 2) as percentage
+ FROM syllables s
+ JOIN tones t ON s.tone = t.id
+ WHERE s.lang = ?
+ GROUP BY t.id, t.name, t.nums
+ ORDER BY t.nums
+ `);
+ return query.all(lang, lang);
+ }
+
+ getSyllableComplexityStats(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ syllable_count,
+ COUNT(*) as word_count,
+ ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM word_phonetics), 2) as percentage
+ FROM word_phonetics wp
+ JOIN expressions e ON wp.word_id = e.id
+ WHERE e.lang = ?
+ GROUP BY syllable_count
+ ORDER BY syllable_count
+ `);
+ return query.all(lang);
+ }
+} \ No newline at end of file
diff --git a/packages/db/src/schema.sql b/packages/db/src/schema.sql
new file mode 100644
index 0000000..c4a7c76
--- /dev/null
+++ b/packages/db/src/schema.sql
@@ -0,0 +1,729 @@
+/**
+ * UNIFIED DATABASE SCHEMA FOR THAI LANGUAGE LEARNING APPLICATION
+ *
+ * This file consolidates 3 redundant database schemas:
+ * - schema.sql (main schema with courses and SRS)
+ * - prosodyschema.sql (phonetic analysis with syllable breakdown)
+ * - senseschema.sql (semantic analysis with subsenses and derivations)
+ *
+ * REDUNDANCY ANALYSIS:
+ * ====================
+ *
+ ** MAJOR CONFLICTS RESOLVED:
+ * 1. Languages table:
+ * - schema.sql: uses 'code' as PRIMARY KEY
+ * - prosodyschema.sql: uses 'iso6392' as PRIMARY KEY
+ * - RESOLVED: Unified with both code systems supported
+ *
+ * 2. Senses table:
+ * - schema.sql: includes ipa/prosody JSONB fields
+ * - senseschema.sql: missing phonetic fields
+ * - RESOLVED: Enhanced version with all fields
+ *
+ * 3. Word/Expression entities:
+ * - schema.sql: uses 'expressions' table
+ * - prosodyschema.sql: uses 'words' table
+ * - RESOLVED: Standardized on 'expressions' terminology
+ *
+ * 4. Categories tables:
+ * - EXACT DUPLICATES in schema.sql and senseschema.sql
+ * - RESOLVED: Keep one instance, remove duplicate
+ *
+ * UNIQUE FEATURES PRESERVED:
+ * ========================
+ *
+ * FROM schema.sql (Main Course Learning):
+ * - User management: users, cookies
+ * - Course structure: lessons, cards, cards_lessons, cards_expressions
+ * - SRS tracking: user_progress, attempts
+ * - Bookmarks and user features
+ *
+ * FROM prosodyschema.sql (Phonetic Analysis):
+ * - Detailed syllable breakdown: tones, onsets, medials, nucleus, codas, rhymes
+ * - Phonetic patterns: word_phonetics, syllables_words
+ * - Rhyme analysis: word_rhymes, words_wrhymes
+ *
+ * FROM senseschema.sql (Semantic Analysis):
+ * - Detailed definitions: subsenses
+ * - Etymology tracking: derivation
+ *
+ * FOREIGN KEY RELATIONSHIPS:
+ * =========================
+ * - Updated all references from 'words.id' to 'expressions.id'
+ * - Unified language references to use languages.code
+ * - Maintained proper cascade relationships
+ *
+ * BENEFITS OF UNIFICATION:
+ * =======================
+ * 1. Single source of truth for database structure
+ * 2. Eliminated conflicting table definitions
+ * 3. Preserved all unique functionality
+ * 4. Improved foreign key consistency
+ * 5. Better support for comprehensive Thai language analysis
+ */
+
+-- Enable foreign key support and performance optimizations
+PRAGMA foreign_keys = ON;
+PRAGMA journal_mode = WAL;
+PRAGMA cache_size = -2000;
+PRAGMA mmap_size = 30000000000;
+
+/**
+ * UNIFIED LANGUAGES TABLE
+ *
+ * RESOLVES CONFLICT between:
+ * - schema.sql: code (PRIMARY KEY), name, native_name
+ * - prosodyschema.sql: iso6392 (PRIMARY KEY), english
+ *
+ * NOW SUPPORTS multiple language code systems for maximum compatibility
+ */
+CREATE TABLE IF NOT EXISTS languages (
+ code TEXT PRIMARY KEY, -- Primary language code (ISO 639-1 preferred)
+ name TEXT NOT NULL, -- English name
+ native_name TEXT, -- Native name
+ iso6392 TEXT UNIQUE, -- ISO 639-2 code alternative
+ CONSTRAINT name_unique UNIQUE (name)
+);
+
+/**
+ * CORE CONTENT TABLES
+ * Standardized on 'expressions' terminology from schema.sql
+ * Enhanced with fields from both schemas
+ */
+
+-- Main expressions table (formerly 'words' in prosodyschema.sql)
+-- UNIFIED from schema.sql expressions and prosodyschema.sql words
+CREATE TABLE IF NOT EXISTS expressions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ spelling TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ frequency INTEGER,
+ type TEXT NOT NULL, -- word | idiom | w/e
+ notes TEXT, -- Additional notes (from prosodyschema.sql)
+ FOREIGN KEY (lang) REFERENCES languages(code),
+ CONSTRAINT spell_unique UNIQUE (spelling, lang)
+);
+
+-- Enhanced senses table with phonetic capabilities
+-- MERGED from schema.sql senses (with ipa/prosody) and senseschema.sql senses
+CREATE TABLE IF NOT EXISTS senses (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ parent_id INTEGER NOT NULL,
+ spelling TEXT NOT NULL,
+ pos TEXT,
+ etymology TEXT,
+ confidence INTEGER NOT NULL DEFAULT 0,
+ FOREIGN KEY (parent_id) REFERENCES expressions(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS categories (
+ name TEXT PRIMARY KEY
+);
+
+-- Word-category relationships
+CREATE TABLE IF NOT EXISTS word_categories (
+ word_id INTEGER NOT NULL,
+ category TEXT NOT NULL,
+ PRIMARY KEY (word_id, category),
+ FOREIGN KEY (word_id) REFERENCES senses(id) ON DELETE CASCADE,
+ FOREIGN KEY (category) REFERENCES categories(name) ON DELETE CASCADE
+);
+
+-- Semantic analysis tables (FROM senseschema.sql)
+CREATE TABLE IF NOT EXISTS examples(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ sid INTEGER NOT NULL,
+ example TEXT NOT NULL, -- Example sentence
+ ref TEXT, -- source of the quote, llm if generated etc.
+ FOREIGN KEY (sid) REFERENCES senses(id) ON DELETE CASCADE
+);
+CREATE TABLE IF NOT EXISTS subsenses (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ sid INTEGER NOT NULL,
+ gloss TEXT NOT NULL, -- Definition/explanation
+ FOREIGN KEY (sid) REFERENCES senses(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS derivation (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ sid INTEGER NOT NULL,
+ type TEXT NOT NULL, -- Type of derivation (prefix, suffix, compound, etc.)
+ text TEXT NOT NULL, -- Derivative text
+ tags JSONB, -- Additional metadata
+ FOREIGN KEY (sid) REFERENCES senses(id) ON DELETE CASCADE
+);
+
+/**
+ * PHONETIC ANALYSIS TABLES (FROM prosodyschema.sql)
+ *
+ * Comprehensive syllable breakdown system for Thai language analysis
+ * These tables provide detailed phonetic analysis beyond basic JSONB fields
+ */
+
+
+-- Syllable component tables
+CREATE TABLE IF NOT EXISTS tones (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ name TEXT NOT NULL,
+ nums INTEGER NOT NULL, -- Tone number (1-5 for Thai)
+ CONSTRAINT tone_unique UNIQUE (ipa, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS onsets (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ CONSTRAINT onset_unique UNIQUE (ipa, text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS medials (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ CONSTRAINT medial_unique UNIQUE (ipa, text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS nucleus (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ CONSTRAINT nucleus_unique UNIQUE (ipa, text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS codas (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ CONSTRAINT coda_unique UNIQUE (ipa, text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS rhymes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipa TEXT NOT NULL,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ CONSTRAINT rhyme_unique UNIQUE (ipa, text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+-- Complete syllable breakdown (UNIQUE to prosodyschema.sql)
+CREATE TABLE IF NOT EXISTS syllables (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ lang TEXT NOT NULL,
+ ipa TEXT NOT NULL,
+ long INTEGER NOT NULL, -- Vowel length (0=short, 1=long)
+ text TEXT NOT NULL,
+ onset INTEGER NOT NULL,
+ medial INTEGER NOT NULL,
+ nucleus INTEGER NOT NULL,
+ coda INTEGER NOT NULL,
+ rhyme INTEGER NOT NULL,
+ tone INTEGER NOT NULL,
+ notes TEXT,
+ FOREIGN KEY (lang) REFERENCES languages(code),
+ FOREIGN KEY (onset) REFERENCES onsets(id),
+ FOREIGN KEY (medial) REFERENCES medials(id),
+ FOREIGN KEY (nucleus) REFERENCES nucleus(id),
+ FOREIGN KEY (coda) REFERENCES codas(id),
+ FOREIGN KEY (rhyme) REFERENCES rhymes(id),
+ FOREIGN KEY (tone) REFERENCES tones(id),
+ CONSTRAINT syllable_unique UNIQUE (text, ipa, lang)
+);
+
+-- Phonetic pattern storage (UNIQUE to prosodyschema.sql)
+CREATE TABLE IF NOT EXISTS word_phonetics (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ word_id INTEGER NOT NULL,
+ sense_id INTEGER,
+ ipa TEXT NOT NULL,
+ ipa_sequence TEXT NOT NULL, -- IPA representation sequence
+ syllable_count INTEGER NOT NULL,
+ syllable_sequence TEXT NOT NULL, -- Comma-separated syllables
+ stressed INTEGER, -- index of stressed syllable
+ tone_sequence TEXT, -- Comma-separated tones
+ tag JSONB, -- Pattern/usage tag
+ notes TEXT,
+ FOREIGN KEY (word_id) REFERENCES expressions(id),
+ FOREIGN KEY (sense_id) REFERENCES senses(id)
+);
+
+-- Rhyme analysis tables (UNIQUE to prosodyschema.sql)
+CREATE TABLE IF NOT EXISTS word_rhymes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ text TEXT NOT NULL,
+ lang TEXT NOT NULL,
+ notes TEXT,
+ CONSTRAINT wrhyme_unique UNIQUE (text, lang),
+ FOREIGN KEY (lang) REFERENCES languages(code)
+);
+
+CREATE TABLE IF NOT EXISTS words_wrhymes (
+ word_id INTEGER NOT NULL,
+ wrhyme_id INTEGER NOT NULL,
+ FOREIGN KEY (word_id) REFERENCES word_phonetics(id),
+ FOREIGN KEY (wrhyme_id) REFERENCES word_rhymes(id),
+ PRIMARY KEY (word_id, wrhyme_id)
+);
+
+-- Junction tables for relationships
+
+-- Expressions to syllables mapping
+CREATE TABLE IF NOT EXISTS syllables_words (
+ syl_id INTEGER NOT NULL,
+ word_id INTEGER NOT NULL,
+ idx INTEGER NOT NULL, -- Position in word
+ stressed INTEGER, -- Stress accent (0=none, 1=stressed)
+ FOREIGN KEY (syl_id) REFERENCES syllables(id),
+ FOREIGN KEY (word_id) REFERENCES word_phonetics(id),
+ PRIMARY KEY (syl_id, word_id, idx)
+);
+
+/**
+ * COURSE AND LEARNING TABLES (FROM schema.sql)
+ *
+ * Complete course structure and lesson management system
+ */
+
+-- User management
+CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ creds TEXT NOT NULL,
+ CONSTRAINT name_unique UNIQUE (name)
+);
+
+CREATE TABLE sessions (
+ id TEXT PRIMARY KEY, -- Session ID/token (usually UUID or random hash)
+ user_id INTEGER NOT NULL,
+ created_at INTEGER NOT NULL, -- Timestamp when created
+ expires_at INTEGER NOT NULL, -- Timestamp when expires
+ last_activity INTEGER, -- Last activity timestamp
+ ip_address TEXT, -- Optional: track IP
+ user_agent TEXT, -- Optional: track browser/client
+ data JSONB, -- Optional: session-specific data
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
+);
+
+CREATE INDEX idx_sessions_user ON sessions(user_id);
+CREATE INDEX idx_sessions_expires ON sessions(expires_at);
+
+ -- The separation is cleaner:
+ -- - sessions table - Server-side session management
+ -- - cookies are just the client-side token that references the session ID
+
+ -- Some apps also add:
+ -- - refresh_tokens table for JWT refresh tokens
+ -- - remember_tokens table for "remember me" functionality
+ -- - active_sessions view for currently valid sessions
+
+ -- The cookie itself just stores the session ID, while all the actual session data lives in the database. This is more secure and gives you better control over session
+ -- invalidation.
+-- Course structure
+CREATE TABLE IF NOT EXISTS lessons (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ position INTEGER NOT NULL DEFAULT 0,
+ description TEXT
+);
+
+CREATE TABLE IF NOT EXISTS lang_lessons (
+ lesson_id INTEGER NOT NULL,
+ lang TEXT NOT NULL,
+ PRIMARY KEY (lang, lesson_id),
+ FOREIGN KEY (lang) REFERENCES languages(code),
+ FOREIGN KEY (lesson_id) REFERENCES lessons(id)
+);
+
+CREATE TABLE IF NOT EXISTS cards (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ text TEXT NOT NULL,
+ note TEXT
+);
+
+CREATE TABLE IF NOT EXISTS cards_expressions (
+ expression_id INTEGER NOT NULL,
+ card_id INTEGER NOT NULL,
+ PRIMARY KEY (card_id, expression_id),
+ FOREIGN KEY (card_id) REFERENCES cards(id),
+ FOREIGN KEY (expression_id) REFERENCES expressions(id)
+);
+
+CREATE TABLE IF NOT EXISTS cards_lessons (
+ lesson_id INTEGER,
+ card_id INTEGER NOT NULL,
+ PRIMARY KEY (card_id, lesson_id),
+ FOREIGN KEY (card_id) REFERENCES cards(id),
+ FOREIGN KEY (lesson_id) REFERENCES lessons(id)
+);
+
+
+/**
+ * USER PROGRESS AND SRS TRACKING (FROM schema.sql)
+ *
+ * Complete spaced repetition system with attempt tracking
+ */
+
+CREATE TABLE IF NOT EXISTS user_progress (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ card_id INTEGER NOT NULL,
+ repetition_count INTEGER DEFAULT 0,
+ ease_factor REAL DEFAULT 2.5,
+ interval INTEGER DEFAULT 1,
+ next_review_date INTEGER,
+ last_reviewed INTEGER,
+ is_mastered BOOLEAN DEFAULT FALSE,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (card_id) REFERENCES cards(id),
+ CONSTRAINT progress_unique UNIQUE (user_id, card_id)
+);
+
+CREATE TABLE IF NOT EXISTS attempts (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ timestamp INTEGER NOT NULL,
+ card_id INTEGER NOT NULL,
+ good INTEGER NOT NULL, -- 0 or 1 for success/failure
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (card_id) REFERENCES cards(id)
+);
+
+/**
+ * USER FEATURES (FROM schema.sql)
+ */
+
+CREATE TABLE IF NOT EXISTS bookmarks (
+ word_id INTEGER NOT NULL,
+ user_id INTEGER NOT NULL,
+ notes TEXT,
+ created INTEGER NOT NULL,
+ PRIMARY KEY (word_id, user_id),
+ FOREIGN KEY (word_id) REFERENCES expressions(id),
+ FOREIGN KEY (user_id) REFERENCES users(id)
+);
+
+/**
+ * INDEXES FOR PERFORMANCE OPTIMIZATION
+ *
+ * Comprehensive indexes based on analysis of query patterns
+ * from all three schemas
+ */
+
+-- Language and expression indexes
+CREATE INDEX IF NOT EXISTS idx_expressions_spelling ON expressions(spelling);
+CREATE INDEX IF NOT EXISTS idx_expressions_type ON expressions(type);
+CREATE INDEX IF NOT EXISTS idx_expressions_lang ON expressions(lang);
+CREATE INDEX IF NOT EXISTS idx_expressions_lang_freq ON expressions(lang, frequency DESC);
+
+-- Sense and semantic indexes
+CREATE INDEX IF NOT EXISTS idx_senses_parent ON senses(parent_id);
+CREATE INDEX IF NOT EXISTS idx_senses_pos ON senses(pos);
+CREATE INDEX IF NOT EXISTS idx_subsenses_sid ON subsenses(sid);
+CREATE INDEX IF NOT EXISTS idx_derivation_sid ON derivation(sid);
+
+-- Phonetic analysis indexes (FROM prosodyschema.sql)
+CREATE INDEX IF NOT EXISTS idx_tones_name_lang ON tones(name, lang);
+CREATE INDEX IF NOT EXISTS idx_tones_nums_lang ON tones(nums, lang);
+CREATE INDEX IF NOT EXISTS idx_syllables_text_lang ON syllables(text, lang);
+CREATE INDEX IF NOT EXISTS idx_syllables_tone ON syllables(tone);
+
+CREATE INDEX IF NOT EXISTS idx_word_phonetics_word_id ON word_phonetics(word_id);
+CREATE INDEX IF NOT EXISTS idx_word_phonetics_syllables ON word_phonetics(syllable_sequence);
+CREATE INDEX IF NOT EXISTS idx_word_phonetics_tones ON word_phonetics(tone_sequence);
+CREATE INDEX IF NOT EXISTS idx_word_phonetics_count ON word_phonetics(syllable_count);
+
+-- Junction table indexes
+CREATE INDEX IF NOT EXISTS idx_syllables_words_word_idx ON syllables_words(word_id, idx);
+CREATE INDEX IF NOT EXISTS idx_syllables_words_syl ON syllables_words(syl_id);
+CREATE INDEX IF NOT EXISTS idx_cards_expressions ON cards_expressions(expression_id, card_id);
+
+-- User progress and SRS indexes
+CREATE INDEX IF NOT EXISTS idx_user_progress_user ON user_progress(user_id);
+CREATE INDEX IF NOT EXISTS idx_user_progress_card ON user_progress(card_id);
+CREATE INDEX IF NOT EXISTS idx_user_progress_next_review ON user_progress(next_review_date);
+CREATE INDEX IF NOT EXISTS idx_attempts_user ON attempts(user_id);
+CREATE INDEX IF NOT EXISTS idx_attempts_card ON attempts(card_id);
+CREATE INDEX IF NOT EXISTS idx_attempts_user_resource ON attempts(user_id, card_id);
+
+-- User feature indexes
+CREATE INDEX IF NOT EXISTS idx_bookmarks ON bookmarks(word_id);
+CREATE INDEX IF NOT EXISTS idx_word_categories_category ON word_categories(category);
+
+-- Composite indexes for common query patterns
+CREATE INDEX IF NOT EXISTS idx_syllables_compound ON syllables(lang, text, tone);
+CREATE INDEX IF NOT EXISTS idx_syllables_words_compound ON syllables_words(word_id, idx, syl_id);
+CREATE INDEX IF NOT EXISTS idx_word_patterns_mixed ON word_phonetics(syllable_count, syllable_sequence, tone_sequence);
+
+/**
+ * TONE-SPECIFIC SRS SYSTEM
+ *
+ * Advanced tone pattern learning with detailed progress tracking
+ * and analytics for effective Thai tone acquisition
+ */
+
+-- Tone pattern progress tracking
+CREATE TABLE IF NOT EXISTS tone_pattern_progress (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ word_id INTEGER NOT NULL,
+ tone_sequence TEXT NOT NULL,
+ syllable_sequence TEXT NOT NULL,
+ repetition_count INTEGER DEFAULT 0,
+ ease_factor REAL DEFAULT 2.5,
+ interval INTEGER DEFAULT 1,
+ next_review_date INTEGER NOT NULL,
+ last_reviewed INTEGER NOT NULL,
+ is_mastered BOOLEAN DEFAULT FALSE,
+ difficulty REAL DEFAULT 2.5,
+ tone_accuracy REAL DEFAULT 0.0,
+ created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ updated_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ FOREIGN KEY (word_id) REFERENCES expressions(id) ON DELETE CASCADE,
+ UNIQUE(user_id, word_id)
+);
+
+-- Tone pattern attempt tracking with detailed metrics
+CREATE TABLE IF NOT EXISTS tone_attempts (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ word_id INTEGER NOT NULL,
+ timestamp INTEGER NOT NULL,
+ accuracy REAL NOT NULL CHECK (accuracy >= 0 AND accuracy <= 1),
+ tone_accuracy REAL NOT NULL CHECK (tone_accuracy >= 0 AND tone_accuracy <= 1),
+ pronunciation_score REAL DEFAULT 0.0 CHECK (pronunciation_score >= 0 AND pronunciation_score <= 1),
+ review_time INTEGER NOT NULL,
+ difficulty REAL NOT NULL,
+ mode TEXT DEFAULT 'exploration' CHECK (mode IN ('exploration', 'practice', 'review')),
+ created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ FOREIGN KEY (word_id) REFERENCES expressions(id) ON DELETE CASCADE
+);
+
+-- User tone learning preferences and settings
+CREATE TABLE IF NOT EXISTS tone_learning_settings (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ preferred_session_length INTEGER DEFAULT 20 CHECK (preferred_session_length > 0),
+ target_daily_reviews INTEGER DEFAULT 50 CHECK (target_daily_reviews > 0),
+ difficulty_preference REAL DEFAULT 2.5 CHECK (difficulty_preference >= 1.0 AND difficulty_preference <= 4.0),
+ audio_enabled BOOLEAN DEFAULT TRUE,
+ tone_visualization_enabled BOOLEAN DEFAULT TRUE,
+ auto_advance BOOLEAN DEFAULT FALSE,
+ created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ updated_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ UNIQUE(user_id)
+);
+
+-- Tone pattern mastery statistics and analytics
+CREATE TABLE IF NOT EXISTS tone_mastery_stats (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ tone_pattern TEXT NOT NULL,
+ syllable_count INTEGER NOT NULL CHECK (syllable_count > 0),
+ total_attempts INTEGER DEFAULT 0,
+ successful_attempts INTEGER DEFAULT 0,
+ avg_accuracy REAL DEFAULT 0.0 CHECK (avg_accuracy >= 0 AND avg_accuracy <= 1),
+ avg_review_time REAL DEFAULT 0.0,
+ best_accuracy REAL DEFAULT 0.0 CHECK (best_accuracy >= 0 AND best_accuracy <= 1),
+ last_attempted INTEGER,
+ mastered_at INTEGER,
+ created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ updated_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ UNIQUE(user_id, tone_pattern, syllable_count)
+);
+
+/**
+ * TONE-SPECIFIC INDEXES
+ * Optimized for tone pattern queries and SRS operations
+ */
+
+CREATE INDEX IF NOT EXISTS idx_tone_progress_user_word ON tone_pattern_progress(user_id, word_id);
+CREATE INDEX IF NOT EXISTS idx_tone_progress_next_review ON tone_pattern_progress(next_review_date);
+CREATE INDEX IF NOT EXISTS idx_tone_progress_user ON tone_pattern_progress(user_id);
+CREATE INDEX IF NOT EXISTS idx_tone_progress_difficulty ON tone_pattern_progress(difficulty);
+CREATE INDEX IF NOT EXISTS idx_tone_progress_mastered ON tone_pattern_progress(is_mastered);
+
+CREATE INDEX IF NOT EXISTS idx_tone_attempts_user_word ON tone_attempts(user_id, word_id);
+CREATE INDEX IF NOT EXISTS idx_tone_attempts_timestamp ON tone_attempts(timestamp);
+CREATE INDEX IF NOT EXISTS idx_tone_attempts_user ON tone_attempts(user_id);
+CREATE INDEX IF NOT EXISTS idx_tone_attempts_mode ON tone_attempts(mode);
+CREATE INDEX IF NOT EXISTS idx_tone_attempts_accuracy ON tone_attempts(accuracy, tone_accuracy);
+
+CREATE INDEX IF NOT EXISTS idx_tone_mastery_pattern ON tone_mastery_stats(tone_pattern, syllable_count);
+CREATE INDEX IF NOT EXISTS idx_tone_mastery_user ON tone_mastery_stats(user_id);
+CREATE INDEX IF NOT EXISTS idx_tone_mastery_accuracy ON tone_mastery_stats(avg_accuracy);
+
+/**
+ * TRIGGERS FOR TONE LEARNING SYSTEM
+ * Automatic timestamp updates and statistics calculation
+ */
+
+-- Update timestamps automatically
+CREATE TRIGGER IF NOT EXISTS update_tone_progress_updated_at
+AFTER UPDATE ON tone_pattern_progress
+FOR EACH ROW
+BEGIN
+ UPDATE tone_pattern_progress SET updated_at = (strftime('%s', 'now') * 1000) WHERE id = NEW.id;
+END;
+
+CREATE TRIGGER IF NOT EXISTS update_tone_learning_settings_updated_at
+AFTER UPDATE ON tone_learning_settings
+FOR EACH ROW
+BEGIN
+ UPDATE tone_learning_settings SET updated_at = (strftime('%s', 'now') * 1000) WHERE id = NEW.id;
+END;
+
+CREATE TRIGGER IF NOT EXISTS update_tone_mastery_updated_at
+AFTER UPDATE ON tone_mastery_stats
+FOR EACH ROW
+BEGIN
+ UPDATE tone_mastery_stats SET updated_at = (strftime('%s', 'now') * 1000) WHERE id = NEW.id;
+END;
+
+-- Update mastery stats when a pattern is mastered
+CREATE TRIGGER IF NOT EXISTS update_tone_mastery_on_progress
+AFTER UPDATE ON tone_pattern_progress
+FOR EACH ROW
+WHEN NEW.is_mastered = 1 AND OLD.is_mastered = 0
+BEGIN
+ INSERT OR REPLACE INTO tone_mastery_stats
+ (user_id, tone_pattern, syllable_count, total_attempts, successful_attempts, avg_accuracy, mastered_at, updated_at)
+ VALUES (
+ NEW.user_id,
+ NEW.tone_sequence,
+ (SELECT COUNT(*) FROM (SELECT value FROM json_each(NEW.syllable_sequence))),
+ NEW.repetition_count,
+ CAST(NEW.repetition_count * NEW.tone_accuracy AS INTEGER),
+ NEW.tone_accuracy,
+ (strftime('%s', 'now') * 1000),
+ (strftime('%s', 'now') * 1000)
+ );
+END;
+
+-- Update tone mastery stats after each attempt
+CREATE TRIGGER IF NOT EXISTS update_tone_mastery_after_attempt
+AFTER INSERT ON tone_attempts
+FOR EACH ROW
+BEGIN
+ -- Get the current tone progress
+ INSERT OR REPLACE INTO tone_mastery_stats
+ (user_id, tone_pattern, syllable_count, total_attempts, successful_attempts, avg_accuracy, avg_review_time, best_accuracy, last_attempted, updated_at)
+ SELECT
+ ta.user_id,
+ tpp.tone_sequence,
+ (SELECT COUNT(*) FROM (SELECT value FROM json_each(tpp.syllable_sequence))) as syllable_count,
+ COALESCE(tms.total_attempts + 1, 1) as total_attempts,
+ COALESCE(tms.successful_attempts + CAST(ta.tone_accuracy >= 0.8 AS INTEGER), CAST(ta.tone_accuracy >= 0.8 AS INTEGER)) as successful_attempts,
+ CASE
+ WHEN tms.total_attempts IS NULL THEN ta.tone_accuracy
+ ELSE (tms.avg_accuracy * tms.total_attempts + ta.tone_accuracy) / (tms.total_attempts + 1)
+ END as avg_accuracy,
+ CASE
+ WHEN tms.total_attempts IS NULL THEN ta.review_time
+ ELSE (tms.avg_review_time * tms.total_attempts + ta.review_time) / (tms.total_attempts + 1)
+ END as avg_review_time,
+ CASE
+ WHEN tms.best_accuracy IS NULL THEN ta.tone_accuracy
+ WHEN ta.tone_accuracy > tms.best_accuracy THEN ta.tone_accuracy
+ ELSE tms.best_accuracy
+ END as best_accuracy,
+ (strftime('%s', 'now') * 1000) as last_attempted,
+ (strftime('%s', 'now') * 1000) as updated_at
+ FROM tone_attempts ta
+ JOIN tone_pattern_progress tpp ON tpp.word_id = ta.word_id AND tpp.user_id = ta.user_id
+ LEFT JOIN tone_mastery_stats tms ON tms.user_id = ta.user_id
+ AND tms.tone_pattern = tpp.tone_sequence
+ AND tms.syllable_count = (SELECT COUNT(*) FROM (SELECT value FROM json_each(tpp.syllable_sequence)))
+ WHERE ta.rowid = NEW.rowid;
+END;
+
+/**
+ * VIEWS FOR TONE LEARNING ANALYTICS
+ * Common queries for dashboard and reporting
+ */
+
+-- User dashboard view combining traditional SRS and tone learning
+CREATE VIEW IF NOT EXISTS user_dashboard AS
+SELECT
+ u.id as user_id,
+ u.name as username,
+ COUNT(DISTINCT tpp.word_id) as total_tone_words,
+ COUNT(DISTINCT CASE WHEN tpp.is_mastered = 1 THEN tpp.word_id END) as mastered_tone_words,
+ COUNT(DISTINCT CASE WHEN tpp.next_review_date <= (strftime('%s', 'now') * 1000) THEN tpp.word_id END) as due_tone_words,
+ COUNT(DISTINCT up.card_id) as total_cards,
+ COUNT(DISTINCT CASE WHEN up.is_mastered = 1 THEN up.card_id END) as mastered_cards,
+ COUNT(DISTINCT CASE WHEN up.next_review_date <= (strftime('%s', 'now') * 1000) THEN up.card_id END) as due_cards,
+ (SELECT COUNT(*) FROM attempts WHERE user_id = u.id AND timestamp > (strftime('%s', 'now') * 1000 - 86400000)) as reviews_today,
+ (SELECT AVG(ta.tone_accuracy) FROM tone_attempts ta WHERE ta.user_id = u.id AND ta.timestamp > (strftime('%s', 'now') * 1000 - 604800000)) as weekly_tone_accuracy
+FROM users u
+LEFT JOIN tone_pattern_progress tpp ON tpp.user_id = u.id
+LEFT JOIN user_progress up ON up.user_id = u.id
+GROUP BY u.id, u.name;
+
+-- Tone learning statistics view
+CREATE VIEW IF NOT EXISTS tone_learning_stats_view AS
+SELECT
+ tms.user_id,
+ tms.tone_pattern,
+ tms.syllable_count,
+ tms.total_attempts,
+ tms.successful_attempts,
+ tms.avg_accuracy,
+ tms.avg_review_time,
+ tms.best_accuracy,
+ tms.last_attempted,
+ tms.mastered_at,
+ COUNT(tpp.word_id) as word_count_with_pattern,
+ AVG(tpp.difficulty) as avg_difficulty,
+ AVG(tpp.tone_accuracy) as avg_current_accuracy
+FROM tone_mastery_stats tms
+LEFT JOIN tone_pattern_progress tpp ON tpp.user_id = tms.user_id
+ AND tpp.tone_sequence = tms.tone_pattern
+GROUP BY tms.user_id, tms.tone_pattern, tms.syllable_count, tms.total_attempts,
+ tms.successful_attempts, tms.avg_accuracy, tms.avg_review_time,
+ tms.best_accuracy, tms.last_attempted, tms.mastered_at;
+
+/**
+ * MIGRATION NOTES:
+ * ===============
+ *
+ * TONE SRS SYSTEM ADDITIONS:
+ * - Added comprehensive tone learning tables to the unified schema
+ * - Maintains consistency with existing phonetic analysis structure
+ * - Supports both traditional SRS and tone-specific learning
+ * - Provides detailed analytics for tone pattern mastery
+ *
+ * BACKWARD COMPATIBILITY:
+ * - All existing tables and relationships preserved
+ * - New tables are additive and don't break existing functionality
+ * - Views provide unified dashboard with both learning systems
+ *
+ * PERFORMANCE:
+ * - Added specialized indexes for tone pattern queries
+ * - Optimized for Thai tone learning analytics
+ * - Supports real-time progress tracking
+ *
+ * This enhanced unified schema now provides comprehensive support for:
+ * - Thai language learning with detailed phonetic analysis
+ * - Course management and lesson structure
+ * - Traditional spaced repetition system
+ * - Advanced tone pattern learning with SRS
+ * - User management and personalization
+ * - Comprehensive learning analytics
+ *
+ * TOTAL: Unified 4 schemas while preserving all unique functionality
+ */
diff --git a/packages/db/src/semantic.ts b/packages/db/src/semantic.ts
new file mode 100644
index 0000000..a3fe44b
--- /dev/null
+++ b/packages/db/src/semantic.ts
@@ -0,0 +1,554 @@
+import { Database } from 'bun:sqlite';
+
+export interface Expression {
+ id: number;
+ spelling: string;
+ lang: string;
+ frequency?: number;
+ type: string;
+ syllables?: number;
+ notes?: string;
+ ipa?: any; // JSONB
+ prosody?: any; // JSONB
+ confidence: number;
+}
+
+export interface Sense {
+ id: number;
+ parent_id: number;
+ spelling: string;
+ pos?: string;
+ etymology?: string;
+ senses?: any; // JSONB
+ forms?: any; // JSONB
+ related?: any; // JSONB
+ ipa?: any; // JSONB
+ prosody?: any; // JSONB
+ confidence: number;
+}
+
+export interface Subsense {
+ id: number;
+ sid: number;
+ gloss: string;
+ examples?: any; // JSONB
+}
+
+export interface Derivation {
+ id: number;
+ sid: number;
+ type: string;
+ text: string;
+ tags?: any; // JSONB
+}
+
+export interface Category {
+ name: string;
+}
+
+export interface WordCategory {
+ word_id: number;
+ category: string;
+}
+
+export interface Bookmark {
+ word_id: number;
+ user_id: number;
+ notes?: string;
+ created: number;
+}
+
+export interface Language {
+ code: string;
+ name: string;
+ native_name?: string;
+ iso6392?: string;
+ english?: string;
+}
+
+export class SemanticQueries {
+ constructor(private db: Database) {}
+
+ // Language operations
+ getLanguages(): Language[] {
+ const query = this.db.query(`
+ SELECT code, name, native_name, iso6392, english
+ FROM languages
+ ORDER BY name
+ `);
+ return query.all() as Language[];
+ }
+
+ getLanguageByCode(code: string): Language | null {
+ const query = this.db.query(`
+ SELECT code, name, native_name, iso6392, english
+ FROM languages
+ WHERE code = ?
+ `);
+ return query.get(code) as Language | null;
+ }
+
+ // Expression operations
+ getExpressionById(id: number): Expression | null {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE id = ?
+ `);
+ const result = query.get(id) as Expression | null;
+ if (result) {
+ // Parse JSON fields
+ result.ipa = result.ipa ? JSON.parse(result.ipa) : undefined;
+ result.prosody = result.prosody ? JSON.parse(result.prosody) : undefined;
+ }
+ return result;
+ }
+
+ getExpressionsByLanguage(lang: string, limit: number = 100, offset: number = 0): Expression[] {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE lang = ?
+ ORDER BY frequency DESC, spelling
+ LIMIT ? OFFSET ?
+ `);
+ const results = query.all(lang, limit, offset) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+
+ searchExpressions(searchTerm: string, lang?: string, limit: number = 50): Expression[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE lang = ? AND spelling LIKE ?
+ ORDER BY
+ CASE
+ WHEN spelling = ? THEN 1
+ WHEN spelling LIKE ? THEN 2
+ ELSE 3
+ END,
+ frequency DESC
+ LIMIT ?
+ `);
+ const results = query.all(lang, `%${searchTerm}%`, searchTerm, `${searchTerm}%`, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE spelling LIKE ?
+ ORDER BY
+ CASE
+ WHEN spelling = ? THEN 1
+ WHEN spelling LIKE ? THEN 2
+ ELSE 3
+ END,
+ frequency DESC
+ LIMIT ?
+ `);
+ const results = query.all(`%${searchTerm}%`, searchTerm, `${searchTerm}%`, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ getExpressionsByType(type: string, lang?: string, limit: number = 100): Expression[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE type = ? AND lang = ?
+ ORDER BY frequency DESC, spelling
+ LIMIT ?
+ `);
+ const results = query.all(type, lang, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE type = ?
+ ORDER BY frequency DESC, spelling
+ LIMIT ?
+ `);
+ const results = query.all(type, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ getExpressionsByFrequency(lang: string, minFrequency: number = 0, maxFrequency: number = 1000, limit: number = 100): Expression[] {
+ const query = this.db.query(`
+ SELECT id, spelling, lang, frequency, type, syllables, notes, ipa, prosody, confidence
+ FROM expressions
+ WHERE lang = ? AND frequency BETWEEN ? AND ?
+ ORDER BY frequency DESC, spelling
+ LIMIT ?
+ `);
+ const results = query.all(lang, minFrequency, maxFrequency, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+
+ // Sense operations
+ getSensesForExpression(expressionId: number): Sense[] {
+ const query = this.db.query(`
+ SELECT id, parent_id, spelling, pos, etymology, senses, forms, related, ipa, prosody, confidence
+ FROM senses
+ WHERE parent_id = ?
+ ORDER BY confidence DESC
+ `);
+ const results = query.all(expressionId) as Sense[];
+ return results.map(result => ({
+ ...result,
+ senses: result.senses ? JSON.parse(result.senses) : undefined,
+ forms: result.forms ? JSON.parse(result.forms) : undefined,
+ related: result.related ? JSON.parse(result.related) : undefined,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+
+ getSenseById(id: number): Sense | null {
+ const query = this.db.query(`
+ SELECT id, parent_id, spelling, pos, etymology, senses, forms, related, ipa, prosody, confidence
+ FROM senses
+ WHERE id = ?
+ `);
+ const result = query.get(id) as Sense | null;
+ if (result) {
+ result.senses = result.senses ? JSON.parse(result.senses) : undefined;
+ result.forms = result.forms ? JSON.parse(result.forms) : undefined;
+ result.related = result.related ? JSON.parse(result.related) : undefined;
+ result.ipa = result.ipa ? JSON.parse(result.ipa) : undefined;
+ result.prosody = result.prosody ? JSON.parse(result.prosody) : undefined;
+ }
+ return result;
+ }
+
+ searchSenses(searchTerm: string, lang?: string, limit: number = 50): Sense[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT s.id, s.parent_id, s.spelling, s.pos, s.etymology, s.senses, s.forms, s.related, s.ipa, s.prosody, s.confidence,
+ e.lang
+ FROM senses s
+ JOIN expressions e ON s.parent_id = e.id
+ WHERE e.lang = ? AND (s.spelling LIKE ? OR s.pos LIKE ?)
+ ORDER BY s.confidence DESC
+ LIMIT ?
+ `);
+ const results = query.all(lang, `%${searchTerm}%`, `%${searchTerm}%`, limit) as Sense[];
+ return results.map(result => ({
+ ...result,
+ senses: result.senses ? JSON.parse(result.senses) : undefined,
+ forms: result.forms ? JSON.parse(result.forms) : undefined,
+ related: result.related ? JSON.parse(result.related) : undefined,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT s.id, s.parent_id, s.spelling, s.pos, s.etymology, s.senses, s.forms, s.related, s.ipa, s.prosody, s.confidence,
+ e.lang
+ FROM senses s
+ JOIN expressions e ON s.parent_id = e.id
+ WHERE s.spelling LIKE ? OR s.pos LIKE ?
+ ORDER BY s.confidence DESC
+ LIMIT ?
+ `);
+ const results = query.all(`%${searchTerm}%`, `%${searchTerm}%`, limit) as Sense[];
+ return results.map(result => ({
+ ...result,
+ senses: result.senses ? JSON.parse(result.senses) : undefined,
+ forms: result.forms ? JSON.parse(result.forms) : undefined,
+ related: result.related ? JSON.parse(result.related) : undefined,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ // Subsense operations
+ getSubsensesForSense(senseId: number): Subsense[] {
+ const query = this.db.query(`
+ SELECT id, sid, gloss, examples
+ FROM subsenses
+ WHERE sid = ?
+ ORDER BY id
+ `);
+ const results = query.all(senseId) as Subsense[];
+ return results.map(result => ({
+ ...result,
+ examples: result.examples ? JSON.parse(result.examples) : undefined,
+ }));
+ }
+
+ // Derivation operations
+ getDerivationsForSense(senseId: number): Derivation[] {
+ const query = this.db.query(`
+ SELECT id, sid, type, text, tags
+ FROM derivation
+ WHERE sid = ?
+ ORDER BY type, text
+ `);
+ const results = query.all(senseId) as Derivation[];
+ return results.map(result => ({
+ ...result,
+ tags: result.tags ? JSON.parse(result.tags) : undefined,
+ }));
+ }
+
+ // Category operations
+ getCategories(): Category[] {
+ const query = this.db.query(`
+ SELECT name
+ FROM categories
+ ORDER BY name
+ `);
+ return query.all() as Category[];
+ }
+
+ getCategoriesForWord(wordId: number): Category[] {
+ const query = this.db.query(`
+ SELECT c.name
+ FROM categories c
+ JOIN word_categories wc ON c.name = wc.category
+ WHERE wc.word_id = ?
+ ORDER BY c.name
+ `);
+ return query.all(wordId) as Category[];
+ }
+
+ getWordsByCategory(category: string, lang?: string, limit: number = 100): Expression[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT e.id, e.spelling, e.lang, e.frequency, e.type, e.syllables, e.notes, e.ipa, e.prosody, e.confidence
+ FROM expressions e
+ JOIN word_categories wc ON e.id = wc.word_id
+ WHERE wc.category = ? AND e.lang = ?
+ ORDER BY e.frequency DESC, e.spelling
+ LIMIT ?
+ `);
+ const results = query.all(category, lang, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT e.id, e.spelling, e.lang, e.frequency, e.type, e.syllables, e.notes, e.ipa, e.prosody, e.confidence
+ FROM expressions e
+ JOIN word_categories wc ON e.id = wc.word_id
+ WHERE wc.category = ?
+ ORDER BY e.frequency DESC, e.spelling
+ LIMIT ?
+ `);
+ const results = query.all(category, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ // Bookmark operations
+ getBookmarksForUser(userId: number): Bookmark[] {
+ const query = this.db.query(`
+ SELECT word_id, user_id, notes, created
+ FROM bookmarks
+ WHERE user_id = ?
+ ORDER BY created DESC
+ `);
+ return query.all(userId) as Bookmark[];
+ }
+
+ createBookmark(userId: number, wordId: number, notes?: string): Bookmark {
+ const query = this.db.query(`
+ INSERT INTO bookmarks (word_id, user_id, notes, created)
+ VALUES (?, ?, ?, ?)
+ RETURNING word_id, user_id, notes, created
+ `);
+ return query.get(wordId, userId, notes, Date.now()) as Bookmark;
+ }
+
+ updateBookmark(userId: number, wordId: number, notes?: string): Bookmark | null {
+ const query = this.db.query(`
+ UPDATE bookmarks
+ SET notes = ?
+ WHERE user_id = ? AND word_id = ?
+ RETURNING word_id, user_id, notes, created
+ `);
+ return query.get(notes, userId, wordId) as Bookmark | null;
+ }
+
+ deleteBookmark(userId: number, wordId: number): boolean {
+ const query = this.db.query(`
+ DELETE FROM bookmarks
+ WHERE user_id = ? AND word_id = ?
+ `);
+ const result = query.run(userId, wordId);
+ return result.changes > 0;
+ }
+
+ isBookmarked(userId: number, wordId: number): boolean {
+ const query = this.db.query(`
+ SELECT 1
+ FROM bookmarks
+ WHERE user_id = ? AND word_id = ?
+ LIMIT 1
+ `);
+ return query.get(userId, wordId) !== undefined;
+ }
+
+ // Advanced search operations
+ searchByPOS(pos: string, lang?: string, limit: number = 50): Expression[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT DISTINCT e.id, e.spelling, e.lang, e.frequency, e.type, e.syllables, e.notes, e.ipa, e.prosody, e.confidence
+ FROM expressions e
+ JOIN senses s ON e.id = s.parent_id
+ WHERE e.lang = ? AND s.pos = ?
+ ORDER BY e.frequency DESC, e.spelling
+ LIMIT ?
+ `);
+ const results = query.all(lang, pos, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT DISTINCT e.id, e.spelling, e.lang, e.frequency, e.type, e.syllables, e.notes, e.ipa, e.prosody, e.confidence
+ FROM expressions e
+ JOIN senses s ON e.id = s.parent_id
+ WHERE s.pos = ?
+ ORDER BY e.frequency DESC, e.spelling
+ LIMIT ?
+ `);
+ const results = query.all(pos, limit) as Expression[];
+ return results.map(result => ({
+ ...result,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ searchByEtymology(term: string, lang?: string, limit: number = 50): Sense[] {
+ if (lang) {
+ const query = this.db.query(`
+ SELECT s.id, s.parent_id, s.spelling, s.pos, s.etymology, s.senses, s.forms, s.related, s.ipa, s.prosody, s.confidence,
+ e.lang
+ FROM senses s
+ JOIN expressions e ON s.parent_id = e.id
+ WHERE e.lang = ? AND s.etymology LIKE ?
+ ORDER BY s.confidence DESC
+ LIMIT ?
+ `);
+ const results = query.all(lang, `%${term}%`, limit) as Sense[];
+ return results.map(result => ({
+ ...result,
+ senses: result.senses ? JSON.parse(result.senses) : undefined,
+ forms: result.forms ? JSON.parse(result.forms) : undefined,
+ related: result.related ? JSON.parse(result.related) : undefined,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ } else {
+ const query = this.db.query(`
+ SELECT s.id, s.parent_id, s.spelling, s.pos, s.etymology, s.senses, s.forms, s.related, s.ipa, s.prosody, s.confidence,
+ e.lang
+ FROM senses s
+ JOIN expressions e ON s.parent_id = e.id
+ WHERE s.etymology LIKE ?
+ ORDER BY s.confidence DESC
+ LIMIT ?
+ `);
+ const results = query.all(`%${term}%`, limit) as Sense[];
+ return results.map(result => ({
+ ...result,
+ senses: result.senses ? JSON.parse(result.senses) : undefined,
+ forms: result.forms ? JSON.parse(result.forms) : undefined,
+ related: result.related ? JSON.parse(result.related) : undefined,
+ ipa: result.ipa ? JSON.parse(result.ipa) : undefined,
+ prosody: result.prosody ? JSON.parse(result.prosody) : undefined,
+ }));
+ }
+ }
+
+ // Statistics operations
+ getSemanticStats(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ COUNT(DISTINCT e.id) as total_expressions,
+ COUNT(DISTINCT s.id) as total_senses,
+ COUNT(DISTINCT s.pos) as unique_pos,
+ COUNT(DISTINCT c.name) as unique_categories,
+ AVG(e.confidence) as avg_confidence,
+ COUNT(DISTINCT e.type) as unique_types
+ FROM expressions e
+ LEFT JOIN senses s ON e.id = s.parent_id
+ LEFT JOIN word_categories wc ON e.id = wc.word_id
+ LEFT JOIN categories c ON wc.category = c.name
+ WHERE e.lang = ?
+ `);
+ return query.get(lang);
+ }
+
+ getPOSTDistribution(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ s.pos,
+ COUNT(DISTINCT s.parent_id) as expression_count,
+ COUNT(s.id) as sense_count,
+ ROUND(COUNT(s.id) * 100.0 / (SELECT COUNT(*) FROM senses WHERE pos IS NOT NULL), 2) as percentage
+ FROM senses s
+ JOIN expressions e ON s.parent_id = e.id
+ WHERE e.lang = ? AND s.pos IS NOT NULL
+ GROUP BY s.pos
+ ORDER BY sense_count DESC
+ `);
+ return query.all(lang);
+ }
+
+ getCategoryDistribution(lang: string) {
+ const query = this.db.query(`
+ SELECT
+ c.name as category,
+ COUNT(DISTINCT wc.word_id) as word_count,
+ ROUND(COUNT(DISTINCT wc.word_id) * 100.0 / (SELECT COUNT(*) FROM word_categories), 2) as percentage
+ FROM categories c
+ JOIN word_categories wc ON c.name = wc.category
+ JOIN expressions e ON wc.word_id = e.id
+ WHERE e.lang = ?
+ GROUP BY c.name
+ ORDER BY word_count DESC
+ `);
+ return query.all(lang);
+ }
+} \ No newline at end of file
diff --git a/packages/db/src/srs.ts b/packages/db/src/srs.ts
new file mode 100644
index 0000000..0b80960
--- /dev/null
+++ b/packages/db/src/srs.ts
@@ -0,0 +1,402 @@
+import { Database } from 'bun:sqlite';
+
+// Traditional SRS Types
+export interface SRSProgress {
+ id?: number;
+ user_id: number;
+ card_id: number;
+ repetition_count: number;
+ ease_factor: number;
+ interval: number;
+ next_review_date: number | null;
+ last_reviewed: number | null;
+ is_mastered: boolean;
+ created_at?: number;
+ updated_at?: number;
+}
+
+export interface SRSAttempt {
+ id?: number;
+ user_id: number;
+ timestamp: number;
+ card_id: number;
+ good: number; // 0 or 1
+ created_at?: number;
+}
+
+// Tone SRS Types
+export interface ToneProgress {
+ id?: number;
+ user_id: number;
+ word_id: number;
+ tone_sequence: string;
+ syllable_sequence: string;
+ repetition_count: number;
+ ease_factor: number;
+ interval: number;
+ next_review_date: number;
+ last_reviewed: number;
+ is_mastered: boolean;
+ difficulty: number;
+ tone_accuracy: number;
+ created_at?: number;
+ updated_at?: number;
+}
+
+export interface ToneAttempt {
+ id?: number;
+ user_id: number;
+ word_id: number;
+ timestamp: number;
+ accuracy: number;
+ tone_accuracy: number;
+ pronunciation_score: number;
+ review_time: number;
+ difficulty: number;
+ mode: 'exploration' | 'practice' | 'review';
+ created_at?: number;
+}
+
+export interface ToneMasteryStats {
+ id?: number;
+ user_id: number;
+ tone_pattern: string;
+ syllable_count: number;
+ total_attempts: number;
+ successful_attempts: number;
+ avg_accuracy: number;
+ avg_review_time: number;
+ best_accuracy: number;
+ last_attempted: number | null;
+ mastered_at: number | null;
+ created_at?: number;
+ updated_at?: number;
+}
+
+export class SRSQueries {
+ constructor(private db: Database) {}
+
+ // Traditional SRS Methods
+
+ /**
+ * Get or create SRS progress for a user and card
+ */
+ getOrCreateSRSProgress(userId: number, cardId: number): SRSProgress {
+ const query = this.db.query(`
+ INSERT INTO user_progress (user_id, card_id)
+ VALUES (?, ?)
+ ON CONFLICT (user_id, card_id) DO UPDATE SET
+ user_id = excluded.user_id,
+ card_id = excluded.card_id
+ RETURNING id, user_id, card_id, repetition_count, ease_factor, interval,
+ next_review_date, last_reviewed, is_mastered, created_at, updated_at
+ `);
+
+ return query.get(userId, cardId) as SRSProgress;
+ }
+
+ /**
+ * Update SRS progress after a review
+ */
+ updateSRSProgress(progress: Omit<SRSProgress, 'id' | 'created_at' | 'updated_at'>): SRSProgress {
+ const query = this.db.query(`
+ UPDATE user_progress
+ SET repetition_count = ?,
+ ease_factor = ?,
+ interval = ?,
+ next_review_date = ?,
+ last_reviewed = ?,
+ is_mastered = ?,
+ updated_at = (strftime('%s', 'now') * 1000)
+ WHERE user_id = ? AND card_id = ?
+ RETURNING id, user_id, card_id, repetition_count, ease_factor, interval,
+ next_review_date, last_reviewed, is_mastered, created_at, updated_at
+ `);
+
+ return query.get(
+ progress.repetition_count,
+ progress.ease_factor,
+ progress.interval,
+ progress.next_review_date,
+ progress.last_reviewed,
+ progress.is_mastered,
+ progress.user_id,
+ progress.card_id
+ ) as SRSProgress;
+ }
+
+ /**
+ * Record a traditional SRS attempt
+ */
+ recordSRSAttempt(attempt: Omit<SRSAttempt, 'id' | 'created_at'>): SRSAttempt {
+ const query = this.db.query(`
+ INSERT INTO attempts (user_id, timestamp, card_id, good)
+ VALUES (?, ?, ?, ?)
+ RETURNING id, user_id, timestamp, card_id, good, created_at
+ `);
+
+ return query.get(
+ attempt.user_id,
+ attempt.timestamp,
+ attempt.card_id,
+ attempt.good
+ ) as SRSAttempt;
+ }
+
+ /**
+ * Get due cards for a user
+ */
+ getDueCards(userId: number, limit: number = 20): number[] {
+ const query = this.db.query(`
+ SELECT card_id
+ FROM user_progress
+ WHERE user_id = ?
+ AND (next_review_date IS NULL OR next_review_date <= ?)
+ AND is_mastered = 0
+ ORDER BY next_review_date ASC
+ LIMIT ?
+ `);
+
+ const results = query.all(userId, Date.now(), limit) as { card_id: number }[];
+ return results.map(r => r.card_id);
+ }
+
+ /**
+ * Get SRS progress statistics for a user
+ */
+ getSRSStats(userId: number) {
+ const query = this.db.query(`
+ SELECT
+ COUNT(*) as total_cards,
+ COUNT(CASE WHEN is_mastered = 1 THEN 1 END) as mastered_cards,
+ COUNT(CASE WHEN next_review_date <= ? THEN 1 END) as due_cards,
+ COUNT(CASE WHEN next_review_date > ? THEN 1 END) as future_cards,
+ AVG(ease_factor) as avg_ease_factor,
+ AVG(interval) as avg_interval
+ FROM user_progress
+ WHERE user_id = ?
+ `);
+
+ return query.get(Date.now(), Date.now(), userId);
+ }
+
+ // Tone SRS Methods
+
+ /**
+ * Get or create tone progress for a user and word
+ */
+ getOrCreateToneProgress(userId: number, wordId: number, toneSequence: string, syllableSequence: string): ToneProgress {
+ const query = this.db.query(`
+ INSERT INTO tone_pattern_progress (user_id, word_id, tone_sequence, syllable_sequence, next_review_date, last_reviewed)
+ VALUES (?, ?, ?, ?, ?, ?)
+ ON CONFLICT (user_id, word_id) DO UPDATE SET
+ tone_sequence = excluded.tone_sequence,
+ syllable_sequence = excluded.syllable_sequence
+ RETURNING id, user_id, word_id, tone_sequence, syllable_sequence, repetition_count,
+ ease_factor, interval, next_review_date, last_reviewed, is_mastered,
+ difficulty, tone_accuracy, created_at, updated_at
+ `);
+
+ return query.get(
+ userId,
+ wordId,
+ toneSequence,
+ syllableSequence,
+ Date.now(), // next_review_date
+ Date.now() // last_reviewed
+ ) as ToneProgress;
+ }
+
+ /**
+ * Update tone progress after a practice session
+ */
+ updateToneProgress(progress: Omit<ToneProgress, 'id' | 'created_at' | 'updated_at'>): ToneProgress {
+ const query = this.db.query(`
+ UPDATE tone_pattern_progress
+ SET repetition_count = ?,
+ ease_factor = ?,
+ interval = ?,
+ next_review_date = ?,
+ last_reviewed = ?,
+ is_mastered = ?,
+ difficulty = ?,
+ tone_accuracy = ?,
+ updated_at = (strftime('%s', 'now') * 1000)
+ WHERE user_id = ? AND word_id = ?
+ RETURNING id, user_id, word_id, tone_sequence, syllable_sequence, repetition_count,
+ ease_factor, interval, next_review_date, last_reviewed, is_mastered,
+ difficulty, tone_accuracy, created_at, updated_at
+ `);
+
+ return query.get(
+ progress.repetition_count,
+ progress.ease_factor,
+ progress.interval,
+ progress.next_review_date,
+ progress.last_reviewed,
+ progress.is_mastered,
+ progress.difficulty,
+ progress.tone_accuracy,
+ progress.user_id,
+ progress.word_id
+ ) as ToneProgress;
+ }
+
+ /**
+ * Record a tone attempt
+ */
+ recordToneAttempt(attempt: Omit<ToneAttempt, 'id' | 'created_at'>): ToneAttempt {
+ const query = this.db.query(`
+ INSERT INTO tone_attempts (user_id, word_id, timestamp, accuracy, tone_accuracy,
+ pronunciation_score, review_time, difficulty, mode)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ RETURNING id, user_id, word_id, timestamp, accuracy, tone_accuracy,
+ pronunciation_score, review_time, difficulty, mode, created_at
+ `);
+
+ return query.get(
+ attempt.user_id,
+ attempt.word_id,
+ attempt.timestamp,
+ attempt.accuracy,
+ attempt.tone_accuracy,
+ attempt.pronunciation_score,
+ attempt.review_time,
+ attempt.difficulty,
+ attempt.mode
+ ) as ToneAttempt;
+ }
+
+ /**
+ * Get due tone words for a user
+ */
+ getDueToneWords(userId: number, limit: number = 20): number[] {
+ const query = this.db.query(`
+ SELECT word_id
+ FROM tone_pattern_progress
+ WHERE user_id = ?
+ AND next_review_date <= ?
+ AND is_mastered = 0
+ ORDER BY next_review_date ASC, difficulty ASC
+ LIMIT ?
+ `);
+
+ const results = query.all(userId, Date.now(), limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ /**
+ * Get tone progress statistics for a user
+ */
+ getToneStats(userId: number) {
+ const query = this.db.query(`
+ SELECT
+ COUNT(*) as total_tone_words,
+ COUNT(CASE WHEN is_mastered = 1 THEN 1 END) as mastered_tone_words,
+ COUNT(CASE WHEN next_review_date <= ? THEN 1 END) as due_tone_words,
+ AVG(difficulty) as avg_difficulty,
+ AVG(tone_accuracy) as avg_tone_accuracy,
+ AVG(ease_factor) as avg_ease_factor
+ FROM tone_pattern_progress
+ WHERE user_id = ?
+ `);
+
+ return query.get(Date.now(), userId);
+ }
+
+ /**
+ * Get recent tone attempts for a user
+ */
+ getRecentToneAttempts(userId: number, limit: number = 10): ToneAttempt[] {
+ const query = this.db.query(`
+ SELECT id, user_id, word_id, timestamp, accuracy, tone_accuracy,
+ pronunciation_score, review_time, difficulty, mode, created_at
+ FROM tone_attempts
+ WHERE user_id = ?
+ ORDER BY timestamp DESC
+ LIMIT ?
+ `);
+
+ return query.all(userId, limit) as ToneAttempt[];
+ }
+
+ /**
+ * Get tone mastery statistics for a user
+ */
+ getToneMasteryStats(userId: number): ToneMasteryStats[] {
+ const query = this.db.query(`
+ SELECT id, user_id, tone_pattern, syllable_count, total_attempts, successful_attempts,
+ avg_accuracy, avg_review_time, best_accuracy, last_attempted, mastered_at,
+ created_at, updated_at
+ FROM tone_mastery_stats
+ WHERE user_id = ?
+ ORDER BY avg_accuracy DESC, last_attempted DESC
+ `);
+
+ return query.all(userId) as ToneMasteryStats[];
+ }
+
+ /**
+ * Get tone words for practice (prioritizes due words, then random new words)
+ */
+ getToneWordsForPractice(userId: number, limit: number = 20): number[] {
+ const query = this.db.query(`
+ WITH due_words AS (
+ SELECT word_id, 0 as priority
+ FROM tone_pattern_progress
+ WHERE user_id = ? AND next_review_date <= ? AND is_mastered = 0
+ LIMIT ?
+ ),
+ new_words AS (
+ SELECT e.id as word_id, 1 as priority
+ FROM expressions e
+ LEFT JOIN tone_pattern_progress tpp ON e.id = tpp.word_id AND tpp.user_id = ?
+ WHERE tpp.word_id IS NULL
+ AND e.lang = 'th'
+ AND e.type = 'word'
+ ORDER BY e.frequency DESC
+ LIMIT ?
+ )
+ SELECT word_id
+ FROM (
+ SELECT word_id, priority FROM due_words
+ UNION ALL
+ SELECT word_id, priority FROM new_words
+ )
+ ORDER BY priority, RANDOM()
+ LIMIT ?
+ `);
+
+ const results = query.all(userId, Date.now(), limit, userId, limit, limit) as { word_id: number }[];
+ return results.map(r => r.word_id);
+ }
+
+ /**
+ * Get user dashboard data (combines traditional SRS and tone SRS)
+ */
+ getUserDashboard(userId: number) {
+ const query = this.db.query(`
+ SELECT
+ u.id as user_id,
+ u.name as username,
+ COUNT(DISTINCT tpp.word_id) as total_tone_words,
+ COUNT(DISTINCT CASE WHEN tpp.is_mastered = 1 THEN tpp.word_id END) as mastered_tone_words,
+ COUNT(DISTINCT CASE WHEN tpp.next_review_date <= ? THEN tpp.word_id END) as due_tone_words,
+ COUNT(DISTINCT up.card_id) as total_cards,
+ COUNT(DISTINCT CASE WHEN up.is_mastered = 1 THEN up.card_id END) as mastered_cards,
+ COUNT(DISTINCT CASE WHEN up.next_review_date <= ? THEN up.card_id END) as due_cards,
+ (SELECT COUNT(*) FROM attempts WHERE user_id = u.id AND timestamp > ?) as reviews_today,
+ (SELECT AVG(ta.tone_accuracy) FROM tone_attempts ta WHERE ta.user_id = u.id AND ta.timestamp > ?) as weekly_tone_accuracy
+ FROM users u
+ LEFT JOIN tone_pattern_progress tpp ON tpp.user_id = u.id
+ LEFT JOIN user_progress up ON up.user_id = u.id
+ WHERE u.id = ?
+ GROUP BY u.id, u.name
+ `);
+
+ const weekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
+ const today = Date.now() - (24 * 60 * 60 * 1000);
+
+ return query.get(Date.now(), Date.now(), today, weekAgo, userId);
+ }
+} \ No newline at end of file
diff --git a/packages/db/src/test.ts b/packages/db/src/test.ts
new file mode 100644
index 0000000..6767c13
--- /dev/null
+++ b/packages/db/src/test.ts
@@ -0,0 +1,149 @@
+import ndb from ".";
+import toneDb from "@/lib/db/prosodydb";
+
+// function test() {
+// const tones = ["low", "mid"];
+// const ndb = new BigQueries();
+// const nq = ndb.fetchWordsByToneAndSyls(tones);
+// const oq = toneDb.fetchWordsByToneAndSyls(tones);
+// // const senses = JSON.parse(res[0].senses_array);
+// console.log({ nq });
+// console.log(nq.length, oq.length);
+// }
+function test() {
+ const tones = ["low", "mid"];
+ console.log("wtf");
+ // const qq = ndb.fetchWordsByToneAndSyls1(tones);
+ // const oq = ndb.fetchWordsByToneAndSylsO(tones);
+ const nq = ndb.fetchWordsByToneAndSyls(tones);
+ // const roq = toneDb.fetchWordsByToneAndSyls(tones);
+ // const senses = JSON.parse(res[0].senses_array);
+ // console.log({ nq });
+ // console.log("old", oq.length);
+ // console.log("old db", roq.length);
+ console.log("new", nq);
+ // console.log("test", qq.length);
+}
+test();
+
+// `
+// `
+// WITH word_tone_sequences AS (
+// SELECT
+// w.id as word_id,
+// w.spelling,
+// wp.ipa,
+// w.frequency,
+// GROUP_CONCAT(sy.text ORDER BY sw.idx) as syl_seq,
+// GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+// COUNT(sw.syl_id) as syllable_count,
+// (SELECT
+// json_group_array(json_object(
+// 'id', s.id,
+// 'pos', s.pos,
+// 'etymology', s.etymology,
+// 'confidence', s.confidence,
+// 'subsenses', (
+// SELECT json_group_array(json_object(
+// 'id', ss.id,
+// 'gloss', ss.gloss
+// ))
+// FROM subsenses ss
+// WHERE ss.sid = s.id
+// ),
+// 'examples', (
+// SELECT json_group_array(json_object(
+// 'id', ex.id,
+// 'example', ex.example,
+// 'ref', ex.ref
+// ))
+// FROM examples ex
+// WHERE ex.sid = s.id
+// ),
+// 'derivation', (
+// SELECT json_group_array(json_object(
+// 'id', d.id,
+// 'type', d.type,
+// 'text', d.text,
+// 'tags', d.tags
+// ))
+// FROM derivation d
+// WHERE d.sid = s.id
+// ),
+// 'categories', (
+// SELECT json_group_array(wc.category)
+// FROM word_categories wc
+// WHERE wc.word_id = s.id
+// )
+// ))
+// FROM senses s
+// WHERE s.parent_id = w.id
+// ) as senses_array
+// FROM expressions w
+// JOIN word_phonetics wp ON w.id = wp.word_id
+// JOIN syllables_words sw ON wp.id = sw.word_id
+// JOIN syllables sy ON sw.syl_id = sy.id
+// JOIN tones t ON sy.tone = t.id
+// GROUP BY w.id, w.spelling, w.lang, w.frequency
+// )
+// SELECT *
+// FROM word_tone_sequences
+// WHERE tone_sequence LIKE ?
+// AND syllable_count = ?
+// ORDER BY frequency ASC NULLS LAST;
+// `
+//
+//
+//
+// `
+// WITH word_tone_sequences AS (
+// SELECT
+// w.id as word_id,
+// w.spelling,
+// wp.ipa,
+// w.frequency,
+// GROUP_CONCAT(sy.text ORDER BY sw.idx) as syl_seq,
+// GROUP_CONCAT(t.name ORDER BY sw.idx) as tone_sequence,
+// COUNT(sw.syl_id) as syllable_count
+// FROM expressions w
+// JOIN word_phonetics wp ON w.id = wp.word_id
+// JOIN syllables_words sw ON wp.id = sw.word_id
+// JOIN syllables sy ON sw.syl_id = sy.id
+// JOIN tones t ON sy.tone = t.id
+// GROUP BY w.id, w.spelling, w.lang, w.frequency, wp.ipa
+// ),
+// sense_data AS (
+// SELECT
+// s.*,
+// GROUP_CONCAT(DISTINCT ss.id || ':' || ss.gloss, '|') as subsenses_data,
+// GROUP_CONCAT(DISTINCT ex.id || ':' || ex.example || ':' || COALESCE(ex.ref, ''), '|') as examples_data,
+// GROUP_CONCAT(DISTINCT d.id || ':' || d.type || ':' || d.text, '|') as derivation_data,
+// GROUP_CONCAT(DISTINCT wc.category, '|') as categories_data
+// FROM senses s
+// LEFT JOIN subsenses ss ON ss.sid = s.id
+// LEFT JOIN examples ex ON ex.sid = s.id
+// LEFT JOIN derivation d ON d.sid = s.id
+// LEFT JOIN word_categories wc ON wc.word_id = s.id
+// GROUP BY s.id
+// )
+// SELECT
+// wts.*,
+// (SELECT
+// json_group_array(json_object(
+// 'id', sd.id,
+// 'pos', sd.pos,
+// 'etymology', sd.etymology,
+// 'confidence', sd.confidence,
+// 'subsenses_data', sd.subsenses_data,
+// 'examples_data', sd.examples_data,
+// 'derivation_data', sd.derivation_data,
+// 'categories_data', sd.categories_data
+// ))
+// FROM sense_data sd
+// WHERE sd.parent_id = wts.word_id
+// ) as senses_array
+// FROM word_tone_sequences wts
+// WHERE wts.tone_sequence LIKE ?
+// AND wts.syllable_count = ?
+// ORDER BY wts.frequency ASC NULLS LAST;
+// `
diff --git a/packages/db/src/types.ts b/packages/db/src/types.ts
new file mode 100644
index 0000000..66c2826
--- /dev/null
+++ b/packages/db/src/types.ts
@@ -0,0 +1,84 @@
+export type Tone = {
+ letters: string;
+ numbers: number;
+ name: string;
+};
+
+export type Phoneme = {
+ ipa: string;
+ spelling: string;
+};
+export type Syllable = {
+ stressed: boolean;
+ long: boolean;
+ spelling: string;
+ ipa: string;
+ nucleus: Phoneme;
+ onset: Phoneme;
+ medial: Phoneme;
+ coda: Phoneme;
+ rhyme: Phoneme;
+ tone: Tone;
+};
+
+export type ToneQuery = Array<string | null>;
+export type MutationType = { change: string } | { keep: string };
+export type MutationOrder = MutationType[];
+
+export type PhoneticData = {
+ word_id: number;
+ tone_sequence: string;
+ syllable_count: number;
+ syl_seq: string;
+ spelling: string;
+ ipa: string;
+ frequency: number;
+};
+
+export const thaiTones: Record<string, string> = {
+ "˧": "mid",
+ "˨˩": "low",
+ "˥˩": "falling",
+ "˦˥": "high",
+ "˩˩˦": "rising",
+};
+export const thaiToneNums: Record<string, number> = {
+ "˧": 33,
+ "˨˩": 21,
+ "˥˩": 41,
+ "˦˥": 45,
+ "˩˩˦": 214,
+};
+
+export type FullWordDataDB = {
+ word_id: number;
+ tone_sequence: string;
+ syllable_count: number;
+ syl_seq: string;
+ spelling: string;
+ ipa: string;
+ frequency: number;
+ senses_array: string;
+};
+export type FullWordData = {
+ word_id: number;
+ tone_sequence: string;
+ syllable_count: number;
+ syl_seq: string;
+ spelling: string;
+ ipa: string;
+ frequency: number;
+ senses: Sense[];
+};
+export type Sense = {
+ confidence: number;
+ examples: Example[];
+ categories: string[];
+ etymology: string;
+ derivation: Derivation[];
+ pos: string;
+ forms: Array<{ form: string; tags: string[] }>;
+ glosses: string[];
+};
+export type Example = { ref: string; text: string };
+export type Derivation = { type: string; text: string; tags: any };
diff --git a/packages/db/src/users.ts b/packages/db/src/users.ts
new file mode 100644
index 0000000..ebf715c
--- /dev/null
+++ b/packages/db/src/users.ts
@@ -0,0 +1,305 @@
+import { Database } from "bun:sqlite";
+
+export interface User {
+ id: number;
+ name: string;
+ creds: string;
+}
+
+export interface Session {
+ id: string;
+ user_id: number;
+ created_at: number;
+ expires_at: number;
+ last_activity?: number;
+ ip_address?: string;
+ user_agent?: string;
+ data?: any;
+}
+
+export interface UserSettings {
+ id?: number;
+ user_id: number;
+ preferred_session_length: number;
+ target_daily_reviews: number;
+ difficulty_preference: number;
+ audio_enabled: boolean;
+ tone_visualization_enabled: boolean;
+ auto_advance: boolean;
+ created_at?: number;
+ updated_at?: number;
+}
+
+export class UserQueries {
+ constructor(private db: Database) {}
+
+ /**
+ * Create a new user
+ */
+ createUser(name: string, creds: string): User {
+ const query = this.db.query(`
+ INSERT INTO users (name, creds)
+ VALUES (?, ?)
+ RETURNING id, name, creds
+ `);
+
+ return query.get(name, creds) as User;
+ }
+
+ /**
+ * Get user by ID
+ */
+ getUserById(id: number): User | null {
+ const query = this.db.query(`
+ SELECT id, name, creds
+ FROM users
+ WHERE id = ?
+ `);
+
+ return query.get(id) as User | null;
+ }
+
+ /**
+ * Get user by name
+ */
+ getUserByName(name: string): User | null {
+ const query = this.db.query(`
+ SELECT id, name, creds
+ FROM users
+ WHERE name = ?
+ `);
+
+ return query.get(name) as User | null;
+ }
+
+ /**
+ * Create a new session for a user
+ */
+ createSession(userId: number, sessionToken: string, expiresAt: number): Session {
+ const query = this.db.query(`
+ INSERT INTO sessions (id, user_id, created_at, expires_at, last_activity)
+ VALUES (?, ?, ?, ?, ?)
+ RETURNING id, user_id, created_at, expires_at, last_activity, ip_address, user_agent, data
+ `);
+
+ const now = Date.now();
+ return query.get(sessionToken, userId, now, expiresAt, now) as Session;
+ }
+
+ /**
+ * Get session by token
+ */
+ getSessionByToken(sessionToken: string): Session | null {
+ const query = this.db.query(`
+ SELECT id, user_id, created_at, expires_at, last_activity, ip_address, user_agent, data
+ FROM sessions
+ WHERE id = ? AND expires_at > ?
+ `);
+
+ return query.get(sessionToken, Date.now()) as Session | null;
+ }
+
+ /**
+ * Get user from session token
+ */
+ getUserFromSession(sessionToken: string): User | null {
+ const query = this.db.query(`
+ SELECT u.id, u.name, u.creds
+ FROM users u
+ JOIN sessions s ON u.id = s.user_id
+ WHERE s.id = ? AND s.expires_at > ?
+ `);
+
+ return query.get(sessionToken, Date.now()) as User | null;
+ }
+
+ /**
+ * Delete session by token
+ */
+ deleteSession(sessionToken: string): boolean {
+ const query = this.db.query(`
+ DELETE FROM sessions
+ WHERE id = ?
+ `);
+
+ const result = query.run(sessionToken);
+ return result.changes > 0;
+ }
+
+ /**
+ * Clean up expired sessions
+ */
+ cleanupExpiredSessions(): number {
+ const query = this.db.query(`
+ DELETE FROM sessions
+ WHERE expires_at <= ?
+ `);
+
+ const result = query.run(Date.now());
+ return result.changes;
+ }
+
+ /**
+ * Update session last activity
+ */
+ updateSessionActivity(sessionToken: string): boolean {
+ const query = this.db.query(`
+ UPDATE sessions
+ SET last_activity = ?
+ WHERE id = ? AND expires_at > ?
+ `);
+
+ const result = query.run(Date.now(), sessionToken, Date.now());
+ return result.changes > 0;
+ }
+
+ /**
+ * Extend session expiry
+ */
+ extendSessionExpiry(sessionToken: string, newExpiry: number): boolean {
+ const query = this.db.query(`
+ UPDATE sessions
+ SET expires_at = ?, last_activity = ?
+ WHERE id = ? AND expires_at > ?
+ `);
+
+ const result = query.run(newExpiry, Date.now(), sessionToken, Date.now());
+ return result.changes > 0;
+ }
+
+ /**
+ * Get all active sessions for a user
+ */
+ getUserSessions(userId: number): Session[] {
+ const query = this.db.query(`
+ SELECT id, user_id, created_at, expires_at, last_activity, ip_address, user_agent, data
+ FROM sessions
+ WHERE user_id = ? AND expires_at > ?
+ ORDER BY last_activity DESC
+ `);
+
+ return query.all(userId, Date.now()) as Session[];
+ }
+
+ /**
+ * Delete all sessions for a user (for logout all devices)
+ */
+ deleteAllUserSessions(userId: number): number {
+ const query = this.db.query(`
+ DELETE FROM sessions
+ WHERE user_id = ?
+ `);
+
+ const result = query.run(userId);
+ return result.changes;
+ }
+
+ /**
+ * Get user settings
+ */
+ getUserSettings(userId: number): UserSettings | null {
+ const query = this.db.query(`
+ SELECT id, user_id, preferred_session_length, target_daily_reviews,
+ difficulty_preference, audio_enabled, tone_visualization_enabled,
+ auto_advance, created_at, updated_at
+ FROM tone_learning_settings
+ WHERE user_id = ?
+ `);
+
+ return query.get(userId) as UserSettings | null;
+ }
+
+ /**
+ * Create or update user settings
+ */
+ upsertUserSettings(
+ settings: Omit<UserSettings, "id" | "created_at" | "updated_at">,
+ ): UserSettings {
+ const query = this.db.query(`
+ INSERT INTO tone_learning_settings
+ (user_id, preferred_session_length, target_daily_reviews,
+ difficulty_preference, audio_enabled, tone_visualization_enabled, auto_advance)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ ON CONFLICT (user_id) DO UPDATE SET
+ preferred_session_length = excluded.preferred_session_length,
+ target_daily_reviews = excluded.target_daily_reviews,
+ difficulty_preference = excluded.difficulty_preference,
+ audio_enabled = excluded.audio_enabled,
+ tone_visualization_enabled = excluded.tone_visualization_enabled,
+ auto_advance = excluded.auto_advance,
+ updated_at = (strftime('%s', 'now') * 1000)
+ RETURNING id, user_id, preferred_session_length, target_daily_reviews,
+ difficulty_preference, audio_enabled, tone_visualization_enabled,
+ auto_advance, created_at, updated_at
+ `);
+
+ return query.get(
+ settings.user_id,
+ settings.preferred_session_length,
+ settings.target_daily_reviews,
+ settings.difficulty_preference,
+ settings.audio_enabled,
+ settings.tone_visualization_enabled,
+ settings.auto_advance,
+ ) as UserSettings;
+ }
+
+ /**
+ * Update user settings partially
+ */
+ updateUserSettings(
+ userId: number,
+ updates: Partial<UserSettings>,
+ ): UserSettings | null {
+ const setClause = Object.keys(updates)
+ .filter(
+ (key) => key !== "id" && key !== "user_id" && key !== "created_at",
+ )
+ .map((key) => `${key} = ?`)
+ .join(", ");
+
+ if (!setClause) return null;
+
+ const values = Object.values(updates).filter((_, index) => {
+ const key = Object.keys(updates)[index];
+ return key !== "id" && key !== "user_id" && key !== "created_at";
+ });
+
+ const query = this.db.query(`
+ UPDATE tone_learning_settings
+ SET ${setClause}, updated_at = (strftime('%s', 'now') * 1000)
+ WHERE user_id = ?
+ RETURNING id, user_id, preferred_session_length, target_daily_reviews,
+ difficulty_preference, audio_enabled, tone_visualization_enabled,
+ auto_advance, created_at, updated_at
+ `);
+
+ return query.get(...values, userId) as UserSettings | null;
+ }
+
+ /**
+ * Get all users (admin function)
+ */
+ getAllUsers(): User[] {
+ const query = this.db.query(`
+ SELECT id, name, creds
+ FROM users
+ ORDER BY name
+ `);
+
+ return query.all() as User[];
+ }
+
+ /**
+ * Delete user (admin function)
+ */
+ deleteUser(userId: number): boolean {
+ const query = this.db.query(`
+ DELETE FROM users
+ WHERE id = ?
+ `);
+
+ const result = query.run(userId);
+ return result.changes > 0;
+ }
+}
diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/packages/db/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/packages/lang/.gitignore b/packages/lang/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/packages/lang/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/lang/CLAUDE.md b/packages/lang/CLAUDE.md
new file mode 100644
index 0000000..1ee6890
--- /dev/null
+++ b/packages/lang/CLAUDE.md
@@ -0,0 +1,106 @@
+
+Default to using Bun instead of Node.js.
+
+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
+- Use `bun test` instead of `jest` or `vitest`
+- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
+- Bun automatically loads .env, so don't use dotenv.
+
+## APIs
+
+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
+- `Bun.redis` for Redis. Don't use `ioredis`.
+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
+- `WebSocket` is built-in. Don't use `ws`.
+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
+- Bun.$`ls` instead of execa.
+
+## Testing
+
+Use `bun test` to run tests.
+
+```ts#index.test.ts
+import { test, expect } from "bun:test";
+
+test("hello world", () => {
+ expect(1).toBe(1);
+});
+```
+
+## Frontend
+
+Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
+
+Server:
+
+```ts#index.ts
+import index from "./index.html"
+
+Bun.serve({
+ routes: {
+ "/": index,
+ "/api/users/:id": {
+ GET: (req) => {
+ return new Response(JSON.stringify({ id: req.params.id }));
+ },
+ },
+ },
+ // optional websocket support
+ websocket: {
+ open: (ws) => {
+ ws.send("Hello, world!");
+ },
+ message: (ws, message) => {
+ ws.send(message);
+ },
+ close: (ws) => {
+ // handle close
+ }
+ },
+ development: {
+ hmr: true,
+ console: true,
+ }
+})
+```
+
+HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
+
+```html#index.html
+<html>
+ <body>
+ <h1>Hello, world!</h1>
+ <script type="module" src="./frontend.tsx"></script>
+ </body>
+</html>
+```
+
+With the following `frontend.tsx`:
+
+```tsx#frontend.tsx
+import React from "react";
+
+// import .css files directly and it works
+import './index.css';
+
+import { createRoot } from "react-dom/client";
+
+const root = createRoot(document.body);
+
+export default function Frontend() {
+ return <h1>Hello, world!</h1>;
+}
+
+root.render(<Frontend />);
+```
+
+Then, run index.ts
+
+```sh
+bun --hot ./index.ts
+```
+
+For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
diff --git a/packages/lang/README.md b/packages/lang/README.md
new file mode 100644
index 0000000..dc185a1
--- /dev/null
+++ b/packages/lang/README.md
@@ -0,0 +1,23 @@
+# Langlib
+
+Utils to deal with languages and stuff.
+
+## Langcodes
+
+- ISO-639-1 are **two** characters. e.g. "en", "fr", "zh".
+- ISO-639-2 are **three** characters. e.g. "eng", "fra", "jpn", "cnm".
+- ISO-639-3 are also **three** characters. It's more exhasustive than ISO-639-2, supposedly covers every single language.
+### BCP-47
+BCP-47 (_Best Current Practice_) is a wider tag that includes in one string language, script and region.
+e.g.
+- `en-US`
+- `en-Latn-US`
+- `zh-Hans-CN`
+Format is `language-script-region-variant-extension`
+
+Region codes are ISO 3166-1
+Script tags are ISO 15924
+Variant tags aren't standardized.
+
+It may include all or some of these.
+
diff --git a/packages/lang/bun.lock b/packages/lang/bun.lock
new file mode 100644
index 0000000..cb5bacd
--- /dev/null
+++ b/packages/lang/bun.lock
@@ -0,0 +1,64 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "lang",
+ "dependencies": {
+ "bcp-47": "^2.1.0",
+ "countryjs": "^1.8.0",
+ "franc-all": "^7.2.0",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
+
+ "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
+
+ "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
+
+ "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
+
+ "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
+
+ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+ "countryjs": ["countryjs@1.8.0", "", { "dependencies": { "lodash": "^4.17.2", "minimatch": "^3.0.3", "require-all": "^2.2.0" } }, "sha512-dOgtXkpGirsknf/sV3qBGUGzN9Xlb6KlshtXwG4hYT8cPaPjuvsu/aKq88mQlXkLqRAbXwaHs8RlwJgF8FODAA=="],
+
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
+
+ "franc-all": ["franc-all@7.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-ZR6ciLQTDBaOvBdkOd8+vqDzaLtmIXRa9GCzcAlaBpqNAKg9QrtClPmqiKac5/xZXfCZGMo1d8dIu1T0BLhHEg=="],
+
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="],
+
+ "require-all": ["require-all@2.2.0", "", {}, "sha512-YWj/WNCxs+KxppuN3j11Ztqzl8MI/oWj4ERwEwgJ5gsHzWi8OAK7FepPu8MLv/Rn8Pov6aPdpRkaoO2Tb6m+zQ=="],
+
+ "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+ }
+}
diff --git a/packages/lang/package.json b/packages/lang/package.json
new file mode 100644
index 0000000..49d50e1
--- /dev/null
+++ b/packages/lang/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@sortug/langlib",
+ "module": "src/index.ts",
+ "type": "module",
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ },
+ "dependencies": {
+ "franc-all": "^7.2.0"
+ }
+}
diff --git a/packages/lang/src/index.ts b/packages/lang/src/index.ts
new file mode 100644
index 0000000..5ac22b2
--- /dev/null
+++ b/packages/lang/src/index.ts
@@ -0,0 +1,3 @@
+export * from "./iso";
+
+export { getScriptPredictor, separateScript } from "./unicode";
diff --git a/packages/lang/src/iso/index.ts b/packages/lang/src/iso/index.ts
new file mode 100644
index 0000000..60467e0
--- /dev/null
+++ b/packages/lang/src/iso/index.ts
@@ -0,0 +1,17 @@
+import type { ISO_6393_3 } from "../types";
+import { iso6393 } from "./iso6393";
+import { iso6393To1 } from "./iso6393-to-1";
+import { iso6393To2B } from "./iso6393-to-2b";
+import { iso6393To2T } from "./iso6393-to-2t";
+import { iso15924 } from "./iso15924";
+import type { ISO_15924_CODE } from "./iso15924";
+
+export { iso6393, iso6393To1, iso6393To2B, iso6393To2T, iso15924 };
+type ISO_15924 = {
+ name: string;
+ numeric: string;
+ pva?: string;
+ date: string;
+ code: ISO_15924_CODE;
+};
+export type { ISO_15924, ISO_15924_CODE, ISO_6393_3 };
diff --git a/packages/lang/src/iso/iso15924.ts b/packages/lang/src/iso/iso15924.ts
new file mode 100644
index 0000000..dcaa42d
--- /dev/null
+++ b/packages/lang/src/iso/iso15924.ts
@@ -0,0 +1,1508 @@
+export const iso15924 = [
+ {
+ code: "Adlm",
+ name: "Adlam",
+ numeric: "166",
+ pva: "Adlam",
+ date: "2016-12-05",
+ },
+ {
+ code: "Afak",
+ name: "Afaka",
+ numeric: "439",
+ date: "2010-12-21",
+ },
+ {
+ code: "Aghb",
+ name: "Caucasian Albanian",
+ numeric: "239",
+ pva: "Caucasian_Albanian",
+ date: "2014-11-15",
+ },
+ {
+ code: "Ahom",
+ name: "Ahom, Tai Ahom",
+ numeric: "338",
+ pva: "Ahom",
+ date: "2015-07-07",
+ },
+ {
+ code: "Arab",
+ name: "Arabic",
+ numeric: "160",
+ pva: "Arabic",
+ date: "2004-05-01",
+ },
+ {
+ code: "Aran",
+ name: "Arabic (Nastaliq variant)",
+ numeric: "161",
+ date: "2014-11-15",
+ },
+ {
+ code: "Armi",
+ name: "Imperial Aramaic",
+ numeric: "124",
+ pva: "Imperial_Aramaic",
+ date: "2009-06-01",
+ },
+ {
+ code: "Armn",
+ name: "Armenian",
+ numeric: "230",
+ pva: "Armenian",
+ date: "2004-05-01",
+ },
+ {
+ code: "Avst",
+ name: "Avestan",
+ numeric: "134",
+ pva: "Avestan",
+ date: "2009-06-01",
+ },
+ {
+ code: "Bali",
+ name: "Balinese",
+ numeric: "360",
+ pva: "Balinese",
+ date: "2006-10-10",
+ },
+ {
+ code: "Bamu",
+ name: "Bamum",
+ numeric: "435",
+ pva: "Bamum",
+ date: "2009-06-01",
+ },
+ {
+ code: "Bass",
+ name: "Bassa Vah",
+ numeric: "259",
+ pva: "Bassa_Vah",
+ date: "2014-11-15",
+ },
+ {
+ code: "Batk",
+ name: "Batak",
+ numeric: "365",
+ pva: "Batak",
+ date: "2010-07-23",
+ },
+ {
+ code: "Beng",
+ name: "Bengali (Bangla)",
+ numeric: "325",
+ pva: "Bengali",
+ date: "2016-12-05",
+ },
+ {
+ code: "Bhks",
+ name: "Bhaiksuki",
+ numeric: "334",
+ pva: "Bhaiksuki",
+ date: "2016-12-05",
+ },
+ {
+ code: "Blis",
+ name: "Blissymbols",
+ numeric: "550",
+ date: "2004-05-01",
+ },
+ {
+ code: "Bopo",
+ name: "Bopomofo",
+ numeric: "285",
+ pva: "Bopomofo",
+ date: "2004-05-01",
+ },
+ {
+ code: "Brah",
+ name: "Brahmi",
+ numeric: "300",
+ pva: "Brahmi",
+ date: "2010-07-23",
+ },
+ {
+ code: "Brai",
+ name: "Braille",
+ numeric: "570",
+ pva: "Braille",
+ date: "2004-05-01",
+ },
+ {
+ code: "Bugi",
+ name: "Buginese",
+ numeric: "367",
+ pva: "Buginese",
+ date: "2006-06-21",
+ },
+ {
+ code: "Buhd",
+ name: "Buhid",
+ numeric: "372",
+ pva: "Buhid",
+ date: "2004-05-01",
+ },
+ {
+ code: "Cakm",
+ name: "Chakma",
+ numeric: "349",
+ pva: "Chakma",
+ date: "2012-02-06",
+ },
+ {
+ code: "Cans",
+ name: "Unified Canadian Aboriginal Syllabics",
+ numeric: "440",
+ pva: "Canadian_Aboriginal",
+ date: "2004-05-29",
+ },
+ {
+ code: "Cari",
+ name: "Carian",
+ numeric: "201",
+ pva: "Carian",
+ date: "2007-07-02",
+ },
+ {
+ code: "Cham",
+ name: "Cham",
+ numeric: "358",
+ pva: "Cham",
+ date: "2009-11-11",
+ },
+ {
+ code: "Cher",
+ name: "Cherokee",
+ numeric: "445",
+ pva: "Cherokee",
+ date: "2004-05-01",
+ },
+ {
+ code: "Chis",
+ name: "Chisoi",
+ numeric: "298",
+ date: "2023-09-12",
+ },
+ {
+ code: "Chrs",
+ name: "Chorasmian",
+ numeric: "109",
+ pva: "Chorasmian",
+ date: "2019-08-19",
+ },
+ {
+ code: "Cirt",
+ name: "Cirth",
+ numeric: "291",
+ date: "2004-05-01",
+ },
+ {
+ code: "Copt",
+ name: "Coptic",
+ numeric: "204",
+ pva: "Coptic",
+ date: "2006-06-21",
+ },
+ {
+ code: "Cpmn",
+ name: "Cypro-Minoan",
+ numeric: "402",
+ pva: "Cypro_Minoan",
+ date: "2017-07-26",
+ },
+ {
+ code: "Cprt",
+ name: "Cypriot syllabary",
+ numeric: "403",
+ pva: "Cypriot",
+ date: "2017-07-26",
+ },
+ {
+ code: "Cyrl",
+ name: "Cyrillic",
+ numeric: "220",
+ pva: "Cyrillic",
+ date: "2004-05-01",
+ },
+ {
+ code: "Cyrs",
+ name: "Cyrillic (Old Church Slavonic variant)",
+ numeric: "221",
+ date: "2004-05-01",
+ },
+ {
+ code: "Deva",
+ name: "Devanagari (Nagari)",
+ numeric: "315",
+ pva: "Devanagari",
+ date: "2004-05-01",
+ },
+ {
+ code: "Diak",
+ name: "Dives Akuru",
+ numeric: "342",
+ pva: "Dives_Akuru",
+ date: "2019-08-19",
+ },
+ {
+ code: "Dogr",
+ name: "Dogra",
+ numeric: "328",
+ pva: "Dogra",
+ date: "2016-12-05",
+ },
+ {
+ code: "Dsrt",
+ name: "Deseret (Mormon)",
+ numeric: "250",
+ pva: "Deseret",
+ date: "2004-05-01",
+ },
+ {
+ code: "Dupl",
+ name: "Duployan shorthand, Duployan stenography",
+ numeric: "755",
+ pva: "Duployan",
+ date: "2014-11-15",
+ },
+ {
+ code: "Egyd",
+ name: "Egyptian demotic",
+ numeric: "070",
+ date: "2004-05-01",
+ },
+ {
+ code: "Egyh",
+ name: "Egyptian hieratic",
+ numeric: "060",
+ date: "2004-05-01",
+ },
+ {
+ code: "Egyp",
+ name: "Egyptian hieroglyphs",
+ numeric: "050",
+ pva: "Egyptian_Hieroglyphs",
+ date: "2009-06-01",
+ },
+ {
+ code: "Elba",
+ name: "Elbasan",
+ numeric: "226",
+ pva: "Elbasan",
+ date: "2014-11-15",
+ },
+ {
+ code: "Elym",
+ name: "Elymaic",
+ numeric: "128",
+ pva: "Elymaic",
+ date: "2018-08-26",
+ },
+ {
+ code: "Ethi",
+ name: "Ethiopic (Geʻez)",
+ numeric: "430",
+ pva: "Ethiopic",
+ date: "2004-10-25",
+ },
+ {
+ code: "Gara",
+ name: "Garay",
+ numeric: "164",
+ date: "2023-09-12",
+ },
+ {
+ code: "Geok",
+ name: "Khutsuri (Asomtavruli and Nuskhuri)",
+ numeric: "241",
+ pva: "Georgian",
+ date: "2012-10-16",
+ },
+ {
+ code: "Geor",
+ name: "Georgian (Mkhedruli and Mtavruli)",
+ numeric: "240",
+ pva: "Georgian",
+ date: "2016-12-05",
+ },
+ {
+ code: "Glag",
+ name: "Glagolitic",
+ numeric: "225",
+ pva: "Glagolitic",
+ date: "2006-06-21",
+ },
+ {
+ code: "Gong",
+ name: "Gunjala Gondi",
+ numeric: "312",
+ pva: "Gunjala_Gondi",
+ date: "2016-12-05",
+ },
+ {
+ code: "Gonm",
+ name: "Masaram Gondi",
+ numeric: "313",
+ pva: "Masaram_Gondi",
+ date: "2017-07-26",
+ },
+ {
+ code: "Goth",
+ name: "Gothic",
+ numeric: "206",
+ pva: "Gothic",
+ date: "2004-05-01",
+ },
+ {
+ code: "Gran",
+ name: "Grantha",
+ numeric: "343",
+ pva: "Grantha",
+ date: "2014-11-15",
+ },
+ {
+ code: "Grek",
+ name: "Greek",
+ numeric: "200",
+ pva: "Greek",
+ date: "2004-05-01",
+ },
+ {
+ code: "Gujr",
+ name: "Gujarati",
+ numeric: "320",
+ pva: "Gujarati",
+ date: "2004-05-01",
+ },
+ {
+ code: "Gukh",
+ name: "Gurung Khema",
+ numeric: "397",
+ date: "2023-09-12",
+ },
+ {
+ code: "Guru",
+ name: "Gurmukhi",
+ numeric: "310",
+ pva: "Gurmukhi",
+ date: "2004-05-01",
+ },
+ {
+ code: "Hanb",
+ name: "Han with Bopomofo (alias for Han + Bopomofo)",
+ numeric: "503",
+ date: "2016-01-19",
+ },
+ {
+ code: "Hang",
+ name: "Hangul (Hangŭl, Hangeul)",
+ numeric: "286",
+ pva: "Hangul",
+ date: "2004-05-29",
+ },
+ {
+ code: "Hani",
+ name: "Han (Hanzi, Kanji, Hanja)",
+ numeric: "500",
+ pva: "Han",
+ date: "2009-02-23",
+ },
+ {
+ code: "Hano",
+ name: "Hanunoo (Hanunóo)",
+ numeric: "371",
+ pva: "Hanunoo",
+ date: "2004-05-29",
+ },
+ {
+ code: "Hans",
+ name: "Han (Simplified variant)",
+ numeric: "501",
+ date: "2004-05-29",
+ },
+ {
+ code: "Hant",
+ name: "Han (Traditional variant)",
+ numeric: "502",
+ date: "2004-05-29",
+ },
+ {
+ code: "Hatr",
+ name: "Hatran",
+ numeric: "127",
+ pva: "Hatran",
+ date: "2015-07-07",
+ },
+ {
+ code: "Hebr",
+ name: "Hebrew",
+ numeric: "125",
+ pva: "Hebrew",
+ date: "2004-05-01",
+ },
+ {
+ code: "Hira",
+ name: "Hiragana",
+ numeric: "410",
+ pva: "Hiragana",
+ date: "2004-05-01",
+ },
+ {
+ code: "Hluw",
+ name: "Anatolian Hieroglyphs (Luwian Hieroglyphs, Hittite Hieroglyphs)",
+ numeric: "080",
+ pva: "Anatolian_Hieroglyphs",
+ date: "2015-07-07",
+ },
+ {
+ code: "Hmng",
+ name: "Pahawh Hmong",
+ numeric: "450",
+ pva: "Pahawh_Hmong",
+ date: "2014-11-15",
+ },
+ {
+ code: "Hmnp",
+ name: "Nyiakeng Puachue Hmong",
+ numeric: "451",
+ pva: "Nyiakeng_Puachue_Hmong",
+ date: "2017-07-26",
+ },
+ {
+ code: "Hrkt",
+ name: "Japanese syllabaries (alias for Hiragana + Katakana)",
+ numeric: "412",
+ pva: "Katakana_Or_Hiragana",
+ date: "2011-06-21",
+ },
+ {
+ code: "Hung",
+ name: "Old Hungarian (Hungarian Runic)",
+ numeric: "176",
+ pva: "Old_Hungarian",
+ date: "2015-07-07",
+ },
+ {
+ code: "Inds",
+ name: "Indus (Harappan)",
+ numeric: "610",
+ date: "2004-05-01",
+ },
+ {
+ code: "Ital",
+ name: "Old Italic (Etruscan, Oscan, etc.)",
+ numeric: "210",
+ pva: "Old_Italic",
+ date: "2004-05-29",
+ },
+ {
+ code: "Jamo",
+ name: "Jamo (alias for Jamo subset of Hangul)",
+ numeric: "284",
+ date: "2016-01-19",
+ },
+ {
+ code: "Java",
+ name: "Javanese",
+ numeric: "361",
+ pva: "Javanese",
+ date: "2009-06-01",
+ },
+ {
+ code: "Jpan",
+ name: "Japanese (alias for Han + Hiragana + Katakana)",
+ numeric: "413",
+ date: "2006-06-21",
+ },
+ {
+ code: "Jurc",
+ name: "Jurchen",
+ numeric: "510",
+ date: "2010-12-21",
+ },
+ {
+ code: "Kali",
+ name: "Kayah Li",
+ numeric: "357",
+ pva: "Kayah_Li",
+ date: "2007-07-02",
+ },
+ {
+ code: "Kana",
+ name: "Katakana",
+ numeric: "411",
+ pva: "Katakana",
+ date: "2004-05-01",
+ },
+ {
+ code: "Kawi",
+ name: "Kawi",
+ numeric: "368",
+ pva: "Kawi",
+ date: "2021-12-03",
+ },
+ {
+ code: "Khar",
+ name: "Kharoshthi",
+ numeric: "305",
+ pva: "Kharoshthi",
+ date: "2006-06-21",
+ },
+ {
+ code: "Khmr",
+ name: "Khmer",
+ numeric: "355",
+ pva: "Khmer",
+ date: "2004-05-29",
+ },
+ {
+ code: "Khoj",
+ name: "Khojki",
+ numeric: "322",
+ pva: "Khojki",
+ date: "2014-11-15",
+ },
+ {
+ code: "Kitl",
+ name: "Khitan large script",
+ numeric: "505",
+ date: "2015-07-15",
+ },
+ {
+ code: "Kits",
+ name: "Khitan small script",
+ numeric: "288",
+ pva: "Khitan_Small_Script",
+ date: "2015-07-15",
+ },
+ {
+ code: "Knda",
+ name: "Kannada",
+ numeric: "345",
+ pva: "Kannada",
+ date: "2004-05-29",
+ },
+ {
+ code: "Kore",
+ name: "Korean (alias for Hangul + Han)",
+ numeric: "287",
+ date: "2007-06-13",
+ },
+ {
+ code: "Kpel",
+ name: "Kpelle",
+ numeric: "436",
+ date: "2010-03-26",
+ },
+ {
+ code: "Krai",
+ name: "Kirat Rai",
+ numeric: "396",
+ date: "2023-09-12",
+ },
+ {
+ code: "Kthi",
+ name: "Kaithi",
+ numeric: "317",
+ pva: "Kaithi",
+ date: "2009-06-01",
+ },
+ {
+ code: "Lana",
+ name: "Tai Tham (Lanna)",
+ numeric: "351",
+ pva: "Tai_Tham",
+ date: "2009-06-01",
+ },
+ {
+ code: "Laoo",
+ name: "Lao",
+ numeric: "356",
+ pva: "Lao",
+ date: "2004-05-01",
+ },
+ {
+ code: "Latf",
+ name: "Latin (Fraktur variant)",
+ numeric: "217",
+ date: "2004-05-01",
+ },
+ {
+ code: "Latg",
+ name: "Latin (Gaelic variant)",
+ numeric: "216",
+ date: "2004-05-01",
+ },
+ {
+ code: "Latn",
+ name: "Latin",
+ numeric: "215",
+ pva: "Latin",
+ date: "2004-05-01",
+ },
+ {
+ code: "Leke",
+ name: "Leke",
+ numeric: "364",
+ date: "2015-07-07",
+ },
+ {
+ code: "Lepc",
+ name: "Lepcha (Róng)",
+ numeric: "335",
+ pva: "Lepcha",
+ date: "2007-07-02",
+ },
+ {
+ code: "Limb",
+ name: "Limbu",
+ numeric: "336",
+ pva: "Limbu",
+ date: "2004-05-29",
+ },
+ {
+ code: "Lina",
+ name: "Linear A",
+ numeric: "400",
+ pva: "Linear_A",
+ date: "2014-11-15",
+ },
+ {
+ code: "Linb",
+ name: "Linear B",
+ numeric: "401",
+ pva: "Linear_B",
+ date: "2004-05-29",
+ },
+ {
+ code: "Lisu",
+ name: "Lisu (Fraser)",
+ numeric: "399",
+ pva: "Lisu",
+ date: "2009-06-01",
+ },
+ {
+ code: "Loma",
+ name: "Loma",
+ numeric: "437",
+ date: "2010-03-26",
+ },
+ {
+ code: "Lyci",
+ name: "Lycian",
+ numeric: "202",
+ pva: "Lycian",
+ date: "2007-07-02",
+ },
+ {
+ code: "Lydi",
+ name: "Lydian",
+ numeric: "116",
+ pva: "Lydian",
+ date: "2007-07-02",
+ },
+ {
+ code: "Mahj",
+ name: "Mahajani",
+ numeric: "314",
+ pva: "Mahajani",
+ date: "2014-11-15",
+ },
+ {
+ code: "Maka",
+ name: "Makasar",
+ numeric: "366",
+ pva: "Makasar",
+ date: "2016-12-05",
+ },
+ {
+ code: "Mand",
+ name: "Mandaic, Mandaean",
+ numeric: "140",
+ pva: "Mandaic",
+ date: "2010-07-23",
+ },
+ {
+ code: "Mani",
+ name: "Manichaean",
+ numeric: "139",
+ pva: "Manichaean",
+ date: "2014-11-15",
+ },
+ {
+ code: "Marc",
+ name: "Marchen",
+ numeric: "332",
+ pva: "Marchen",
+ date: "2016-12-05",
+ },
+ {
+ code: "Maya",
+ name: "Mayan hieroglyphs",
+ numeric: "090",
+ date: "2004-05-01",
+ },
+ {
+ code: "Medf",
+ name: "Medefaidrin (Oberi Okaime, Oberi Ɔkaimɛ)",
+ numeric: "265",
+ pva: "Medefaidrin",
+ date: "2016-12-05",
+ },
+ {
+ code: "Mend",
+ name: "Mende Kikakui",
+ numeric: "438",
+ pva: "Mende_Kikakui",
+ date: "2014-11-15",
+ },
+ {
+ code: "Merc",
+ name: "Meroitic Cursive",
+ numeric: "101",
+ pva: "Meroitic_Cursive",
+ date: "2012-02-06",
+ },
+ {
+ code: "Mero",
+ name: "Meroitic Hieroglyphs",
+ numeric: "100",
+ pva: "Meroitic_Hieroglyphs",
+ date: "2012-02-06",
+ },
+ {
+ code: "Mlym",
+ name: "Malayalam",
+ numeric: "347",
+ pva: "Malayalam",
+ date: "2004-05-01",
+ },
+ {
+ code: "Modi",
+ name: "Modi, Moḍī",
+ numeric: "324",
+ pva: "Modi",
+ date: "2014-11-15",
+ },
+ {
+ code: "Mong",
+ name: "Mongolian",
+ numeric: "145",
+ pva: "Mongolian",
+ date: "2004-05-01",
+ },
+ {
+ code: "Moon",
+ name: "Moon (Moon code, Moon script, Moon type)",
+ numeric: "218",
+ date: "2006-12-11",
+ },
+ {
+ code: "Mroo",
+ name: "Mro, Mru",
+ numeric: "264",
+ pva: "Mro",
+ date: "2016-12-05",
+ },
+ {
+ code: "Mtei",
+ name: "Meitei Mayek (Meithei, Meetei)",
+ numeric: "337",
+ pva: "Meetei_Mayek",
+ date: "2009-06-01",
+ },
+ {
+ code: "Mult",
+ name: "Multani",
+ numeric: "323",
+ pva: "Multani",
+ date: "2015-07-07",
+ },
+ {
+ code: "Mymr",
+ name: "Myanmar (Burmese)",
+ numeric: "350",
+ pva: "Myanmar",
+ date: "2004-05-01",
+ },
+ {
+ code: "Nagm",
+ name: "Nag Mundari",
+ numeric: "295",
+ pva: "Nag_Mundari",
+ date: "2021-12-03",
+ },
+ {
+ code: "Nand",
+ name: "Nandinagari",
+ numeric: "311",
+ pva: "Nandinagari",
+ date: "2018-08-26",
+ },
+ {
+ code: "Narb",
+ name: "Old North Arabian (Ancient North Arabian)",
+ numeric: "106",
+ pva: "Old_North_Arabian",
+ date: "2014-11-15",
+ },
+ {
+ code: "Nbat",
+ name: "Nabataean",
+ numeric: "159",
+ pva: "Nabataean",
+ date: "2014-11-15",
+ },
+ {
+ code: "Newa",
+ name: "Newa, Newar, Newari, Nepāla lipi",
+ numeric: "333",
+ pva: "Newa",
+ date: "2016-12-05",
+ },
+ {
+ code: "Nkdb",
+ name: "Naxi Dongba (na²¹ɕi³³ to³³ba²¹, Nakhi Tomba)",
+ numeric: "085",
+ date: "2017-07-26",
+ },
+ {
+ code: "Nkgb",
+ name: "Naxi Geba (na²¹ɕi³³ gʌ²¹ba²¹, 'Na-'Khi ²Ggŏ-¹baw, Nakhi Geba)",
+ numeric: "420",
+ date: "2017-07-26",
+ },
+ {
+ code: "Nkoo",
+ name: "N’Ko",
+ numeric: "165",
+ pva: "Nko",
+ date: "2006-10-10",
+ },
+ {
+ code: "Nshu",
+ name: "Nüshu",
+ numeric: "499",
+ pva: "Nushu",
+ date: "2017-07-26",
+ },
+ {
+ code: "Ogam",
+ name: "Ogham",
+ numeric: "212",
+ pva: "Ogham",
+ date: "2004-05-01",
+ },
+ {
+ code: "Olck",
+ name: "Ol Chiki (Ol Cemet’, Ol, Santali)",
+ numeric: "261",
+ pva: "Ol_Chiki",
+ date: "2007-07-02",
+ },
+ {
+ code: "Onao",
+ name: "Ol Onal",
+ numeric: "296",
+ date: "2023-09-12",
+ },
+ {
+ code: "Orkh",
+ name: "Old Turkic, Orkhon Runic",
+ numeric: "175",
+ pva: "Old_Turkic",
+ date: "2009-06-01",
+ },
+ {
+ code: "Orya",
+ name: "Oriya (Odia)",
+ numeric: "327",
+ pva: "Oriya",
+ date: "2016-12-05",
+ },
+ {
+ code: "Osge",
+ name: "Osage",
+ numeric: "219",
+ pva: "Osage",
+ date: "2016-12-05",
+ },
+ {
+ code: "Osma",
+ name: "Osmanya",
+ numeric: "260",
+ pva: "Osmanya",
+ date: "2004-05-01",
+ },
+ {
+ code: "Ougr",
+ name: "Old Uyghur",
+ numeric: "143",
+ pva: "Old_Uyghur",
+ date: "2021-01-25",
+ },
+ {
+ code: "Palm",
+ name: "Palmyrene",
+ numeric: "126",
+ pva: "Palmyrene",
+ date: "2014-11-15",
+ },
+ {
+ code: "Pauc",
+ name: "Pau Cin Hau",
+ numeric: "263",
+ pva: "Pau_Cin_Hau",
+ date: "2014-11-15",
+ },
+ {
+ code: "Pcun",
+ name: "Proto-Cuneiform",
+ numeric: "015",
+ date: "2021-01-25",
+ },
+ {
+ code: "Pelm",
+ name: "Proto-Elamite",
+ numeric: "016",
+ date: "2021-01-25",
+ },
+ {
+ code: "Perm",
+ name: "Old Permic",
+ numeric: "227",
+ pva: "Old_Permic",
+ date: "2014-11-15",
+ },
+ {
+ code: "Phag",
+ name: "Phags-pa",
+ numeric: "331",
+ pva: "Phags_Pa",
+ date: "2006-10-10",
+ },
+ {
+ code: "Phli",
+ name: "Inscriptional Pahlavi",
+ numeric: "131",
+ pva: "Inscriptional_Pahlavi",
+ date: "2009-06-01",
+ },
+ {
+ code: "Phlp",
+ name: "Psalter Pahlavi",
+ numeric: "132",
+ pva: "Psalter_Pahlavi",
+ date: "2014-11-15",
+ },
+ {
+ code: "Phlv",
+ name: "Book Pahlavi",
+ numeric: "133",
+ date: "2007-07-15",
+ },
+ {
+ code: "Phnx",
+ name: "Phoenician",
+ numeric: "115",
+ pva: "Phoenician",
+ date: "2006-10-10",
+ },
+ {
+ code: "Plrd",
+ name: "Miao (Pollard)",
+ numeric: "282",
+ pva: "Miao",
+ date: "2012-02-06",
+ },
+ {
+ code: "Piqd",
+ name: "Klingon (KLI pIqaD)",
+ numeric: "293",
+ date: "2015-12-16",
+ },
+ {
+ code: "Prti",
+ name: "Inscriptional Parthian",
+ numeric: "130",
+ pva: "Inscriptional_Parthian",
+ date: "2009-06-01",
+ },
+ {
+ code: "Psin",
+ name: "Proto-Sinaitic",
+ numeric: "103",
+ date: "2021-01-25",
+ },
+ {
+ code: "Qaaa",
+ name: "Reserved for private use (start)",
+ numeric: "900",
+ date: "2004-05-29",
+ },
+ {
+ code: "Qabx",
+ name: "Reserved for private use (end)",
+ numeric: "949",
+ date: "2004-05-29",
+ },
+ {
+ code: "Ranj",
+ name: "Ranjana",
+ numeric: "303",
+ date: "2021-01-25",
+ },
+ {
+ code: "Rjng",
+ name: "Rejang (Redjang, Kaganga)",
+ numeric: "363",
+ pva: "Rejang",
+ date: "2009-02-23",
+ },
+ {
+ code: "Rohg",
+ name: "Hanifi Rohingya",
+ numeric: "167",
+ pva: "Hanifi_Rohingya",
+ date: "2017-11-21",
+ },
+ {
+ code: "Roro",
+ name: "Rongorongo",
+ numeric: "620",
+ date: "2004-05-01",
+ },
+ {
+ code: "Runr",
+ name: "Runic",
+ numeric: "211",
+ pva: "Runic",
+ date: "2004-05-01",
+ },
+ {
+ code: "Samr",
+ name: "Samaritan",
+ numeric: "123",
+ pva: "Samaritan",
+ date: "2009-06-01",
+ },
+ {
+ code: "Sara",
+ name: "Sarati",
+ numeric: "292",
+ date: "2004-05-29",
+ },
+ {
+ code: "Sarb",
+ name: "Old South Arabian",
+ numeric: "105",
+ pva: "Old_South_Arabian",
+ date: "2009-06-01",
+ },
+ {
+ code: "Saur",
+ name: "Saurashtra",
+ numeric: "344",
+ pva: "Saurashtra",
+ date: "2007-07-02",
+ },
+ {
+ code: "Sgnw",
+ name: "SignWriting",
+ numeric: "095",
+ pva: "SignWriting",
+ date: "2015-07-07",
+ },
+ {
+ code: "Shaw",
+ name: "Shavian (Shaw)",
+ numeric: "281",
+ pva: "Shavian",
+ date: "2004-05-01",
+ },
+ {
+ code: "Shrd",
+ name: "Sharada, Śāradā",
+ numeric: "319",
+ pva: "Sharada",
+ date: "2012-02-06",
+ },
+ {
+ code: "Shui",
+ name: "Shuishu",
+ numeric: "530",
+ date: "2017-07-26",
+ },
+ {
+ code: "Sidd",
+ name: "Siddham, Siddhaṃ, Siddhamātṛkā",
+ numeric: "302",
+ pva: "Siddham",
+ date: "2014-11-15",
+ },
+ {
+ code: "Sidt",
+ name: "Sidetic",
+ numeric: "180",
+ date: "2023-09-12",
+ },
+ {
+ code: "Sind",
+ name: "Khudawadi, Sindhi",
+ numeric: "318",
+ pva: "Khudawadi",
+ date: "2014-11-15",
+ },
+ {
+ code: "Sinh",
+ name: "Sinhala",
+ numeric: "348",
+ pva: "Sinhala",
+ date: "2004-05-01",
+ },
+ {
+ code: "Sogd",
+ name: "Sogdian",
+ numeric: "141",
+ pva: "Sogdian",
+ date: "2017-11-21",
+ },
+ {
+ code: "Sogo",
+ name: "Old Sogdian",
+ numeric: "142",
+ pva: "Old_Sogdian",
+ date: "2017-11-21",
+ },
+ {
+ code: "Sora",
+ name: "Sora Sompeng",
+ numeric: "398",
+ pva: "Sora_Sompeng",
+ date: "2012-02-06",
+ },
+ {
+ code: "Soyo",
+ name: "Soyombo",
+ numeric: "329",
+ pva: "Soyombo",
+ date: "2017-07-26",
+ },
+ {
+ code: "Sund",
+ name: "Sundanese",
+ numeric: "362",
+ pva: "Sundanese",
+ date: "2007-07-02",
+ },
+ {
+ code: "Sunu",
+ name: "Sunuwar",
+ numeric: "274",
+ date: "2021-12-03",
+ },
+ {
+ code: "Sylo",
+ name: "Syloti Nagri",
+ numeric: "316",
+ pva: "Syloti_Nagri",
+ date: "2006-06-21",
+ },
+ {
+ code: "Syrc",
+ name: "Syriac",
+ numeric: "135",
+ pva: "Syriac",
+ date: "2004-05-01",
+ },
+ {
+ code: "Syre",
+ name: "Syriac (Estrangelo variant)",
+ numeric: "138",
+ date: "2004-05-01",
+ },
+ {
+ code: "Syrj",
+ name: "Syriac (Western variant)",
+ numeric: "137",
+ date: "2004-05-01",
+ },
+ {
+ code: "Syrn",
+ name: "Syriac (Eastern variant)",
+ numeric: "136",
+ date: "2004-05-01",
+ },
+ {
+ code: "Tagb",
+ name: "Tagbanwa",
+ numeric: "373",
+ pva: "Tagbanwa",
+ date: "2004-05-01",
+ },
+ {
+ code: "Takr",
+ name: "Takri, Ṭākrī, Ṭāṅkrī",
+ numeric: "321",
+ pva: "Takri",
+ date: "2012-02-06",
+ },
+ {
+ code: "Tale",
+ name: "Tai Le",
+ numeric: "353",
+ pva: "Tai_Le",
+ date: "2004-10-25",
+ },
+ {
+ code: "Talu",
+ name: "New Tai Lue",
+ numeric: "354",
+ pva: "New_Tai_Lue",
+ date: "2006-06-21",
+ },
+ {
+ code: "Taml",
+ name: "Tamil",
+ numeric: "346",
+ pva: "Tamil",
+ date: "2004-05-01",
+ },
+ {
+ code: "Tang",
+ name: "Tangut",
+ numeric: "520",
+ pva: "Tangut",
+ date: "2016-12-05",
+ },
+ {
+ code: "Tavt",
+ name: "Tai Viet",
+ numeric: "359",
+ pva: "Tai_Viet",
+ date: "2009-06-01",
+ },
+ {
+ code: "Tayo",
+ name: "Tai Yo",
+ numeric: "380",
+ date: "2023-09-12",
+ },
+ {
+ code: "Telu",
+ name: "Telugu",
+ numeric: "340",
+ pva: "Telugu",
+ date: "2004-05-01",
+ },
+ {
+ code: "Teng",
+ name: "Tengwar",
+ numeric: "290",
+ date: "2004-05-01",
+ },
+ {
+ code: "Tfng",
+ name: "Tifinagh (Berber)",
+ numeric: "120",
+ pva: "Tifinagh",
+ date: "2006-06-21",
+ },
+ {
+ code: "Tglg",
+ name: "Tagalog (Baybayin, Alibata)",
+ numeric: "370",
+ pva: "Tagalog",
+ date: "2009-02-23",
+ },
+ {
+ code: "Thaa",
+ name: "Thaana",
+ numeric: "170",
+ pva: "Thaana",
+ date: "2004-05-01",
+ },
+ {
+ code: "Thai",
+ name: "Thai",
+ numeric: "352",
+ pva: "Thai",
+ date: "2004-05-01",
+ },
+ {
+ code: "Tibt",
+ name: "Tibetan",
+ numeric: "330",
+ pva: "Tibetan",
+ date: "2004-05-01",
+ },
+ {
+ code: "Tirh",
+ name: "Tirhuta",
+ numeric: "326",
+ pva: "Tirhuta",
+ date: "2014-11-15",
+ },
+ {
+ code: "Tnsa",
+ name: "Tangsa",
+ numeric: "275",
+ pva: "Tangsa",
+ date: "2021-02-17",
+ },
+ {
+ code: "Todr",
+ name: "Todhri",
+ numeric: "229",
+ date: "2023-09-12",
+ },
+ {
+ code: "Tols",
+ name: "Tolong Siki",
+ numeric: "299",
+ date: "2023-09-12",
+ },
+ {
+ code: "Toto",
+ name: "Toto",
+ numeric: "294",
+ pva: "Toto",
+ date: "2020-04-16",
+ },
+ {
+ code: "Tutg",
+ name: "Tulu-Tigalari",
+ numeric: "341",
+ date: "2023-09-12",
+ },
+ {
+ code: "Ugar",
+ name: "Ugaritic",
+ numeric: "040",
+ pva: "Ugaritic",
+ date: "2004-05-01",
+ },
+ {
+ code: "Vaii",
+ name: "Vai",
+ numeric: "470",
+ pva: "Vai",
+ date: "2007-07-02",
+ },
+ {
+ code: "Visp",
+ name: "Visible Speech",
+ numeric: "280",
+ date: "2004-05-01",
+ },
+ {
+ code: "Vith",
+ name: "Vithkuqi",
+ numeric: "228",
+ pva: "Vithkuqi",
+ date: "2021-02-17",
+ },
+ {
+ code: "Wara",
+ name: "Warang Citi (Varang Kshiti)",
+ numeric: "262",
+ pva: "Warang_Citi",
+ date: "2014-11-15",
+ },
+ {
+ code: "Wcho",
+ name: "Wancho",
+ numeric: "283",
+ pva: "Wancho",
+ date: "2017-07-26",
+ },
+ {
+ code: "Wole",
+ name: "Woleai",
+ numeric: "480",
+ date: "2010-12-21",
+ },
+ {
+ code: "Xpeo",
+ name: "Old Persian",
+ numeric: "030",
+ pva: "Old_Persian",
+ date: "2006-06-21",
+ },
+ {
+ code: "Xsux",
+ name: "Cuneiform, Sumero-Akkadian",
+ numeric: "020",
+ pva: "Cuneiform",
+ date: "2006-10-10",
+ },
+ {
+ code: "Yezi",
+ name: "Yezidi",
+ numeric: "192",
+ pva: "Yezidi",
+ date: "2019-08-19",
+ },
+ {
+ code: "Yiii",
+ name: "Yi",
+ numeric: "460",
+ pva: "Yi",
+ date: "2004-05-01",
+ },
+ {
+ code: "Zanb",
+ name: "Zanabazar Square (Zanabazarin Dörböljin Useg, Xewtee Dörböljin Bicig, Horizontal Square Script)",
+ numeric: "339",
+ pva: "Zanabazar_Square",
+ date: "2017-07-26",
+ },
+ {
+ code: "Zinh",
+ name: "Code for inherited script",
+ numeric: "994",
+ pva: "Inherited",
+ date: "2009-02-23",
+ },
+ {
+ code: "Zmth",
+ name: "Mathematical notation",
+ numeric: "995",
+ date: "2007-11-26",
+ },
+ {
+ code: "Zsye",
+ name: "Symbols (Emoji variant)",
+ numeric: "993",
+ date: "2015-12-16",
+ },
+ {
+ code: "Zsym",
+ name: "Symbols",
+ numeric: "996",
+ date: "2007-11-26",
+ },
+ {
+ code: "Zxxx",
+ name: "Code for unwritten documents",
+ numeric: "997",
+ date: "2011-06-21",
+ },
+ {
+ code: "Zyyy",
+ name: "Code for undetermined script",
+ numeric: "998",
+ pva: "Common",
+ date: "2004-05-29",
+ },
+ {
+ code: "Zzzz",
+ name: "Code for uncoded script",
+ numeric: "999",
+ pva: "Unknown",
+ date: "2006-10-10",
+ },
+] as const;
+
+export type ISO_15924_CODE = (typeof iso15924)[number]["code"];
diff --git a/packages/lang/src/iso/iso6393-to-1.js b/packages/lang/src/iso/iso6393-to-1.js
new file mode 100644
index 0000000..ed818f9
--- /dev/null
+++ b/packages/lang/src/iso/iso6393-to-1.js
@@ -0,0 +1,192 @@
+
+/**
+ * Map of ISO 639-3 codes to ISO 639-1 codes.
+ *
+ * @type {Record<string, string>}
+ */
+export const iso6393To1 = {
+ aar: 'aa',
+ abk: 'ab',
+ afr: 'af',
+ aka: 'ak',
+ amh: 'am',
+ ara: 'ar',
+ arg: 'an',
+ asm: 'as',
+ ava: 'av',
+ ave: 'ae',
+ aym: 'ay',
+ aze: 'az',
+ bak: 'ba',
+ bam: 'bm',
+ bel: 'be',
+ ben: 'bn',
+ bis: 'bi',
+ bod: 'bo',
+ bos: 'bs',
+ bre: 'br',
+ bul: 'bg',
+ cat: 'ca',
+ ces: 'cs',
+ cha: 'ch',
+ che: 'ce',
+ chu: 'cu',
+ chv: 'cv',
+ cor: 'kw',
+ cos: 'co',
+ cre: 'cr',
+ cym: 'cy',
+ dan: 'da',
+ deu: 'de',
+ div: 'dv',
+ dzo: 'dz',
+ ell: 'el',
+ eng: 'en',
+ epo: 'eo',
+ est: 'et',
+ eus: 'eu',
+ ewe: 'ee',
+ fao: 'fo',
+ fas: 'fa',
+ fij: 'fj',
+ fin: 'fi',
+ fra: 'fr',
+ fry: 'fy',
+ ful: 'ff',
+ gla: 'gd',
+ gle: 'ga',
+ glg: 'gl',
+ glv: 'gv',
+ grn: 'gn',
+ guj: 'gu',
+ hat: 'ht',
+ hau: 'ha',
+ hbs: 'sh',
+ heb: 'he',
+ her: 'hz',
+ hin: 'hi',
+ hmo: 'ho',
+ hrv: 'hr',
+ hun: 'hu',
+ hye: 'hy',
+ ibo: 'ig',
+ ido: 'io',
+ iii: 'ii',
+ iku: 'iu',
+ ile: 'ie',
+ ina: 'ia',
+ ind: 'id',
+ ipk: 'ik',
+ isl: 'is',
+ ita: 'it',
+ jav: 'jv',
+ jpn: 'ja',
+ kal: 'kl',
+ kan: 'kn',
+ kas: 'ks',
+ kat: 'ka',
+ kau: 'kr',
+ kaz: 'kk',
+ khm: 'km',
+ kik: 'ki',
+ kin: 'rw',
+ kir: 'ky',
+ kom: 'kv',
+ kon: 'kg',
+ kor: 'ko',
+ kua: 'kj',
+ kur: 'ku',
+ lao: 'lo',
+ lat: 'la',
+ lav: 'lv',
+ lim: 'li',
+ lin: 'ln',
+ lit: 'lt',
+ ltz: 'lb',
+ lub: 'lu',
+ lug: 'lg',
+ mah: 'mh',
+ mal: 'ml',
+ mar: 'mr',
+ mkd: 'mk',
+ mlg: 'mg',
+ mlt: 'mt',
+ mon: 'mn',
+ mri: 'mi',
+ msa: 'ms',
+ mya: 'my',
+ nau: 'na',
+ nav: 'nv',
+ nbl: 'nr',
+ nde: 'nd',
+ ndo: 'ng',
+ nep: 'ne',
+ nld: 'nl',
+ nno: 'nn',
+ nob: 'nb',
+ nor: 'no',
+ nya: 'ny',
+ oci: 'oc',
+ oji: 'oj',
+ ori: 'or',
+ orm: 'om',
+ oss: 'os',
+ pan: 'pa',
+ pli: 'pi',
+ pol: 'pl',
+ por: 'pt',
+ pus: 'ps',
+ que: 'qu',
+ roh: 'rm',
+ ron: 'ro',
+ run: 'rn',
+ rus: 'ru',
+ sag: 'sg',
+ san: 'sa',
+ sin: 'si',
+ slk: 'sk',
+ slv: 'sl',
+ sme: 'se',
+ smo: 'sm',
+ sna: 'sn',
+ snd: 'sd',
+ som: 'so',
+ sot: 'st',
+ spa: 'es',
+ sqi: 'sq',
+ srd: 'sc',
+ srp: 'sr',
+ ssw: 'ss',
+ sun: 'su',
+ swa: 'sw',
+ swe: 'sv',
+ tah: 'ty',
+ tam: 'ta',
+ tat: 'tt',
+ tel: 'te',
+ tgk: 'tg',
+ tgl: 'tl',
+ tha: 'th',
+ tir: 'ti',
+ ton: 'to',
+ tsn: 'tn',
+ tso: 'ts',
+ tuk: 'tk',
+ tur: 'tr',
+ twi: 'tw',
+ uig: 'ug',
+ ukr: 'uk',
+ urd: 'ur',
+ uzb: 'uz',
+ ven: 've',
+ vie: 'vi',
+ vol: 'vo',
+ wln: 'wa',
+ wol: 'wo',
+ xho: 'xh',
+ yid: 'yi',
+ yor: 'yo',
+ zha: 'za',
+ zho: 'zh',
+ zul: 'zu'
+}
diff --git a/packages/lang/src/iso/iso6393-to-2b.js b/packages/lang/src/iso/iso6393-to-2b.js
new file mode 100644
index 0000000..c65f0a1
--- /dev/null
+++ b/packages/lang/src/iso/iso6393-to-2b.js
@@ -0,0 +1,428 @@
+
+/**
+ * Map of ISO 639-3 codes to bibliographic ISO 639-2 codes.
+ *
+ * @type {Record<string, string>}
+ */
+export const iso6393To2B = {
+ aar: 'aar',
+ abk: 'abk',
+ ace: 'ace',
+ ach: 'ach',
+ ada: 'ada',
+ ady: 'ady',
+ afh: 'afh',
+ afr: 'afr',
+ ain: 'ain',
+ aka: 'aka',
+ akk: 'akk',
+ ale: 'ale',
+ alt: 'alt',
+ amh: 'amh',
+ ang: 'ang',
+ anp: 'anp',
+ ara: 'ara',
+ arc: 'arc',
+ arg: 'arg',
+ arn: 'arn',
+ arp: 'arp',
+ arw: 'arw',
+ asm: 'asm',
+ ast: 'ast',
+ ava: 'ava',
+ ave: 'ave',
+ awa: 'awa',
+ aym: 'aym',
+ aze: 'aze',
+ bak: 'bak',
+ bal: 'bal',
+ bam: 'bam',
+ ban: 'ban',
+ bas: 'bas',
+ bej: 'bej',
+ bel: 'bel',
+ bem: 'bem',
+ ben: 'ben',
+ bho: 'bho',
+ bik: 'bik',
+ bin: 'bin',
+ bis: 'bis',
+ bla: 'bla',
+ bod: 'tib',
+ bos: 'bos',
+ bra: 'bra',
+ bre: 'bre',
+ bua: 'bua',
+ bug: 'bug',
+ bul: 'bul',
+ byn: 'byn',
+ cad: 'cad',
+ car: 'car',
+ cat: 'cat',
+ ceb: 'ceb',
+ ces: 'cze',
+ cha: 'cha',
+ chb: 'chb',
+ che: 'che',
+ chg: 'chg',
+ chk: 'chk',
+ chm: 'chm',
+ chn: 'chn',
+ cho: 'cho',
+ chp: 'chp',
+ chr: 'chr',
+ chu: 'chu',
+ chv: 'chv',
+ chy: 'chy',
+ cnr: 'cnr',
+ cop: 'cop',
+ cor: 'cor',
+ cos: 'cos',
+ cre: 'cre',
+ crh: 'crh',
+ csb: 'csb',
+ cym: 'wel',
+ dak: 'dak',
+ dan: 'dan',
+ dar: 'dar',
+ del: 'del',
+ den: 'den',
+ deu: 'ger',
+ dgr: 'dgr',
+ din: 'din',
+ div: 'div',
+ doi: 'doi',
+ dsb: 'dsb',
+ dua: 'dua',
+ dum: 'dum',
+ dyu: 'dyu',
+ dzo: 'dzo',
+ efi: 'efi',
+ egy: 'egy',
+ eka: 'eka',
+ ell: 'gre',
+ elx: 'elx',
+ eng: 'eng',
+ enm: 'enm',
+ epo: 'epo',
+ est: 'est',
+ eus: 'baq',
+ ewe: 'ewe',
+ ewo: 'ewo',
+ fan: 'fan',
+ fao: 'fao',
+ fas: 'per',
+ fat: 'fat',
+ fij: 'fij',
+ fil: 'fil',
+ fin: 'fin',
+ fon: 'fon',
+ fra: 'fre',
+ frm: 'frm',
+ fro: 'fro',
+ frr: 'frr',
+ frs: 'frs',
+ fry: 'fry',
+ ful: 'ful',
+ fur: 'fur',
+ gaa: 'gaa',
+ gay: 'gay',
+ gba: 'gba',
+ gez: 'gez',
+ gil: 'gil',
+ gla: 'gla',
+ gle: 'gle',
+ glg: 'glg',
+ glv: 'glv',
+ gmh: 'gmh',
+ goh: 'goh',
+ gon: 'gon',
+ gor: 'gor',
+ got: 'got',
+ grb: 'grb',
+ grc: 'grc',
+ grn: 'grn',
+ gsw: 'gsw',
+ guj: 'guj',
+ gwi: 'gwi',
+ hai: 'hai',
+ hat: 'hat',
+ hau: 'hau',
+ haw: 'haw',
+ heb: 'heb',
+ her: 'her',
+ hil: 'hil',
+ hin: 'hin',
+ hit: 'hit',
+ hmn: 'hmn',
+ hmo: 'hmo',
+ hrv: 'hrv',
+ hsb: 'hsb',
+ hun: 'hun',
+ hup: 'hup',
+ hye: 'arm',
+ iba: 'iba',
+ ibo: 'ibo',
+ ido: 'ido',
+ iii: 'iii',
+ iku: 'iku',
+ ile: 'ile',
+ ilo: 'ilo',
+ ina: 'ina',
+ ind: 'ind',
+ inh: 'inh',
+ ipk: 'ipk',
+ isl: 'ice',
+ ita: 'ita',
+ jav: 'jav',
+ jbo: 'jbo',
+ jpn: 'jpn',
+ jpr: 'jpr',
+ jrb: 'jrb',
+ kaa: 'kaa',
+ kab: 'kab',
+ kac: 'kac',
+ kal: 'kal',
+ kam: 'kam',
+ kan: 'kan',
+ kas: 'kas',
+ kat: 'geo',
+ kau: 'kau',
+ kaw: 'kaw',
+ kaz: 'kaz',
+ kbd: 'kbd',
+ kha: 'kha',
+ khm: 'khm',
+ kho: 'kho',
+ kik: 'kik',
+ kin: 'kin',
+ kir: 'kir',
+ kmb: 'kmb',
+ kok: 'kok',
+ kom: 'kom',
+ kon: 'kon',
+ kor: 'kor',
+ kos: 'kos',
+ kpe: 'kpe',
+ krc: 'krc',
+ krl: 'krl',
+ kru: 'kru',
+ kua: 'kua',
+ kum: 'kum',
+ kur: 'kur',
+ kut: 'kut',
+ lad: 'lad',
+ lah: 'lah',
+ lam: 'lam',
+ lao: 'lao',
+ lat: 'lat',
+ lav: 'lav',
+ lez: 'lez',
+ lim: 'lim',
+ lin: 'lin',
+ lit: 'lit',
+ lol: 'lol',
+ loz: 'loz',
+ ltz: 'ltz',
+ lua: 'lua',
+ lub: 'lub',
+ lug: 'lug',
+ lui: 'lui',
+ lun: 'lun',
+ luo: 'luo',
+ lus: 'lus',
+ mad: 'mad',
+ mag: 'mag',
+ mah: 'mah',
+ mai: 'mai',
+ mak: 'mak',
+ mal: 'mal',
+ man: 'man',
+ mar: 'mar',
+ mas: 'mas',
+ mdf: 'mdf',
+ mdr: 'mdr',
+ men: 'men',
+ mga: 'mga',
+ mic: 'mic',
+ min: 'min',
+ mis: 'mis',
+ mkd: 'mac',
+ mlg: 'mlg',
+ mlt: 'mlt',
+ mnc: 'mnc',
+ mni: 'mni',
+ moh: 'moh',
+ mon: 'mon',
+ mos: 'mos',
+ mri: 'mao',
+ msa: 'may',
+ mul: 'mul',
+ mus: 'mus',
+ mwl: 'mwl',
+ mwr: 'mwr',
+ mya: 'bur',
+ myv: 'myv',
+ nap: 'nap',
+ nau: 'nau',
+ nav: 'nav',
+ nbl: 'nbl',
+ nde: 'nde',
+ ndo: 'ndo',
+ nds: 'nds',
+ nep: 'nep',
+ new: 'new',
+ nia: 'nia',
+ niu: 'niu',
+ nld: 'dut',
+ nno: 'nno',
+ nob: 'nob',
+ nog: 'nog',
+ non: 'non',
+ nor: 'nor',
+ nqo: 'nqo',
+ nso: 'nso',
+ nwc: 'nwc',
+ nya: 'nya',
+ nym: 'nym',
+ nyn: 'nyn',
+ nyo: 'nyo',
+ nzi: 'nzi',
+ oci: 'oci',
+ oji: 'oji',
+ ori: 'ori',
+ orm: 'orm',
+ osa: 'osa',
+ oss: 'oss',
+ ota: 'ota',
+ pag: 'pag',
+ pal: 'pal',
+ pam: 'pam',
+ pan: 'pan',
+ pap: 'pap',
+ pau: 'pau',
+ peo: 'peo',
+ phn: 'phn',
+ pli: 'pli',
+ pol: 'pol',
+ pon: 'pon',
+ por: 'por',
+ pro: 'pro',
+ pus: 'pus',
+ que: 'que',
+ raj: 'raj',
+ rap: 'rap',
+ rar: 'rar',
+ roh: 'roh',
+ rom: 'rom',
+ ron: 'rum',
+ run: 'run',
+ rup: 'rup',
+ rus: 'rus',
+ sad: 'sad',
+ sag: 'sag',
+ sah: 'sah',
+ sam: 'sam',
+ san: 'san',
+ sas: 'sas',
+ sat: 'sat',
+ scn: 'scn',
+ sco: 'sco',
+ sel: 'sel',
+ sga: 'sga',
+ shn: 'shn',
+ sid: 'sid',
+ sin: 'sin',
+ slk: 'slo',
+ slv: 'slv',
+ sma: 'sma',
+ sme: 'sme',
+ smj: 'smj',
+ smn: 'smn',
+ smo: 'smo',
+ sms: 'sms',
+ sna: 'sna',
+ snd: 'snd',
+ snk: 'snk',
+ sog: 'sog',
+ som: 'som',
+ sot: 'sot',
+ spa: 'spa',
+ sqi: 'alb',
+ srd: 'srd',
+ srn: 'srn',
+ srp: 'srp',
+ srr: 'srr',
+ ssw: 'ssw',
+ suk: 'suk',
+ sun: 'sun',
+ sus: 'sus',
+ sux: 'sux',
+ swa: 'swa',
+ swe: 'swe',
+ syc: 'syc',
+ syr: 'syr',
+ tah: 'tah',
+ tam: 'tam',
+ tat: 'tat',
+ tel: 'tel',
+ tem: 'tem',
+ ter: 'ter',
+ tet: 'tet',
+ tgk: 'tgk',
+ tgl: 'tgl',
+ tha: 'tha',
+ tig: 'tig',
+ tir: 'tir',
+ tiv: 'tiv',
+ tkl: 'tkl',
+ tlh: 'tlh',
+ tli: 'tli',
+ tmh: 'tmh',
+ tog: 'tog',
+ ton: 'ton',
+ tpi: 'tpi',
+ tsi: 'tsi',
+ tsn: 'tsn',
+ tso: 'tso',
+ tuk: 'tuk',
+ tum: 'tum',
+ tur: 'tur',
+ tvl: 'tvl',
+ twi: 'twi',
+ tyv: 'tyv',
+ udm: 'udm',
+ uga: 'uga',
+ uig: 'uig',
+ ukr: 'ukr',
+ umb: 'umb',
+ und: 'und',
+ urd: 'urd',
+ uzb: 'uzb',
+ vai: 'vai',
+ ven: 'ven',
+ vie: 'vie',
+ vol: 'vol',
+ vot: 'vot',
+ wal: 'wal',
+ war: 'war',
+ was: 'was',
+ wln: 'wln',
+ wol: 'wol',
+ xal: 'xal',
+ xho: 'xho',
+ yao: 'yao',
+ yap: 'yap',
+ yid: 'yid',
+ yor: 'yor',
+ zap: 'zap',
+ zbl: 'zbl',
+ zen: 'zen',
+ zgh: 'zgh',
+ zha: 'zha',
+ zho: 'chi',
+ zul: 'zul',
+ zun: 'zun',
+ zxx: 'zxx',
+ zza: 'zza'
+}
diff --git a/packages/lang/src/iso/iso6393-to-2t.js b/packages/lang/src/iso/iso6393-to-2t.js
new file mode 100644
index 0000000..ab84590
--- /dev/null
+++ b/packages/lang/src/iso/iso6393-to-2t.js
@@ -0,0 +1,427 @@
+/**
+ * Map of ISO 639-3 codes to terminologic ISO 639-2 codes.
+ *
+ * @type {Record<string, string>}
+ */
+export const iso6393To2T = {
+ aar: 'aar',
+ abk: 'abk',
+ ace: 'ace',
+ ach: 'ach',
+ ada: 'ada',
+ ady: 'ady',
+ afh: 'afh',
+ afr: 'afr',
+ ain: 'ain',
+ aka: 'aka',
+ akk: 'akk',
+ ale: 'ale',
+ alt: 'alt',
+ amh: 'amh',
+ ang: 'ang',
+ anp: 'anp',
+ ara: 'ara',
+ arc: 'arc',
+ arg: 'arg',
+ arn: 'arn',
+ arp: 'arp',
+ arw: 'arw',
+ asm: 'asm',
+ ast: 'ast',
+ ava: 'ava',
+ ave: 'ave',
+ awa: 'awa',
+ aym: 'aym',
+ aze: 'aze',
+ bak: 'bak',
+ bal: 'bal',
+ bam: 'bam',
+ ban: 'ban',
+ bas: 'bas',
+ bej: 'bej',
+ bel: 'bel',
+ bem: 'bem',
+ ben: 'ben',
+ bho: 'bho',
+ bik: 'bik',
+ bin: 'bin',
+ bis: 'bis',
+ bla: 'bla',
+ bod: 'bod',
+ bos: 'bos',
+ bra: 'bra',
+ bre: 'bre',
+ bua: 'bua',
+ bug: 'bug',
+ bul: 'bul',
+ byn: 'byn',
+ cad: 'cad',
+ car: 'car',
+ cat: 'cat',
+ ceb: 'ceb',
+ ces: 'ces',
+ cha: 'cha',
+ chb: 'chb',
+ che: 'che',
+ chg: 'chg',
+ chk: 'chk',
+ chm: 'chm',
+ chn: 'chn',
+ cho: 'cho',
+ chp: 'chp',
+ chr: 'chr',
+ chu: 'chu',
+ chv: 'chv',
+ chy: 'chy',
+ cnr: 'cnr',
+ cop: 'cop',
+ cor: 'cor',
+ cos: 'cos',
+ cre: 'cre',
+ crh: 'crh',
+ csb: 'csb',
+ cym: 'cym',
+ dak: 'dak',
+ dan: 'dan',
+ dar: 'dar',
+ del: 'del',
+ den: 'den',
+ deu: 'deu',
+ dgr: 'dgr',
+ din: 'din',
+ div: 'div',
+ doi: 'doi',
+ dsb: 'dsb',
+ dua: 'dua',
+ dum: 'dum',
+ dyu: 'dyu',
+ dzo: 'dzo',
+ efi: 'efi',
+ egy: 'egy',
+ eka: 'eka',
+ ell: 'ell',
+ elx: 'elx',
+ eng: 'eng',
+ enm: 'enm',
+ epo: 'epo',
+ est: 'est',
+ eus: 'eus',
+ ewe: 'ewe',
+ ewo: 'ewo',
+ fan: 'fan',
+ fao: 'fao',
+ fas: 'fas',
+ fat: 'fat',
+ fij: 'fij',
+ fil: 'fil',
+ fin: 'fin',
+ fon: 'fon',
+ fra: 'fra',
+ frm: 'frm',
+ fro: 'fro',
+ frr: 'frr',
+ frs: 'frs',
+ fry: 'fry',
+ ful: 'ful',
+ fur: 'fur',
+ gaa: 'gaa',
+ gay: 'gay',
+ gba: 'gba',
+ gez: 'gez',
+ gil: 'gil',
+ gla: 'gla',
+ gle: 'gle',
+ glg: 'glg',
+ glv: 'glv',
+ gmh: 'gmh',
+ goh: 'goh',
+ gon: 'gon',
+ gor: 'gor',
+ got: 'got',
+ grb: 'grb',
+ grc: 'grc',
+ grn: 'grn',
+ gsw: 'gsw',
+ guj: 'guj',
+ gwi: 'gwi',
+ hai: 'hai',
+ hat: 'hat',
+ hau: 'hau',
+ haw: 'haw',
+ heb: 'heb',
+ her: 'her',
+ hil: 'hil',
+ hin: 'hin',
+ hit: 'hit',
+ hmn: 'hmn',
+ hmo: 'hmo',
+ hrv: 'hrv',
+ hsb: 'hsb',
+ hun: 'hun',
+ hup: 'hup',
+ hye: 'hye',
+ iba: 'iba',
+ ibo: 'ibo',
+ ido: 'ido',
+ iii: 'iii',
+ iku: 'iku',
+ ile: 'ile',
+ ilo: 'ilo',
+ ina: 'ina',
+ ind: 'ind',
+ inh: 'inh',
+ ipk: 'ipk',
+ isl: 'isl',
+ ita: 'ita',
+ jav: 'jav',
+ jbo: 'jbo',
+ jpn: 'jpn',
+ jpr: 'jpr',
+ jrb: 'jrb',
+ kaa: 'kaa',
+ kab: 'kab',
+ kac: 'kac',
+ kal: 'kal',
+ kam: 'kam',
+ kan: 'kan',
+ kas: 'kas',
+ kat: 'kat',
+ kau: 'kau',
+ kaw: 'kaw',
+ kaz: 'kaz',
+ kbd: 'kbd',
+ kha: 'kha',
+ khm: 'khm',
+ kho: 'kho',
+ kik: 'kik',
+ kin: 'kin',
+ kir: 'kir',
+ kmb: 'kmb',
+ kok: 'kok',
+ kom: 'kom',
+ kon: 'kon',
+ kor: 'kor',
+ kos: 'kos',
+ kpe: 'kpe',
+ krc: 'krc',
+ krl: 'krl',
+ kru: 'kru',
+ kua: 'kua',
+ kum: 'kum',
+ kur: 'kur',
+ kut: 'kut',
+ lad: 'lad',
+ lah: 'lah',
+ lam: 'lam',
+ lao: 'lao',
+ lat: 'lat',
+ lav: 'lav',
+ lez: 'lez',
+ lim: 'lim',
+ lin: 'lin',
+ lit: 'lit',
+ lol: 'lol',
+ loz: 'loz',
+ ltz: 'ltz',
+ lua: 'lua',
+ lub: 'lub',
+ lug: 'lug',
+ lui: 'lui',
+ lun: 'lun',
+ luo: 'luo',
+ lus: 'lus',
+ mad: 'mad',
+ mag: 'mag',
+ mah: 'mah',
+ mai: 'mai',
+ mak: 'mak',
+ mal: 'mal',
+ man: 'man',
+ mar: 'mar',
+ mas: 'mas',
+ mdf: 'mdf',
+ mdr: 'mdr',
+ men: 'men',
+ mga: 'mga',
+ mic: 'mic',
+ min: 'min',
+ mis: 'mis',
+ mkd: 'mkd',
+ mlg: 'mlg',
+ mlt: 'mlt',
+ mnc: 'mnc',
+ mni: 'mni',
+ moh: 'moh',
+ mon: 'mon',
+ mos: 'mos',
+ mri: 'mri',
+ msa: 'msa',
+ mul: 'mul',
+ mus: 'mus',
+ mwl: 'mwl',
+ mwr: 'mwr',
+ mya: 'mya',
+ myv: 'myv',
+ nap: 'nap',
+ nau: 'nau',
+ nav: 'nav',
+ nbl: 'nbl',
+ nde: 'nde',
+ ndo: 'ndo',
+ nds: 'nds',
+ nep: 'nep',
+ new: 'new',
+ nia: 'nia',
+ niu: 'niu',
+ nld: 'nld',
+ nno: 'nno',
+ nob: 'nob',
+ nog: 'nog',
+ non: 'non',
+ nor: 'nor',
+ nqo: 'nqo',
+ nso: 'nso',
+ nwc: 'nwc',
+ nya: 'nya',
+ nym: 'nym',
+ nyn: 'nyn',
+ nyo: 'nyo',
+ nzi: 'nzi',
+ oci: 'oci',
+ oji: 'oji',
+ ori: 'ori',
+ orm: 'orm',
+ osa: 'osa',
+ oss: 'oss',
+ ota: 'ota',
+ pag: 'pag',
+ pal: 'pal',
+ pam: 'pam',
+ pan: 'pan',
+ pap: 'pap',
+ pau: 'pau',
+ peo: 'peo',
+ phn: 'phn',
+ pli: 'pli',
+ pol: 'pol',
+ pon: 'pon',
+ por: 'por',
+ pro: 'pro',
+ pus: 'pus',
+ que: 'que',
+ raj: 'raj',
+ rap: 'rap',
+ rar: 'rar',
+ roh: 'roh',
+ rom: 'rom',
+ ron: 'ron',
+ run: 'run',
+ rup: 'rup',
+ rus: 'rus',
+ sad: 'sad',
+ sag: 'sag',
+ sah: 'sah',
+ sam: 'sam',
+ san: 'san',
+ sas: 'sas',
+ sat: 'sat',
+ scn: 'scn',
+ sco: 'sco',
+ sel: 'sel',
+ sga: 'sga',
+ shn: 'shn',
+ sid: 'sid',
+ sin: 'sin',
+ slk: 'slk',
+ slv: 'slv',
+ sma: 'sma',
+ sme: 'sme',
+ smj: 'smj',
+ smn: 'smn',
+ smo: 'smo',
+ sms: 'sms',
+ sna: 'sna',
+ snd: 'snd',
+ snk: 'snk',
+ sog: 'sog',
+ som: 'som',
+ sot: 'sot',
+ spa: 'spa',
+ sqi: 'sqi',
+ srd: 'srd',
+ srn: 'srn',
+ srp: 'srp',
+ srr: 'srr',
+ ssw: 'ssw',
+ suk: 'suk',
+ sun: 'sun',
+ sus: 'sus',
+ sux: 'sux',
+ swa: 'swa',
+ swe: 'swe',
+ syc: 'syc',
+ syr: 'syr',
+ tah: 'tah',
+ tam: 'tam',
+ tat: 'tat',
+ tel: 'tel',
+ tem: 'tem',
+ ter: 'ter',
+ tet: 'tet',
+ tgk: 'tgk',
+ tgl: 'tgl',
+ tha: 'tha',
+ tig: 'tig',
+ tir: 'tir',
+ tiv: 'tiv',
+ tkl: 'tkl',
+ tlh: 'tlh',
+ tli: 'tli',
+ tmh: 'tmh',
+ tog: 'tog',
+ ton: 'ton',
+ tpi: 'tpi',
+ tsi: 'tsi',
+ tsn: 'tsn',
+ tso: 'tso',
+ tuk: 'tuk',
+ tum: 'tum',
+ tur: 'tur',
+ tvl: 'tvl',
+ twi: 'twi',
+ tyv: 'tyv',
+ udm: 'udm',
+ uga: 'uga',
+ uig: 'uig',
+ ukr: 'ukr',
+ umb: 'umb',
+ und: 'und',
+ urd: 'urd',
+ uzb: 'uzb',
+ vai: 'vai',
+ ven: 'ven',
+ vie: 'vie',
+ vol: 'vol',
+ vot: 'vot',
+ wal: 'wal',
+ war: 'war',
+ was: 'was',
+ wln: 'wln',
+ wol: 'wol',
+ xal: 'xal',
+ xho: 'xho',
+ yao: 'yao',
+ yap: 'yap',
+ yid: 'yid',
+ yor: 'yor',
+ zap: 'zap',
+ zbl: 'zbl',
+ zen: 'zen',
+ zgh: 'zgh',
+ zha: 'zha',
+ zho: 'zho',
+ zul: 'zul',
+ zun: 'zun',
+ zxx: 'zxx',
+ zza: 'zza'
+}
diff --git a/packages/lang/src/iso/iso6393.js b/packages/lang/src/iso/iso6393.js
new file mode 100644
index 0000000..6e728de
--- /dev/null
+++ b/packages/lang/src/iso/iso6393.js
@@ -0,0 +1,48289 @@
+
+/**
+ * @typedef {'living'|'historical'|'extinct'|'ancient'|'constructed'|'special'} Type
+ * Category of a language:
+ *
+ * * `'living'`
+ * — currently spoken language
+ * (example: `nhi` for `Zacatlán-Ahuacatlán-Tepetzintla Nahuatl`)
+ * * `'historical'`
+ * — extinct language distinct from modern languages that descended from it
+ * (example: `ofs` for `Old Frisian`)
+ * * `'extinct'`
+ * — language that went extinct recently
+ * (example: `rbp` for `Barababaraba`)
+ * * `'ancient'`
+ * — language that went extinct long ago
+ * (example: `got` for `Gothic`)
+ * * `'constructed'`
+ * — artificial languages, excluding programming languages
+ * (example: `epo` for `Esperanto`)
+ * * `'special'`
+ * — non-language codes
+ * (example: `und` for `Undetermined`)
+ *
+ * @typedef {'individual'|'macrolanguage'|'special'} Scope
+ * Scope of a language:
+ *
+ * * `'individual'`
+ * — normal, single language
+ * (example: `eng` for `English`)
+ * * `'macrolanguage'`
+ * — one-to-many grouping of languages, because older ISO 639s included them
+ * (example: `ara` for `Arabic`)
+ * * `'special'`
+ * — non-language codes
+ * (example: `und` for `Undetermined`)
+ *
+ * @typedef Language
+ * Object representing a language.
+ * @property {string} name
+ * Name (example: `'English'`).
+ * @property {Type} type
+ * Type (example: `'living'`).
+ * @property {Scope} scope
+ * Scope (example: `'individual'`)
+ * @property {string} iso6393
+ * ISO 639-3 code.
+ * @property {string} [iso6392B]
+ * ISO 639-2 (bibliographic) code (example: `'eng'`).
+ * @property {string} [iso6392T]
+ * ISO 639-2 (terminologic) code (example: `'eng'`).
+ * @property {string} [iso6391]
+ * ISO 639-1 code (example: `'en'`).
+ */
+
+/**
+ * List of ISO 639-3 languages.
+ *
+ * @type {Array<Language>}
+ */
+// @ts-expect-error
+export const iso6393 = [
+ {
+ name: 'Ghotuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aaa'
+ },
+ {
+ name: 'Alumu-Tesu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aab'
+ },
+ {
+ name: 'Ari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aac'
+ },
+ {
+ name: 'Amal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aad'
+ },
+ {
+ name: 'Arbëreshë Albanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aae'
+ },
+ {
+ name: 'Aranadan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aaf'
+ },
+ {
+ name: 'Ambrak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aag'
+ },
+ {
+ name: "Abu' Arapesh",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aah'
+ },
+ {
+ name: 'Arifama-Miniafia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aai'
+ },
+ {
+ name: 'Ankave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aak'
+ },
+ {
+ name: 'Afade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aal'
+ },
+ {
+ name: 'Anambé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aan'
+ },
+ {
+ name: 'Algerian Saharan Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aao'
+ },
+ {
+ name: 'Pará Arára',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aap'
+ },
+ {
+ name: 'Eastern Abnaki',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aaq'
+ },
+ {
+ name: 'Afar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aar',
+ iso6392B: 'aar',
+ iso6392T: 'aar',
+ iso6391: 'aa'
+ },
+ {
+ name: 'Aasáx',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aas'
+ },
+ {
+ name: 'Arvanitika Albanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aat'
+ },
+ {
+ name: 'Abau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aau'
+ },
+ {
+ name: 'Solong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aaw'
+ },
+ {
+ name: 'Mandobo Atas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aax'
+ },
+ {
+ name: 'Amarasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aaz'
+ },
+ {
+ name: 'Abé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aba'
+ },
+ {
+ name: 'Bankon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abb'
+ },
+ {
+ name: 'Ambala Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abc'
+ },
+ {
+ name: 'Manide',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abd'
+ },
+ {
+ name: 'Western Abnaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abe'
+ },
+ {
+ name: 'Abai Sungai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abf'
+ },
+ {
+ name: 'Abaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abg'
+ },
+ {
+ name: 'Tajiki Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abh'
+ },
+ {
+ name: 'Abidji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abi'
+ },
+ {
+ name: 'Aka-Bea',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'abj'
+ },
+ {
+ name: 'Abkhazian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abk',
+ iso6392B: 'abk',
+ iso6392T: 'abk',
+ iso6391: 'ab'
+ },
+ {
+ name: 'Lampung Nyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abl'
+ },
+ {
+ name: 'Abanyom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abm'
+ },
+ {
+ name: 'Abua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abn'
+ },
+ {
+ name: 'Abon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abo'
+ },
+ {
+ name: 'Abellen Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abp'
+ },
+ {
+ name: 'Abaza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abq'
+ },
+ {
+ name: 'Abron',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abr'
+ },
+ {
+ name: 'Ambonese Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abs'
+ },
+ {
+ name: 'Ambulas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abt'
+ },
+ {
+ name: 'Abure',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abu'
+ },
+ {
+ name: 'Baharna Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abv'
+ },
+ {
+ name: 'Pal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abw'
+ },
+ {
+ name: 'Inabaknon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abx'
+ },
+ {
+ name: 'Aneme Wake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aby'
+ },
+ {
+ name: 'Abui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'abz'
+ },
+ {
+ name: 'Achagua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aca'
+ },
+ {
+ name: 'Áncá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acb'
+ },
+ {
+ name: 'Gikyode',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acd'
+ },
+ {
+ name: 'Achinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ace',
+ iso6392B: 'ace',
+ iso6392T: 'ace'
+ },
+ {
+ name: 'Saint Lucian Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acf'
+ },
+ {
+ name: 'Acoli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ach',
+ iso6392B: 'ach',
+ iso6392T: 'ach'
+ },
+ {
+ name: 'Aka-Cari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aci'
+ },
+ {
+ name: 'Aka-Kora',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ack'
+ },
+ {
+ name: 'Akar-Bale',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'acl'
+ },
+ {
+ name: 'Mesopotamian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acm'
+ },
+ {
+ name: 'Achang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acn'
+ },
+ {
+ name: 'Eastern Acipa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acp'
+ },
+ {
+ name: "Ta'izzi-Adeni Arabic",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acq'
+ },
+ {
+ name: 'Achi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acr'
+ },
+ {
+ name: 'Acroá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'acs'
+ },
+ {
+ name: 'Achterhoeks',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'act'
+ },
+ {
+ name: 'Achuar-Shiwiar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acu'
+ },
+ {
+ name: 'Achumawi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acv'
+ },
+ {
+ name: 'Hijazi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acw'
+ },
+ {
+ name: 'Omani Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acx'
+ },
+ {
+ name: 'Cypriot Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acy'
+ },
+ {
+ name: 'Acheron',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'acz'
+ },
+ {
+ name: 'Adangme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ada',
+ iso6392B: 'ada',
+ iso6392T: 'ada'
+ },
+ {
+ name: 'Atauran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adb'
+ },
+ {
+ name: 'Lidzonka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'add'
+ },
+ {
+ name: 'Adele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ade'
+ },
+ {
+ name: 'Dhofari Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adf'
+ },
+ {
+ name: 'Andegerebinha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adg'
+ },
+ {
+ name: 'Adhola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adh'
+ },
+ {
+ name: 'Adi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adi'
+ },
+ {
+ name: 'Adioukrou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adj'
+ },
+ {
+ name: 'Galo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adl'
+ },
+ {
+ name: 'Adang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adn'
+ },
+ {
+ name: 'Abu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ado'
+ },
+ {
+ name: 'Adangbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adq'
+ },
+ {
+ name: 'Adonara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adr'
+ },
+ {
+ name: 'Adamorobe Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ads'
+ },
+ {
+ name: 'Adnyamathanha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adt'
+ },
+ {
+ name: 'Aduge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adu'
+ },
+ {
+ name: 'Amundava',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adw'
+ },
+ {
+ name: 'Amdo Tibetan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adx'
+ },
+ {
+ name: 'Adyghe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ady',
+ iso6392B: 'ady',
+ iso6392T: 'ady'
+ },
+ {
+ name: 'Adzera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'adz'
+ },
+ {
+ name: 'Areba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aea'
+ },
+ {
+ name: 'Tunisian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aeb'
+ },
+ {
+ name: 'Saidi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aec'
+ },
+ {
+ name: 'Argentine Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aed'
+ },
+ {
+ name: 'Northeast Pashai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aee'
+ },
+ {
+ name: 'Haeke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aek'
+ },
+ {
+ name: 'Ambele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ael'
+ },
+ {
+ name: 'Arem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aem'
+ },
+ {
+ name: 'Armenian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aen'
+ },
+ {
+ name: 'Aer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aeq'
+ },
+ {
+ name: 'Eastern Arrernte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aer'
+ },
+ {
+ name: 'Alsea',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aes'
+ },
+ {
+ name: 'Akeu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aeu'
+ },
+ {
+ name: 'Ambakich',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aew'
+ },
+ {
+ name: 'Amele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aey'
+ },
+ {
+ name: 'Aeka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aez'
+ },
+ {
+ name: 'Gulf Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afb'
+ },
+ {
+ name: 'Andai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afd'
+ },
+ {
+ name: 'Putukwam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afe'
+ },
+ {
+ name: 'Afghan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afg'
+ },
+ {
+ name: 'Afrihili',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'afh',
+ iso6392B: 'afh',
+ iso6392T: 'afh'
+ },
+ {
+ name: 'Akrukay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afi'
+ },
+ {
+ name: 'Nanubae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afk'
+ },
+ {
+ name: 'Defaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afn'
+ },
+ {
+ name: 'Eloyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afo'
+ },
+ {
+ name: 'Tapei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afp'
+ },
+ {
+ name: 'Afrikaans',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afr',
+ iso6392B: 'afr',
+ iso6392T: 'afr',
+ iso6391: 'af'
+ },
+ {
+ name: 'Afro-Seminole Creole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afs'
+ },
+ {
+ name: 'Afitti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aft'
+ },
+ {
+ name: 'Awutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afu'
+ },
+ {
+ name: 'Obokuitai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'afz'
+ },
+ {
+ name: 'Aguano',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aga'
+ },
+ {
+ name: 'Legbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agb'
+ },
+ {
+ name: 'Agatu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agc'
+ },
+ {
+ name: 'Agarabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agd'
+ },
+ {
+ name: 'Angal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'age'
+ },
+ {
+ name: 'Arguni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agf'
+ },
+ {
+ name: 'Angor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agg'
+ },
+ {
+ name: 'Ngelima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agh'
+ },
+ {
+ name: 'Agariya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agi'
+ },
+ {
+ name: 'Argobba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agj'
+ },
+ {
+ name: 'Isarog Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agk'
+ },
+ {
+ name: 'Fembe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agl'
+ },
+ {
+ name: 'Angaataha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agm'
+ },
+ {
+ name: 'Agutaynen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agn'
+ },
+ {
+ name: 'Tainae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ago'
+ },
+ {
+ name: 'Aghem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agq'
+ },
+ {
+ name: 'Aguaruna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agr'
+ },
+ {
+ name: 'Esimbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ags'
+ },
+ {
+ name: 'Central Cagayan Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agt'
+ },
+ {
+ name: 'Aguacateco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agu'
+ },
+ {
+ name: 'Remontado Dumagat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agv'
+ },
+ {
+ name: 'Kahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agw'
+ },
+ {
+ name: 'Aghul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agx'
+ },
+ {
+ name: 'Southern Alta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agy'
+ },
+ {
+ name: 'Mt. Iriga Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'agz'
+ },
+ {
+ name: 'Ahanta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aha'
+ },
+ {
+ name: 'Axamb',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahb'
+ },
+ {
+ name: 'Qimant',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahg'
+ },
+ {
+ name: 'Aghu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahh'
+ },
+ {
+ name: 'Tiagbamrin Aizi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahi'
+ },
+ {
+ name: 'Akha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahk'
+ },
+ {
+ name: 'Igo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahl'
+ },
+ {
+ name: 'Mobumrin Aizi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahm'
+ },
+ {
+ name: 'Àhàn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahn'
+ },
+ {
+ name: 'Ahom',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aho'
+ },
+ {
+ name: 'Aproumu Aizi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahp'
+ },
+ {
+ name: 'Ahirani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahr'
+ },
+ {
+ name: 'Ashe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ahs'
+ },
+ {
+ name: 'Ahtena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aht'
+ },
+ {
+ name: 'Arosi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aia'
+ },
+ {
+ name: 'Ainu (China)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aib'
+ },
+ {
+ name: 'Ainbai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aic'
+ },
+ {
+ name: 'Alngith',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aid'
+ },
+ {
+ name: 'Amara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aie'
+ },
+ {
+ name: 'Agi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aif'
+ },
+ {
+ name: 'Antigua and Barbuda Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aig'
+ },
+ {
+ name: 'Ai-Cham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aih'
+ },
+ {
+ name: 'Assyrian Neo-Aramaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aii'
+ },
+ {
+ name: 'Lishanid Noshan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aij'
+ },
+ {
+ name: 'Ake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aik'
+ },
+ {
+ name: 'Aimele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ail'
+ },
+ {
+ name: 'Aimol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aim'
+ },
+ {
+ name: 'Ainu (Japan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ain',
+ iso6392B: 'ain',
+ iso6392T: 'ain'
+ },
+ {
+ name: 'Aiton',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aio'
+ },
+ {
+ name: 'Burumakok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aip'
+ },
+ {
+ name: 'Aimaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aiq'
+ },
+ {
+ name: 'Airoran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'air'
+ },
+ {
+ name: 'Arikem',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ait'
+ },
+ {
+ name: 'Aari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aiw'
+ },
+ {
+ name: 'Aighon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aix'
+ },
+ {
+ name: 'Ali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aiy'
+ },
+ {
+ name: 'Aja (South Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aja'
+ },
+ {
+ name: 'Aja (Benin)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ajg'
+ },
+ {
+ name: 'Ajië',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aji'
+ },
+ {
+ name: 'Andajin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ajn'
+ },
+ {
+ name: 'South Levantine Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ajp'
+ },
+ {
+ name: 'Judeo-Tunisian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ajt'
+ },
+ {
+ name: 'Judeo-Moroccan Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aju'
+ },
+ {
+ name: 'Ajawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ajw'
+ },
+ {
+ name: 'Amri Karbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ajz'
+ },
+ {
+ name: 'Akan',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'aka',
+ iso6392B: 'aka',
+ iso6392T: 'aka',
+ iso6391: 'ak'
+ },
+ {
+ name: 'Batak Angkola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akb'
+ },
+ {
+ name: 'Mpur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akc'
+ },
+ {
+ name: 'Ukpet-Ehom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akd'
+ },
+ {
+ name: 'Akawaio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ake'
+ },
+ {
+ name: 'Akpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akf'
+ },
+ {
+ name: 'Anakalangu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akg'
+ },
+ {
+ name: 'Angal Heneng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akh'
+ },
+ {
+ name: 'Aiome',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aki'
+ },
+ {
+ name: 'Aka-Jeru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'akj'
+ },
+ {
+ name: 'Akkadian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'akk',
+ iso6392B: 'akk',
+ iso6392T: 'akk'
+ },
+ {
+ name: 'Aklanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akl'
+ },
+ {
+ name: 'Aka-Bo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'akm'
+ },
+ {
+ name: 'Akurio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ako'
+ },
+ {
+ name: 'Siwu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akp'
+ },
+ {
+ name: 'Ak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akq'
+ },
+ {
+ name: 'Araki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akr'
+ },
+ {
+ name: 'Akaselem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aks'
+ },
+ {
+ name: 'Akolet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akt'
+ },
+ {
+ name: 'Akum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aku'
+ },
+ {
+ name: 'Akhvakh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akv'
+ },
+ {
+ name: 'Akwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akw'
+ },
+ {
+ name: 'Aka-Kede',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'akx'
+ },
+ {
+ name: 'Aka-Kol',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aky'
+ },
+ {
+ name: 'Alabama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'akz'
+ },
+ {
+ name: 'Alago',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ala'
+ },
+ {
+ name: 'Qawasqar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alc'
+ },
+ {
+ name: 'Alladian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ald'
+ },
+ {
+ name: 'Aleut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ale',
+ iso6392B: 'ale',
+ iso6392T: 'ale'
+ },
+ {
+ name: 'Alege',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alf'
+ },
+ {
+ name: 'Alawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alh'
+ },
+ {
+ name: 'Amaimon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ali'
+ },
+ {
+ name: 'Alangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alj'
+ },
+ {
+ name: 'Alak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alk'
+ },
+ {
+ name: 'Allar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'all'
+ },
+ {
+ name: 'Amblong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alm'
+ },
+ {
+ name: 'Gheg Albanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aln'
+ },
+ {
+ name: 'Larike-Wakasihu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alo'
+ },
+ {
+ name: 'Alune',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alp'
+ },
+ {
+ name: 'Algonquin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alq'
+ },
+ {
+ name: 'Alutor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alr'
+ },
+ {
+ name: 'Tosk Albanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'als'
+ },
+ {
+ name: 'Southern Altai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alt',
+ iso6392B: 'alt',
+ iso6392T: 'alt'
+ },
+ {
+ name: "'Are'are",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alu'
+ },
+ {
+ name: 'Alaba-K’abeena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alw'
+ },
+ {
+ name: 'Amol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alx'
+ },
+ {
+ name: 'Alyawarr',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aly'
+ },
+ {
+ name: 'Alur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'alz'
+ },
+ {
+ name: 'Amanayé',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ama'
+ },
+ {
+ name: 'Ambo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amb'
+ },
+ {
+ name: 'Amahuaca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amc'
+ },
+ {
+ name: "Yanesha'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ame'
+ },
+ {
+ name: 'Hamer-Banna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amf'
+ },
+ {
+ name: 'Amurdak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amg'
+ },
+ {
+ name: 'Amharic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amh',
+ iso6392B: 'amh',
+ iso6392T: 'amh',
+ iso6391: 'am'
+ },
+ {
+ name: 'Amis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ami'
+ },
+ {
+ name: 'Amdang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amj'
+ },
+ {
+ name: 'Ambai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amk'
+ },
+ {
+ name: 'War-Jaintia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aml'
+ },
+ {
+ name: 'Ama (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amm'
+ },
+ {
+ name: 'Amanab',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amn'
+ },
+ {
+ name: 'Amo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amo'
+ },
+ {
+ name: 'Alamblak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amp'
+ },
+ {
+ name: 'Amahai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amq'
+ },
+ {
+ name: 'Amarakaeri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amr'
+ },
+ {
+ name: 'Southern Amami-Oshima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ams'
+ },
+ {
+ name: 'Amto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amt'
+ },
+ {
+ name: 'Guerrero Amuzgo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amu'
+ },
+ {
+ name: 'Ambelau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amv'
+ },
+ {
+ name: 'Western Neo-Aramaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amw'
+ },
+ {
+ name: 'Anmatyerre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amx'
+ },
+ {
+ name: 'Ami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'amy'
+ },
+ {
+ name: 'Atampaya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'amz'
+ },
+ {
+ name: 'Andaqui',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ana'
+ },
+ {
+ name: 'Andoa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'anb'
+ },
+ {
+ name: 'Ngas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anc'
+ },
+ {
+ name: 'Ansus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'and'
+ },
+ {
+ name: 'Xârâcùù',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ane'
+ },
+ {
+ name: 'Animere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anf'
+ },
+ {
+ name: 'Old English (ca. 450-1100)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ang',
+ iso6392B: 'ang',
+ iso6392T: 'ang'
+ },
+ {
+ name: 'Nend',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anh'
+ },
+ {
+ name: 'Andi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ani'
+ },
+ {
+ name: 'Anor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anj'
+ },
+ {
+ name: 'Goemai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ank'
+ },
+ {
+ name: 'Anu-Hkongso Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anl'
+ },
+ {
+ name: 'Anal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anm'
+ },
+ {
+ name: 'Obolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ann'
+ },
+ {
+ name: 'Andoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ano'
+ },
+ {
+ name: 'Angika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anp',
+ iso6392B: 'anp',
+ iso6392T: 'anp'
+ },
+ {
+ name: 'Jarawa (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anq'
+ },
+ {
+ name: 'Andh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anr'
+ },
+ {
+ name: 'Anserma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ans'
+ },
+ {
+ name: 'Antakarinya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ant'
+ },
+ {
+ name: 'Anuak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anu'
+ },
+ {
+ name: 'Denya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anv'
+ },
+ {
+ name: 'Anaang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anw'
+ },
+ {
+ name: 'Andra-Hus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anx'
+ },
+ {
+ name: 'Anyin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'any'
+ },
+ {
+ name: 'Anem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'anz'
+ },
+ {
+ name: 'Angolar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoa'
+ },
+ {
+ name: 'Abom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aob'
+ },
+ {
+ name: 'Pemon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoc'
+ },
+ {
+ name: 'Andarum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aod'
+ },
+ {
+ name: 'Angal Enen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoe'
+ },
+ {
+ name: 'Bragat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aof'
+ },
+ {
+ name: 'Angoram',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aog'
+ },
+ {
+ name: 'Anindilyakwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoi'
+ },
+ {
+ name: 'Mufian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoj'
+ },
+ {
+ name: 'Arhö',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aok'
+ },
+ {
+ name: 'Alor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aol'
+ },
+ {
+ name: 'Ömie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aom'
+ },
+ {
+ name: 'Bumbita Arapesh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aon'
+ },
+ {
+ name: 'Aore',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aor'
+ },
+ {
+ name: 'Taikat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aos'
+ },
+ {
+ name: 'Atong (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aot'
+ },
+ {
+ name: "A'ou",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aou'
+ },
+ {
+ name: 'Atorada',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aox'
+ },
+ {
+ name: 'Uab Meto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aoz'
+ },
+ {
+ name: "Sa'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apb'
+ },
+ {
+ name: 'North Levantine Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apc'
+ },
+ {
+ name: 'Sudanese Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apd'
+ },
+ {
+ name: 'Bukiyip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ape'
+ },
+ {
+ name: 'Pahanan Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apf'
+ },
+ {
+ name: 'Ampanang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apg'
+ },
+ {
+ name: 'Athpariya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aph'
+ },
+ {
+ name: 'Apiaká',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'api'
+ },
+ {
+ name: 'Jicarilla Apache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apj'
+ },
+ {
+ name: 'Kiowa Apache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apk'
+ },
+ {
+ name: 'Lipan Apache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apl'
+ },
+ {
+ name: 'Mescalero-Chiricahua Apache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apm'
+ },
+ {
+ name: 'Apinayé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apn'
+ },
+ {
+ name: 'Ambul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apo'
+ },
+ {
+ name: 'Apma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'app'
+ },
+ {
+ name: 'A-Pucikwar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apq'
+ },
+ {
+ name: 'Arop-Lokep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apr'
+ },
+ {
+ name: 'Arop-Sissano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aps'
+ },
+ {
+ name: 'Apatani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apt'
+ },
+ {
+ name: 'Apurinã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apu'
+ },
+ {
+ name: 'Alapmunte',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'apv'
+ },
+ {
+ name: 'Western Apache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apw'
+ },
+ {
+ name: 'Aputai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apx'
+ },
+ {
+ name: 'Apalaí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apy'
+ },
+ {
+ name: 'Safeyoka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'apz'
+ },
+ {
+ name: 'Archi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqc'
+ },
+ {
+ name: 'Ampari Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqd'
+ },
+ {
+ name: 'Arigidi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqg'
+ },
+ {
+ name: 'Atohwaim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqm'
+ },
+ {
+ name: 'Northern Alta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqn'
+ },
+ {
+ name: 'Atakapa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aqp'
+ },
+ {
+ name: 'Arhâ',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqr'
+ },
+ {
+ name: 'Angaité',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqt'
+ },
+ {
+ name: 'Akuntsu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aqz'
+ },
+ {
+ name: 'Arabic',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'ara',
+ iso6392B: 'ara',
+ iso6392T: 'ara',
+ iso6391: 'ar'
+ },
+ {
+ name: 'Standard Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arb'
+ },
+ {
+ name: 'Official Aramaic (700-300 BCE)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'arc',
+ iso6392B: 'arc',
+ iso6392T: 'arc'
+ },
+ {
+ name: 'Arabana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ard'
+ },
+ {
+ name: 'Western Arrarnta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'are'
+ },
+ {
+ name: 'Aragonese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arg',
+ iso6392B: 'arg',
+ iso6392T: 'arg',
+ iso6391: 'an'
+ },
+ {
+ name: 'Arhuaco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arh'
+ },
+ {
+ name: 'Arikara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ari'
+ },
+ {
+ name: 'Arapaso',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'arj'
+ },
+ {
+ name: 'Arikapú',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ark'
+ },
+ {
+ name: 'Arabela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arl'
+ },
+ {
+ name: 'Mapudungun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arn',
+ iso6392B: 'arn',
+ iso6392T: 'arn'
+ },
+ {
+ name: 'Araona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aro'
+ },
+ {
+ name: 'Arapaho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arp',
+ iso6392B: 'arp',
+ iso6392T: 'arp'
+ },
+ {
+ name: 'Algerian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arq'
+ },
+ {
+ name: 'Karo (Brazil)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arr'
+ },
+ {
+ name: 'Najdi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ars'
+ },
+ {
+ name: 'Aruá (Amazonas State)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aru'
+ },
+ {
+ name: 'Arbore',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arv'
+ },
+ {
+ name: 'Arawak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arw',
+ iso6392B: 'arw',
+ iso6392T: 'arw'
+ },
+ {
+ name: 'Aruá (Rodonia State)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arx'
+ },
+ {
+ name: 'Moroccan Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ary'
+ },
+ {
+ name: 'Egyptian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'arz'
+ },
+ {
+ name: 'Asu (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asa'
+ },
+ {
+ name: 'Assiniboine',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asb'
+ },
+ {
+ name: 'Casuarina Coast Asmat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asc'
+ },
+ {
+ name: 'American Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ase'
+ },
+ {
+ name: 'Auslan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asf'
+ },
+ {
+ name: 'Cishingini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asg'
+ },
+ {
+ name: 'Abishira',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ash'
+ },
+ {
+ name: 'Buruwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asi'
+ },
+ {
+ name: 'Sari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asj'
+ },
+ {
+ name: 'Ashkun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ask'
+ },
+ {
+ name: 'Asilulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asl'
+ },
+ {
+ name: 'Assamese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asm',
+ iso6392B: 'asm',
+ iso6392T: 'asm',
+ iso6391: 'as'
+ },
+ {
+ name: 'Xingú Asuriní',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asn'
+ },
+ {
+ name: 'Dano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aso'
+ },
+ {
+ name: 'Algerian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asp'
+ },
+ {
+ name: 'Austrian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asq'
+ },
+ {
+ name: 'Asuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asr'
+ },
+ {
+ name: 'Ipulo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ass'
+ },
+ {
+ name: 'Asturian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ast',
+ iso6392B: 'ast',
+ iso6392T: 'ast'
+ },
+ {
+ name: 'Tocantins Asurini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asu'
+ },
+ {
+ name: 'Asoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asv'
+ },
+ {
+ name: 'Australian Aborigines Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asw'
+ },
+ {
+ name: 'Muratayak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asx'
+ },
+ {
+ name: 'Yaosakor Asmat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asy'
+ },
+ {
+ name: 'As',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'asz'
+ },
+ {
+ name: 'Pele-Ata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ata'
+ },
+ {
+ name: 'Zaiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atb'
+ },
+ {
+ name: 'Atsahuaca',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'atc'
+ },
+ {
+ name: 'Ata Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atd'
+ },
+ {
+ name: 'Atemble',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ate'
+ },
+ {
+ name: 'Ivbie North-Okpela-Arhe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atg'
+ },
+ {
+ name: 'Attié',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ati'
+ },
+ {
+ name: 'Atikamekw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atj'
+ },
+ {
+ name: 'Ati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atk'
+ },
+ {
+ name: 'Mt. Iraya Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atl'
+ },
+ {
+ name: 'Ata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atm'
+ },
+ {
+ name: 'Ashtiani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atn'
+ },
+ {
+ name: 'Atong (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ato'
+ },
+ {
+ name: 'Pudtol Atta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atp'
+ },
+ {
+ name: 'Aralle-Tabulahan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atq'
+ },
+ {
+ name: 'Waimiri-Atroari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atr'
+ },
+ {
+ name: 'Gros Ventre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ats'
+ },
+ {
+ name: 'Pamplona Atta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'att'
+ },
+ {
+ name: 'Reel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atu'
+ },
+ {
+ name: 'Northern Altai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atv'
+ },
+ {
+ name: 'Atsugewi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atw'
+ },
+ {
+ name: 'Arutani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atx'
+ },
+ {
+ name: 'Aneityum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aty'
+ },
+ {
+ name: 'Arta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'atz'
+ },
+ {
+ name: 'Asumboa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aua'
+ },
+ {
+ name: 'Alugu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aub'
+ },
+ {
+ name: 'Waorani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auc'
+ },
+ {
+ name: 'Anuta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aud'
+ },
+ {
+ name: 'Aguna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aug'
+ },
+ {
+ name: 'Aushi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auh'
+ },
+ {
+ name: 'Anuki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aui'
+ },
+ {
+ name: 'Awjilah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auj'
+ },
+ {
+ name: 'Heyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auk'
+ },
+ {
+ name: 'Aulua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aul'
+ },
+ {
+ name: 'Asu (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aum'
+ },
+ {
+ name: 'Molmo One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aun'
+ },
+ {
+ name: 'Auyokawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'auo'
+ },
+ {
+ name: 'Makayam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aup'
+ },
+ {
+ name: 'Anus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auq'
+ },
+ {
+ name: 'Aruek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aur'
+ },
+ {
+ name: 'Austral',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aut'
+ },
+ {
+ name: 'Auye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auu'
+ },
+ {
+ name: 'Awyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auw'
+ },
+ {
+ name: 'Aurá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'aux'
+ },
+ {
+ name: 'Awiyaana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auy'
+ },
+ {
+ name: 'Uzbeki Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'auz'
+ },
+ {
+ name: 'Avaric',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ava',
+ iso6392B: 'ava',
+ iso6392T: 'ava',
+ iso6391: 'av'
+ },
+ {
+ name: 'Avau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avb'
+ },
+ {
+ name: 'Alviri-Vidari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avd'
+ },
+ {
+ name: 'Avestan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'ave',
+ iso6392B: 'ave',
+ iso6392T: 'ave',
+ iso6391: 'ae'
+ },
+ {
+ name: 'Avikam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avi'
+ },
+ {
+ name: 'Kotava',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'avk'
+ },
+ {
+ name: 'Eastern Egyptian Bedawi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avl'
+ },
+ {
+ name: 'Angkamuthi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'avm'
+ },
+ {
+ name: 'Avatime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avn'
+ },
+ {
+ name: 'Agavotaguerra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'avo'
+ },
+ {
+ name: 'Aushiri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'avs'
+ },
+ {
+ name: 'Au',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avt'
+ },
+ {
+ name: 'Avokaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avu'
+ },
+ {
+ name: 'Avá-Canoeiro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'avv'
+ },
+ {
+ name: 'Awadhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awa',
+ iso6392B: 'awa',
+ iso6392T: 'awa'
+ },
+ {
+ name: 'Awa (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awb'
+ },
+ {
+ name: 'Cicipu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awc'
+ },
+ {
+ name: 'Awetí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awe'
+ },
+ {
+ name: 'Anguthimri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'awg'
+ },
+ {
+ name: 'Awbono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awh'
+ },
+ {
+ name: 'Aekyom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awi'
+ },
+ {
+ name: 'Awabakal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'awk'
+ },
+ {
+ name: 'Arawum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awm'
+ },
+ {
+ name: 'Awngi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awn'
+ },
+ {
+ name: 'Awak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awo'
+ },
+ {
+ name: 'Awera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awr'
+ },
+ {
+ name: 'South Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aws'
+ },
+ {
+ name: 'Araweté',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awt'
+ },
+ {
+ name: 'Central Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awu'
+ },
+ {
+ name: 'Jair Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awv'
+ },
+ {
+ name: 'Awun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aww'
+ },
+ {
+ name: 'Awara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awx'
+ },
+ {
+ name: 'Edera Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'awy'
+ },
+ {
+ name: 'Abipon',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'axb'
+ },
+ {
+ name: 'Ayerrerenge',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'axe'
+ },
+ {
+ name: 'Mato Grosso Arára',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'axg'
+ },
+ {
+ name: 'Yaka (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'axk'
+ },
+ {
+ name: 'Lower Southern Aranda',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'axl'
+ },
+ {
+ name: 'Middle Armenian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'axm'
+ },
+ {
+ name: 'Xârâgurè',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'axx'
+ },
+ {
+ name: 'Awar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aya'
+ },
+ {
+ name: 'Ayizo Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayb'
+ },
+ {
+ name: 'Southern Aymara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayc'
+ },
+ {
+ name: 'Ayabadhu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ayd'
+ },
+ {
+ name: 'Ayere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aye'
+ },
+ {
+ name: 'Ginyanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayg'
+ },
+ {
+ name: 'Hadrami Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayh'
+ },
+ {
+ name: 'Leyigha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayi'
+ },
+ {
+ name: 'Akuku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayk'
+ },
+ {
+ name: 'Libyan Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayl'
+ },
+ {
+ name: 'Aymara',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'aym',
+ iso6392B: 'aym',
+ iso6392T: 'aym',
+ iso6391: 'ay'
+ },
+ {
+ name: 'Sanaani Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayn'
+ },
+ {
+ name: 'Ayoreo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayo'
+ },
+ {
+ name: 'North Mesopotamian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayp'
+ },
+ {
+ name: 'Ayi (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayq'
+ },
+ {
+ name: 'Central Aymara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayr'
+ },
+ {
+ name: 'Sorsogon Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ays'
+ },
+ {
+ name: 'Magbukun Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayt'
+ },
+ {
+ name: 'Ayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayu'
+ },
+ {
+ name: 'Mai Brat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ayz'
+ },
+ {
+ name: 'Azha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'aza'
+ },
+ {
+ name: 'South Azerbaijani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azb'
+ },
+ {
+ name: 'Eastern Durango Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azd'
+ },
+ {
+ name: 'Azerbaijani',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'aze',
+ iso6392B: 'aze',
+ iso6392T: 'aze',
+ iso6391: 'az'
+ },
+ {
+ name: 'San Pedro Amuzgos Amuzgo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azg'
+ },
+ {
+ name: 'North Azerbaijani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azj'
+ },
+ {
+ name: 'Ipalapa Amuzgo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azm'
+ },
+ {
+ name: 'Western Durango Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azn'
+ },
+ {
+ name: 'Awing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azo'
+ },
+ {
+ name: 'Faire Atta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azt'
+ },
+ {
+ name: 'Highland Puebla Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'azz'
+ },
+ {
+ name: 'Babatana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'baa'
+ },
+ {
+ name: 'Bainouk-Gunyuño',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bab'
+ },
+ {
+ name: 'Badui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bac'
+ },
+ {
+ name: 'Baré',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bae'
+ },
+ {
+ name: 'Nubaca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'baf'
+ },
+ {
+ name: 'Tuki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bag'
+ },
+ {
+ name: 'Bahamas Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bah'
+ },
+ {
+ name: 'Barakai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'baj'
+ },
+ {
+ name: 'Bashkir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bak',
+ iso6392B: 'bak',
+ iso6392T: 'bak',
+ iso6391: 'ba'
+ },
+ {
+ name: 'Baluchi',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'bal',
+ iso6392B: 'bal',
+ iso6392T: 'bal'
+ },
+ {
+ name: 'Bambara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bam',
+ iso6392B: 'bam',
+ iso6392T: 'bam',
+ iso6391: 'bm'
+ },
+ {
+ name: 'Balinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ban',
+ iso6392B: 'ban',
+ iso6392T: 'ban'
+ },
+ {
+ name: 'Waimaha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bao'
+ },
+ {
+ name: 'Bantawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bap'
+ },
+ {
+ name: 'Bavarian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bar'
+ },
+ {
+ name: 'Basa (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bas',
+ iso6392B: 'bas',
+ iso6392T: 'bas'
+ },
+ {
+ name: 'Bada (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bau'
+ },
+ {
+ name: 'Vengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bav'
+ },
+ {
+ name: 'Bambili-Bambui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'baw'
+ },
+ {
+ name: 'Bamun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bax'
+ },
+ {
+ name: 'Batuley',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bay'
+ },
+ {
+ name: 'Baatonum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bba'
+ },
+ {
+ name: 'Barai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbb'
+ },
+ {
+ name: 'Batak Toba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbc'
+ },
+ {
+ name: 'Bau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbd'
+ },
+ {
+ name: 'Bangba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbe'
+ },
+ {
+ name: 'Baibai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbf'
+ },
+ {
+ name: 'Barama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbg'
+ },
+ {
+ name: 'Bugan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbh'
+ },
+ {
+ name: 'Barombi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbi'
+ },
+ {
+ name: "Ghomálá'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbj'
+ },
+ {
+ name: 'Babanki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbk'
+ },
+ {
+ name: 'Bats',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbl'
+ },
+ {
+ name: 'Babango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbm'
+ },
+ {
+ name: 'Uneapa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbn'
+ },
+ {
+ name: 'Northern Bobo Madaré',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbo'
+ },
+ {
+ name: 'West Central Banda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbp'
+ },
+ {
+ name: 'Bamali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbq'
+ },
+ {
+ name: 'Girawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbr'
+ },
+ {
+ name: 'Bakpinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbs'
+ },
+ {
+ name: 'Mburku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbt'
+ },
+ {
+ name: 'Kulung (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbu'
+ },
+ {
+ name: 'Karnai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbv'
+ },
+ {
+ name: 'Baba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbw'
+ },
+ {
+ name: 'Bubia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bbx'
+ },
+ {
+ name: 'Befang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bby'
+ },
+ {
+ name: 'Central Bai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bca'
+ },
+ {
+ name: 'Bainouk-Samik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcb'
+ },
+ {
+ name: 'Southern Balochi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcc'
+ },
+ {
+ name: 'North Babar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcd'
+ },
+ {
+ name: 'Bamenyam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bce'
+ },
+ {
+ name: 'Bamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcf'
+ },
+ {
+ name: 'Baga Pokur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcg'
+ },
+ {
+ name: 'Bariai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bch'
+ },
+ {
+ name: 'Baoulé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bci'
+ },
+ {
+ name: 'Bardi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcj'
+ },
+ {
+ name: 'Bunuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bck'
+ },
+ {
+ name: 'Central Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcl'
+ },
+ {
+ name: 'Bannoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcm'
+ },
+ {
+ name: 'Bali (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcn'
+ },
+ {
+ name: 'Kaluli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bco'
+ },
+ {
+ name: 'Bali (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcp'
+ },
+ {
+ name: 'Bench',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcq'
+ },
+ {
+ name: 'Babine',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcr'
+ },
+ {
+ name: 'Kohumono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcs'
+ },
+ {
+ name: 'Bendi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bct'
+ },
+ {
+ name: 'Awad Bing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcu'
+ },
+ {
+ name: 'Shoo-Minda-Nye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcv'
+ },
+ {
+ name: 'Bana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcw'
+ },
+ {
+ name: 'Bacama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcy'
+ },
+ {
+ name: 'Bainouk-Gunyaamolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bcz'
+ },
+ {
+ name: 'Bayot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bda'
+ },
+ {
+ name: 'Basap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdb'
+ },
+ {
+ name: 'Emberá-Baudó',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdc'
+ },
+ {
+ name: 'Bunama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdd'
+ },
+ {
+ name: 'Bade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bde'
+ },
+ {
+ name: 'Biage',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdf'
+ },
+ {
+ name: 'Bonggi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdg'
+ },
+ {
+ name: 'Baka (South Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdh'
+ },
+ {
+ name: 'Burun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdi'
+ },
+ {
+ name: 'Bai (South Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdj'
+ },
+ {
+ name: 'Budukh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdk'
+ },
+ {
+ name: 'Indonesian Bajau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdl'
+ },
+ {
+ name: 'Buduma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdm'
+ },
+ {
+ name: 'Baldemu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdn'
+ },
+ {
+ name: 'Morom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdo'
+ },
+ {
+ name: 'Bende',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdp'
+ },
+ {
+ name: 'Bahnar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdq'
+ },
+ {
+ name: 'West Coast Bajau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdr'
+ },
+ {
+ name: 'Burunge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bds'
+ },
+ {
+ name: 'Bokoto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdt'
+ },
+ {
+ name: 'Oroko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdu'
+ },
+ {
+ name: 'Bodo Parja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdv'
+ },
+ {
+ name: 'Baham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdw'
+ },
+ {
+ name: 'Budong-Budong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdx'
+ },
+ {
+ name: 'Bandjalang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdy'
+ },
+ {
+ name: 'Badeshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bdz'
+ },
+ {
+ name: 'Beaver',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bea'
+ },
+ {
+ name: 'Bebele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beb'
+ },
+ {
+ name: 'Iceve-Maci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bec'
+ },
+ {
+ name: 'Bedoanas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bed'
+ },
+ {
+ name: 'Byangsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bee'
+ },
+ {
+ name: 'Benabena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bef'
+ },
+ {
+ name: 'Belait',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beg'
+ },
+ {
+ name: 'Biali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beh'
+ },
+ {
+ name: "Bekati'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bei'
+ },
+ {
+ name: 'Beja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bej',
+ iso6392B: 'bej',
+ iso6392T: 'bej'
+ },
+ {
+ name: 'Bebeli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bek'
+ },
+ {
+ name: 'Belarusian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bel',
+ iso6392B: 'bel',
+ iso6392T: 'bel',
+ iso6391: 'be'
+ },
+ {
+ name: 'Bemba (Zambia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bem',
+ iso6392B: 'bem',
+ iso6392T: 'bem'
+ },
+ {
+ name: 'Bengali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ben',
+ iso6392B: 'ben',
+ iso6392T: 'ben',
+ iso6391: 'bn'
+ },
+ {
+ name: 'Beami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beo'
+ },
+ {
+ name: 'Besoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bep'
+ },
+ {
+ name: 'Beembe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beq'
+ },
+ {
+ name: 'Besme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bes'
+ },
+ {
+ name: 'Guiberoua Béte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bet'
+ },
+ {
+ name: 'Blagar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'beu'
+ },
+ {
+ name: 'Daloa Bété',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bev'
+ },
+ {
+ name: 'Betawi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bew'
+ },
+ {
+ name: 'Jur Modo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bex'
+ },
+ {
+ name: 'Beli (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bey'
+ },
+ {
+ name: 'Bena (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bez'
+ },
+ {
+ name: 'Bari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfa'
+ },
+ {
+ name: 'Pauri Bareli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfb'
+ },
+ {
+ name: 'Panyi Bai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfc'
+ },
+ {
+ name: 'Bafut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfd'
+ },
+ {
+ name: 'Betaf',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfe'
+ },
+ {
+ name: 'Bofi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bff'
+ },
+ {
+ name: 'Busang Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfg'
+ },
+ {
+ name: 'Blafe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfh'
+ },
+ {
+ name: 'British Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfi'
+ },
+ {
+ name: 'Bafanji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfj'
+ },
+ {
+ name: 'Ban Khor Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfk'
+ },
+ {
+ name: 'Banda-Ndélé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfl'
+ },
+ {
+ name: 'Mmen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfm'
+ },
+ {
+ name: 'Bunak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfn'
+ },
+ {
+ name: 'Malba Birifor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfo'
+ },
+ {
+ name: 'Beba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfp'
+ },
+ {
+ name: 'Badaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfq'
+ },
+ {
+ name: 'Bazigar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfr'
+ },
+ {
+ name: 'Southern Bai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfs'
+ },
+ {
+ name: 'Balti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bft'
+ },
+ {
+ name: 'Gahri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfu'
+ },
+ {
+ name: 'Bondo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfw'
+ },
+ {
+ name: 'Bantayanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfx'
+ },
+ {
+ name: 'Bagheli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfy'
+ },
+ {
+ name: 'Mahasu Pahari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bfz'
+ },
+ {
+ name: 'Gwamhi-Wuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bga'
+ },
+ {
+ name: 'Bobongko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgb'
+ },
+ {
+ name: 'Haryanvi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgc'
+ },
+ {
+ name: 'Rathwi Bareli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgd'
+ },
+ {
+ name: 'Bauria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bge'
+ },
+ {
+ name: 'Bangandu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgf'
+ },
+ {
+ name: 'Bugun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgg'
+ },
+ {
+ name: 'Giangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgi'
+ },
+ {
+ name: 'Bangolan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgj'
+ },
+ {
+ name: 'Bit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgk'
+ },
+ {
+ name: 'Bo (Laos)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgl'
+ },
+ {
+ name: 'Western Balochi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgn'
+ },
+ {
+ name: 'Baga Koga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgo'
+ },
+ {
+ name: 'Eastern Balochi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgp'
+ },
+ {
+ name: 'Bagri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgq'
+ },
+ {
+ name: 'Bawm Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgr'
+ },
+ {
+ name: 'Tagabawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgs'
+ },
+ {
+ name: 'Bughotu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgt'
+ },
+ {
+ name: 'Mbongno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgu'
+ },
+ {
+ name: 'Warkay-Bipim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgv'
+ },
+ {
+ name: 'Bhatri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgw'
+ },
+ {
+ name: 'Balkan Gagauz Turkish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgx'
+ },
+ {
+ name: 'Benggoi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgy'
+ },
+ {
+ name: 'Banggai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bgz'
+ },
+ {
+ name: 'Bharia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bha'
+ },
+ {
+ name: 'Bhili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhb'
+ },
+ {
+ name: 'Biga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhc'
+ },
+ {
+ name: 'Bhadrawahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhd'
+ },
+ {
+ name: 'Bhaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhe'
+ },
+ {
+ name: 'Odiai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhf'
+ },
+ {
+ name: 'Binandere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhg'
+ },
+ {
+ name: 'Bukharic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhh'
+ },
+ {
+ name: 'Bhilali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhi'
+ },
+ {
+ name: 'Bahing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhj'
+ },
+ {
+ name: 'Bimin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhl'
+ },
+ {
+ name: 'Bathari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhm'
+ },
+ {
+ name: 'Bohtan Neo-Aramaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhn'
+ },
+ {
+ name: 'Bhojpuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bho',
+ iso6392B: 'bho',
+ iso6392T: 'bho'
+ },
+ {
+ name: 'Bima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhp'
+ },
+ {
+ name: 'Tukang Besi South',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhq'
+ },
+ {
+ name: 'Bara Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhr'
+ },
+ {
+ name: 'Buwal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhs'
+ },
+ {
+ name: 'Bhattiyali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bht'
+ },
+ {
+ name: 'Bhunjia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhu'
+ },
+ {
+ name: 'Bahau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhv'
+ },
+ {
+ name: 'Biak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhw'
+ },
+ {
+ name: 'Bhalay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhx'
+ },
+ {
+ name: 'Bhele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhy'
+ },
+ {
+ name: 'Bada (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bhz'
+ },
+ {
+ name: 'Badimaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bia'
+ },
+ {
+ name: 'Bissa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bib'
+ },
+ {
+ name: 'Bikaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bic'
+ },
+ {
+ name: 'Bidiyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bid'
+ },
+ {
+ name: 'Bepour',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bie'
+ },
+ {
+ name: 'Biafada',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bif'
+ },
+ {
+ name: 'Biangai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'big'
+ },
+ {
+ name: 'Vaghat-Ya-Bijim-Legeri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bij'
+ },
+ {
+ name: 'Bikol',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'bik',
+ iso6392B: 'bik',
+ iso6392T: 'bik'
+ },
+ {
+ name: 'Bile',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bil'
+ },
+ {
+ name: 'Bimoba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bim'
+ },
+ {
+ name: 'Bini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bin',
+ iso6392B: 'bin',
+ iso6392T: 'bin'
+ },
+ {
+ name: 'Nai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bio'
+ },
+ {
+ name: 'Bila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bip'
+ },
+ {
+ name: 'Bipi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biq'
+ },
+ {
+ name: 'Bisorio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bir'
+ },
+ {
+ name: 'Bislama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bis',
+ iso6392B: 'bis',
+ iso6392T: 'bis',
+ iso6391: 'bi'
+ },
+ {
+ name: 'Berinomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bit'
+ },
+ {
+ name: 'Biete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biu'
+ },
+ {
+ name: 'Southern Birifor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biv'
+ },
+ {
+ name: 'Kol (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biw'
+ },
+ {
+ name: 'Bijori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bix'
+ },
+ {
+ name: 'Birhor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biy'
+ },
+ {
+ name: 'Baloi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'biz'
+ },
+ {
+ name: 'Budza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bja'
+ },
+ {
+ name: 'Banggarla',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bjb'
+ },
+ {
+ name: 'Bariji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjc'
+ },
+ {
+ name: 'Biao-Jiao Mien',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bje'
+ },
+ {
+ name: 'Barzani Jewish Neo-Aramaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjf'
+ },
+ {
+ name: 'Bidyogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjg'
+ },
+ {
+ name: 'Bahinemo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjh'
+ },
+ {
+ name: 'Burji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bji'
+ },
+ {
+ name: 'Kanauji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjj'
+ },
+ {
+ name: 'Barok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjk'
+ },
+ {
+ name: 'Bulu (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjl'
+ },
+ {
+ name: 'Bajelani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjm'
+ },
+ {
+ name: 'Banjar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjn'
+ },
+ {
+ name: 'Mid-Southern Banda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjo'
+ },
+ {
+ name: 'Fanamaket',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjp'
+ },
+ {
+ name: 'Binumarien',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjr'
+ },
+ {
+ name: 'Bajan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjs'
+ },
+ {
+ name: 'Balanta-Ganja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjt'
+ },
+ {
+ name: 'Busuu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bju'
+ },
+ {
+ name: 'Bedjond',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjv'
+ },
+ {
+ name: 'Bakwé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjw'
+ },
+ {
+ name: 'Banao Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjx'
+ },
+ {
+ name: 'Bayali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bjy'
+ },
+ {
+ name: 'Baruga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bjz'
+ },
+ {
+ name: 'Kyak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bka'
+ },
+ {
+ name: 'Baka (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkc'
+ },
+ {
+ name: 'Binukid',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkd'
+ },
+ {
+ name: 'Beeke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkf'
+ },
+ {
+ name: 'Buraka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkg'
+ },
+ {
+ name: 'Bakoko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkh'
+ },
+ {
+ name: 'Baki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bki'
+ },
+ {
+ name: 'Pande',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkj'
+ },
+ {
+ name: 'Brokskat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkk'
+ },
+ {
+ name: 'Berik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkl'
+ },
+ {
+ name: 'Kom (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkm'
+ },
+ {
+ name: 'Bukitan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkn'
+ },
+ {
+ name: "Kwa'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bko'
+ },
+ {
+ name: 'Boko (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkp'
+ },
+ {
+ name: 'Bakairí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkq'
+ },
+ {
+ name: 'Bakumpai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkr'
+ },
+ {
+ name: 'Northern Sorsoganon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bks'
+ },
+ {
+ name: 'Boloki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkt'
+ },
+ {
+ name: 'Buhid',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bku'
+ },
+ {
+ name: 'Bekwarra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkv'
+ },
+ {
+ name: 'Bekwel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkw'
+ },
+ {
+ name: 'Baikeno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkx'
+ },
+ {
+ name: 'Bokyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bky'
+ },
+ {
+ name: 'Bungku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bkz'
+ },
+ {
+ name: 'Siksika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bla',
+ iso6392B: 'bla',
+ iso6392T: 'bla'
+ },
+ {
+ name: 'Bilua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blb'
+ },
+ {
+ name: 'Bella Coola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blc'
+ },
+ {
+ name: 'Bolango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bld'
+ },
+ {
+ name: 'Balanta-Kentohe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ble'
+ },
+ {
+ name: 'Buol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blf'
+ },
+ {
+ name: 'Balau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blg'
+ },
+ {
+ name: 'Kuwaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blh'
+ },
+ {
+ name: 'Bolia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bli'
+ },
+ {
+ name: 'Bolongan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blj'
+ },
+ {
+ name: "Pa'o Karen",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blk'
+ },
+ {
+ name: 'Biloxi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bll'
+ },
+ {
+ name: 'Beli (South Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blm'
+ },
+ {
+ name: 'Southern Catanduanes Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bln'
+ },
+ {
+ name: 'Anii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blo'
+ },
+ {
+ name: 'Blablanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blp'
+ },
+ {
+ name: 'Baluan-Pam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blq'
+ },
+ {
+ name: 'Blang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blr'
+ },
+ {
+ name: 'Balaesang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bls'
+ },
+ {
+ name: 'Tai Dam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blt'
+ },
+ {
+ name: 'Kibala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blv'
+ },
+ {
+ name: 'Balangao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blw'
+ },
+ {
+ name: 'Mag-Indi Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blx'
+ },
+ {
+ name: 'Notre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bly'
+ },
+ {
+ name: 'Balantak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'blz'
+ },
+ {
+ name: 'Lame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bma'
+ },
+ {
+ name: 'Bembe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmb'
+ },
+ {
+ name: 'Biem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmc'
+ },
+ {
+ name: 'Baga Manduri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmd'
+ },
+ {
+ name: 'Limassa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bme'
+ },
+ {
+ name: 'Bom-Kim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmf'
+ },
+ {
+ name: 'Bamwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmg'
+ },
+ {
+ name: 'Kein',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmh'
+ },
+ {
+ name: 'Bagirmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmi'
+ },
+ {
+ name: 'Bote-Majhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmj'
+ },
+ {
+ name: 'Ghayavi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmk'
+ },
+ {
+ name: 'Bomboli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bml'
+ },
+ {
+ name: 'Northern Betsimisaraka Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmm'
+ },
+ {
+ name: 'Bina (Papua New Guinea)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bmn'
+ },
+ {
+ name: 'Bambalang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmo'
+ },
+ {
+ name: 'Bulgebi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmp'
+ },
+ {
+ name: 'Bomu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmq'
+ },
+ {
+ name: 'Muinane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmr'
+ },
+ {
+ name: 'Bilma Kanuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bms'
+ },
+ {
+ name: 'Biao Mon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmt'
+ },
+ {
+ name: 'Somba-Siawari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmu'
+ },
+ {
+ name: 'Bum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmv'
+ },
+ {
+ name: 'Bomwali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmw'
+ },
+ {
+ name: 'Baimak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmx'
+ },
+ {
+ name: 'Baramu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bmz'
+ },
+ {
+ name: 'Bonerate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bna'
+ },
+ {
+ name: 'Bookan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnb'
+ },
+ {
+ name: 'Bontok',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'bnc'
+ },
+ {
+ name: 'Banda (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnd'
+ },
+ {
+ name: 'Bintauna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bne'
+ },
+ {
+ name: 'Masiwang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnf'
+ },
+ {
+ name: 'Benga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bng'
+ },
+ {
+ name: 'Bangi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bni'
+ },
+ {
+ name: 'Eastern Tawbuid',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnj'
+ },
+ {
+ name: 'Bierebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnk'
+ },
+ {
+ name: 'Boon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnl'
+ },
+ {
+ name: 'Batanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnm'
+ },
+ {
+ name: 'Bunun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnn'
+ },
+ {
+ name: 'Bantoanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bno'
+ },
+ {
+ name: 'Bola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnp'
+ },
+ {
+ name: 'Bantik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnq'
+ },
+ {
+ name: 'Butmas-Tur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnr'
+ },
+ {
+ name: 'Bundeli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bns'
+ },
+ {
+ name: 'Bentong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnu'
+ },
+ {
+ name: 'Bonerif',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnv'
+ },
+ {
+ name: 'Bisis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnw'
+ },
+ {
+ name: 'Bangubangu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnx'
+ },
+ {
+ name: 'Bintulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bny'
+ },
+ {
+ name: 'Beezen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bnz'
+ },
+ {
+ name: 'Bora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boa'
+ },
+ {
+ name: 'Aweer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bob'
+ },
+ {
+ name: 'Tibetan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bod',
+ iso6392B: 'tib',
+ iso6392T: 'bod',
+ iso6391: 'bo'
+ },
+ {
+ name: 'Mundabli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boe'
+ },
+ {
+ name: 'Bolon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bof'
+ },
+ {
+ name: 'Bamako Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bog'
+ },
+ {
+ name: 'Boma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boh'
+ },
+ {
+ name: 'Barbareño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'boi'
+ },
+ {
+ name: 'Anjam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boj'
+ },
+ {
+ name: 'Bonjo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bok'
+ },
+ {
+ name: 'Bole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bol'
+ },
+ {
+ name: 'Berom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bom'
+ },
+ {
+ name: 'Bine',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bon'
+ },
+ {
+ name: 'Tiemacèwè Bozo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boo'
+ },
+ {
+ name: 'Bonkiman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bop'
+ },
+ {
+ name: 'Bogaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boq'
+ },
+ {
+ name: 'Borôro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bor'
+ },
+ {
+ name: 'Bosnian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bos',
+ iso6392B: 'bos',
+ iso6392T: 'bos',
+ iso6391: 'bs'
+ },
+ {
+ name: 'Bongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bot'
+ },
+ {
+ name: 'Bondei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bou'
+ },
+ {
+ name: 'Tuwuli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bov'
+ },
+ {
+ name: 'Rema',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bow'
+ },
+ {
+ name: 'Buamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'box'
+ },
+ {
+ name: 'Bodo (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boy'
+ },
+ {
+ name: 'Tiéyaxo Bozo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'boz'
+ },
+ {
+ name: 'Daakaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpa'
+ },
+ {
+ name: 'Banda-Banda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpd'
+ },
+ {
+ name: 'Bonggo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpg'
+ },
+ {
+ name: 'Botlikh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bph'
+ },
+ {
+ name: 'Bagupi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpi'
+ },
+ {
+ name: 'Binji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpj'
+ },
+ {
+ name: 'Orowe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpk'
+ },
+ {
+ name: 'Broome Pearling Lugger Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpl'
+ },
+ {
+ name: 'Biyom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpm'
+ },
+ {
+ name: 'Dzao Min',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpn'
+ },
+ {
+ name: 'Anasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpo'
+ },
+ {
+ name: 'Kaure',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpp'
+ },
+ {
+ name: 'Banda Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpq'
+ },
+ {
+ name: 'Koronadal Blaan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpr'
+ },
+ {
+ name: 'Sarangani Blaan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bps'
+ },
+ {
+ name: 'Barrow Point',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bpt'
+ },
+ {
+ name: 'Bongu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpu'
+ },
+ {
+ name: 'Bian Marind',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpv'
+ },
+ {
+ name: 'Bo (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpw'
+ },
+ {
+ name: 'Palya Bareli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpx'
+ },
+ {
+ name: 'Bishnupriya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpy'
+ },
+ {
+ name: 'Bilba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bpz'
+ },
+ {
+ name: 'Tchumbuli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqa'
+ },
+ {
+ name: 'Bagusa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqb'
+ },
+ {
+ name: 'Boko (Benin)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqc'
+ },
+ {
+ name: 'Bung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqd'
+ },
+ {
+ name: 'Baga Kaloum',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bqf'
+ },
+ {
+ name: 'Bago-Kusuntu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqg'
+ },
+ {
+ name: 'Baima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqh'
+ },
+ {
+ name: 'Bakhtiari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqi'
+ },
+ {
+ name: 'Bandial',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqj'
+ },
+ {
+ name: 'Banda-Mbrès',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqk'
+ },
+ {
+ name: 'Bilakura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bql'
+ },
+ {
+ name: 'Wumboko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqm'
+ },
+ {
+ name: 'Bulgarian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqn'
+ },
+ {
+ name: 'Balo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqo'
+ },
+ {
+ name: 'Busa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqp'
+ },
+ {
+ name: 'Biritai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqq'
+ },
+ {
+ name: 'Burusu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqr'
+ },
+ {
+ name: 'Bosngun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqs'
+ },
+ {
+ name: 'Bamukumbit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqt'
+ },
+ {
+ name: 'Boguru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqu'
+ },
+ {
+ name: 'Koro Wachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqv'
+ },
+ {
+ name: 'Buru (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqw'
+ },
+ {
+ name: 'Baangi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqx'
+ },
+ {
+ name: 'Bengkala Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqy'
+ },
+ {
+ name: 'Bakaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bqz'
+ },
+ {
+ name: 'Braj',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bra',
+ iso6392B: 'bra',
+ iso6392T: 'bra'
+ },
+ {
+ name: 'Lave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brb'
+ },
+ {
+ name: 'Berbice Creole Dutch',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'brc'
+ },
+ {
+ name: 'Baraamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brd'
+ },
+ {
+ name: 'Breton',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bre',
+ iso6392B: 'bre',
+ iso6392T: 'bre',
+ iso6391: 'br'
+ },
+ {
+ name: 'Bira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brf'
+ },
+ {
+ name: 'Baure',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brg'
+ },
+ {
+ name: 'Brahui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brh'
+ },
+ {
+ name: 'Mokpwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bri'
+ },
+ {
+ name: 'Bieria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brj'
+ },
+ {
+ name: 'Birked',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'brk'
+ },
+ {
+ name: 'Birwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brl'
+ },
+ {
+ name: 'Barambu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brm'
+ },
+ {
+ name: 'Boruca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brn'
+ },
+ {
+ name: 'Brokkat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bro'
+ },
+ {
+ name: 'Barapasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brp'
+ },
+ {
+ name: 'Breri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brq'
+ },
+ {
+ name: 'Birao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brr'
+ },
+ {
+ name: 'Baras',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brs'
+ },
+ {
+ name: 'Bitare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brt'
+ },
+ {
+ name: 'Eastern Bru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bru'
+ },
+ {
+ name: 'Western Bru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brv'
+ },
+ {
+ name: 'Bellari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brw'
+ },
+ {
+ name: 'Bodo (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brx'
+ },
+ {
+ name: 'Burui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bry'
+ },
+ {
+ name: 'Bilbil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'brz'
+ },
+ {
+ name: 'Abinomn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsa'
+ },
+ {
+ name: 'Brunei Bisaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsb'
+ },
+ {
+ name: 'Bassari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsc'
+ },
+ {
+ name: 'Wushi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bse'
+ },
+ {
+ name: 'Bauchi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsf'
+ },
+ {
+ name: 'Bashkardi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsg'
+ },
+ {
+ name: 'Kati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsh'
+ },
+ {
+ name: 'Bassossi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsi'
+ },
+ {
+ name: 'Bangwinji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsj'
+ },
+ {
+ name: 'Burushaski',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsk'
+ },
+ {
+ name: 'Basa-Gumna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bsl'
+ },
+ {
+ name: 'Busami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsm'
+ },
+ {
+ name: 'Barasana-Eduria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsn'
+ },
+ {
+ name: 'Buso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bso'
+ },
+ {
+ name: 'Baga Sitemu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsp'
+ },
+ {
+ name: 'Bassa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsq'
+ },
+ {
+ name: 'Bassa-Kontagora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsr'
+ },
+ {
+ name: 'Akoose',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bss'
+ },
+ {
+ name: 'Basketo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bst'
+ },
+ {
+ name: 'Bahonsuai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsu'
+ },
+ {
+ name: 'Baga Sobané',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bsv'
+ },
+ {
+ name: 'Baiso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsw'
+ },
+ {
+ name: 'Yangkam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsx'
+ },
+ {
+ name: 'Sabah Bisaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bsy'
+ },
+ {
+ name: 'Bata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bta'
+ },
+ {
+ name: 'Bati (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btc'
+ },
+ {
+ name: 'Batak Dairi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btd'
+ },
+ {
+ name: 'Gamo-Ningi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bte'
+ },
+ {
+ name: 'Birgit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btf'
+ },
+ {
+ name: 'Gagnoa Bété',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btg'
+ },
+ {
+ name: 'Biatah Bidayuh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bth'
+ },
+ {
+ name: 'Burate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bti'
+ },
+ {
+ name: 'Bacanese Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btj'
+ },
+ {
+ name: 'Batak Mandailing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btm'
+ },
+ {
+ name: 'Ratagnon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btn'
+ },
+ {
+ name: 'Rinconada Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bto'
+ },
+ {
+ name: 'Budibud',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btp'
+ },
+ {
+ name: 'Batek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btq'
+ },
+ {
+ name: 'Baetora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btr'
+ },
+ {
+ name: 'Batak Simalungun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bts'
+ },
+ {
+ name: 'Bete-Bendi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btt'
+ },
+ {
+ name: 'Batu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btu'
+ },
+ {
+ name: 'Bateri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btv'
+ },
+ {
+ name: 'Butuanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btw'
+ },
+ {
+ name: 'Batak Karo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btx'
+ },
+ {
+ name: 'Bobot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bty'
+ },
+ {
+ name: 'Batak Alas-Kluet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'btz'
+ },
+ {
+ name: 'Buriat',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'bua',
+ iso6392B: 'bua',
+ iso6392T: 'bua'
+ },
+ {
+ name: 'Bua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bub'
+ },
+ {
+ name: 'Bushi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buc'
+ },
+ {
+ name: 'Ntcham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bud'
+ },
+ {
+ name: 'Beothuk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bue'
+ },
+ {
+ name: 'Bushoong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buf'
+ },
+ {
+ name: 'Buginese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bug',
+ iso6392B: 'bug',
+ iso6392T: 'bug'
+ },
+ {
+ name: 'Younuo Bunu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buh'
+ },
+ {
+ name: 'Bongili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bui'
+ },
+ {
+ name: 'Basa-Gurmana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buj'
+ },
+ {
+ name: 'Bugawac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buk'
+ },
+ {
+ name: 'Bulgarian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bul',
+ iso6392B: 'bul',
+ iso6392T: 'bul',
+ iso6391: 'bg'
+ },
+ {
+ name: 'Bulu (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bum'
+ },
+ {
+ name: 'Sherbro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bun'
+ },
+ {
+ name: 'Terei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buo'
+ },
+ {
+ name: 'Busoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bup'
+ },
+ {
+ name: 'Brem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buq'
+ },
+ {
+ name: 'Bokobaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bus'
+ },
+ {
+ name: 'Bungain',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'but'
+ },
+ {
+ name: 'Budu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buu'
+ },
+ {
+ name: 'Bun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buv'
+ },
+ {
+ name: 'Bubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buw'
+ },
+ {
+ name: 'Boghom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bux'
+ },
+ {
+ name: 'Bullom So',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buy'
+ },
+ {
+ name: 'Bukwen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'buz'
+ },
+ {
+ name: 'Barein',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bva'
+ },
+ {
+ name: 'Bube',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvb'
+ },
+ {
+ name: 'Baelelea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvc'
+ },
+ {
+ name: 'Baeggu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvd'
+ },
+ {
+ name: 'Berau Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bve'
+ },
+ {
+ name: 'Boor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvf'
+ },
+ {
+ name: 'Bonkeng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvg'
+ },
+ {
+ name: 'Bure',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvh'
+ },
+ {
+ name: 'Belanda Viri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvi'
+ },
+ {
+ name: 'Baan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvj'
+ },
+ {
+ name: 'Bukat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvk'
+ },
+ {
+ name: 'Bolivian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvl'
+ },
+ {
+ name: 'Bamunka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvm'
+ },
+ {
+ name: 'Buna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvn'
+ },
+ {
+ name: 'Bolgo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvo'
+ },
+ {
+ name: 'Bumang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvp'
+ },
+ {
+ name: 'Birri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvq'
+ },
+ {
+ name: 'Burarra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvr'
+ },
+ {
+ name: 'Bati (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvt'
+ },
+ {
+ name: 'Bukit Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvu'
+ },
+ {
+ name: 'Baniva',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bvv'
+ },
+ {
+ name: 'Boga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvw'
+ },
+ {
+ name: 'Dibole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvx'
+ },
+ {
+ name: 'Baybayanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvy'
+ },
+ {
+ name: 'Bauzi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bvz'
+ },
+ {
+ name: 'Bwatoo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwa'
+ },
+ {
+ name: 'Namosi-Naitasiri-Serua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwb'
+ },
+ {
+ name: 'Bwile',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwc'
+ },
+ {
+ name: 'Bwaidoka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwd'
+ },
+ {
+ name: 'Bwe Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwe'
+ },
+ {
+ name: 'Boselewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwf'
+ },
+ {
+ name: 'Barwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwg'
+ },
+ {
+ name: 'Bishuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwh'
+ },
+ {
+ name: 'Baniwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwi'
+ },
+ {
+ name: 'Láá Láá Bwamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwj'
+ },
+ {
+ name: 'Bauwaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwk'
+ },
+ {
+ name: 'Bwela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwl'
+ },
+ {
+ name: 'Biwat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwm'
+ },
+ {
+ name: 'Wunai Bunu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwn'
+ },
+ {
+ name: 'Boro (Ethiopia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwo'
+ },
+ {
+ name: 'Mandobo Bawah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwp'
+ },
+ {
+ name: 'Southern Bobo Madaré',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwq'
+ },
+ {
+ name: 'Bura-Pabir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwr'
+ },
+ {
+ name: 'Bomboma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bws'
+ },
+ {
+ name: 'Bafaw-Balong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwt'
+ },
+ {
+ name: 'Buli (Ghana)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwu'
+ },
+ {
+ name: 'Bwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bww'
+ },
+ {
+ name: 'Bu-Nao Bunu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwx'
+ },
+ {
+ name: 'Cwi Bwamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwy'
+ },
+ {
+ name: 'Bwisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bwz'
+ },
+ {
+ name: 'Tairaha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxa'
+ },
+ {
+ name: 'Belanda Bor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxb'
+ },
+ {
+ name: 'Molengue',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxc'
+ },
+ {
+ name: 'Pela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxd'
+ },
+ {
+ name: 'Birale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxe'
+ },
+ {
+ name: 'Bilur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxf'
+ },
+ {
+ name: 'Bangala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxg'
+ },
+ {
+ name: 'Buhutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxh'
+ },
+ {
+ name: 'Pirlatapa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bxi'
+ },
+ {
+ name: 'Bayungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxj'
+ },
+ {
+ name: 'Bukusu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxk'
+ },
+ {
+ name: 'Jalkunan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxl'
+ },
+ {
+ name: 'Mongolia Buriat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxm'
+ },
+ {
+ name: 'Burduna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxn'
+ },
+ {
+ name: 'Barikanchi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxo'
+ },
+ {
+ name: 'Bebil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxp'
+ },
+ {
+ name: 'Beele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxq'
+ },
+ {
+ name: 'Russia Buriat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxr'
+ },
+ {
+ name: 'Busam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxs'
+ },
+ {
+ name: 'China Buriat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxu'
+ },
+ {
+ name: 'Berakou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxv'
+ },
+ {
+ name: 'Bankagooma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxw'
+ },
+ {
+ name: 'Binahari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bxz'
+ },
+ {
+ name: 'Batak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bya'
+ },
+ {
+ name: 'Bikya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byb'
+ },
+ {
+ name: 'Ubaghara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byc'
+ },
+ {
+ name: "Benyadu'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byd'
+ },
+ {
+ name: 'Pouye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bye'
+ },
+ {
+ name: 'Bete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byf'
+ },
+ {
+ name: 'Baygo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'byg'
+ },
+ {
+ name: 'Bhujel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byh'
+ },
+ {
+ name: 'Buyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byi'
+ },
+ {
+ name: 'Bina (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byj'
+ },
+ {
+ name: 'Biao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byk'
+ },
+ {
+ name: 'Bayono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byl'
+ },
+ {
+ name: 'Bidjara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bym'
+ },
+ {
+ name: 'Bilin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byn',
+ iso6392B: 'byn',
+ iso6392T: 'byn'
+ },
+ {
+ name: 'Biyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byo'
+ },
+ {
+ name: 'Bumaji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byp'
+ },
+ {
+ name: 'Basay',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'byq'
+ },
+ {
+ name: 'Baruya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byr'
+ },
+ {
+ name: 'Burak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bys'
+ },
+ {
+ name: 'Berti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'byt'
+ },
+ {
+ name: 'Medumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byv'
+ },
+ {
+ name: 'Belhariya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byw'
+ },
+ {
+ name: 'Qaqet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byx'
+ },
+ {
+ name: 'Banaro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'byz'
+ },
+ {
+ name: 'Bandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bza'
+ },
+ {
+ name: 'Andio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzb'
+ },
+ {
+ name: 'Southern Betsimisaraka Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzc'
+ },
+ {
+ name: 'Bribri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzd'
+ },
+ {
+ name: 'Jenaama Bozo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bze'
+ },
+ {
+ name: 'Boikin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzf'
+ },
+ {
+ name: 'Babuza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzg'
+ },
+ {
+ name: 'Mapos Buang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzh'
+ },
+ {
+ name: 'Bisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzi'
+ },
+ {
+ name: 'Belize Kriol English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzj'
+ },
+ {
+ name: 'Nicaragua Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzk'
+ },
+ {
+ name: 'Boano (Sulawesi)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzl'
+ },
+ {
+ name: 'Bolondo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzm'
+ },
+ {
+ name: 'Boano (Maluku)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzn'
+ },
+ {
+ name: 'Bozaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzo'
+ },
+ {
+ name: 'Kemberano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzp'
+ },
+ {
+ name: 'Buli (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzq'
+ },
+ {
+ name: 'Biri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'bzr'
+ },
+ {
+ name: 'Brazilian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzs'
+ },
+ {
+ name: 'Brithenig',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'bzt'
+ },
+ {
+ name: 'Burmeso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzu'
+ },
+ {
+ name: 'Naami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzv'
+ },
+ {
+ name: 'Basa (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzw'
+ },
+ {
+ name: 'Kɛlɛngaxo Bozo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzx'
+ },
+ {
+ name: 'Obanliku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzy'
+ },
+ {
+ name: 'Evant',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'bzz'
+ },
+ {
+ name: 'Chortí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'caa'
+ },
+ {
+ name: 'Garifuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cab'
+ },
+ {
+ name: 'Chuj',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cac'
+ },
+ {
+ name: 'Caddo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cad',
+ iso6392B: 'cad',
+ iso6392T: 'cad'
+ },
+ {
+ name: 'Lehar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cae'
+ },
+ {
+ name: 'Southern Carrier',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'caf'
+ },
+ {
+ name: 'Nivaclé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cag'
+ },
+ {
+ name: 'Cahuarano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cah'
+ },
+ {
+ name: 'Chané',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'caj'
+ },
+ {
+ name: 'Kaqchikel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cak'
+ },
+ {
+ name: 'Carolinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cal'
+ },
+ {
+ name: 'Cemuhî',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cam'
+ },
+ {
+ name: 'Chambri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'can'
+ },
+ {
+ name: 'Chácobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cao'
+ },
+ {
+ name: 'Chipaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cap'
+ },
+ {
+ name: 'Car Nicobarese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'caq'
+ },
+ {
+ name: 'Galibi Carib',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'car',
+ iso6392B: 'car',
+ iso6392T: 'car'
+ },
+ {
+ name: 'Tsimané',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cas'
+ },
+ {
+ name: 'Catalan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cat',
+ iso6392B: 'cat',
+ iso6392T: 'cat',
+ iso6391: 'ca'
+ },
+ {
+ name: 'Cavineña',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cav'
+ },
+ {
+ name: 'Callawalla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'caw'
+ },
+ {
+ name: 'Chiquitano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cax'
+ },
+ {
+ name: 'Cayuga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cay'
+ },
+ {
+ name: 'Canichana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'caz'
+ },
+ {
+ name: 'Cabiyarí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbb'
+ },
+ {
+ name: 'Carapana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbc'
+ },
+ {
+ name: 'Carijona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbd'
+ },
+ {
+ name: 'Chimila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbg'
+ },
+ {
+ name: 'Chachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbi'
+ },
+ {
+ name: 'Ede Cabe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbj'
+ },
+ {
+ name: 'Chavacano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbk'
+ },
+ {
+ name: 'Bualkhaw Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbl'
+ },
+ {
+ name: 'Nyahkur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbn'
+ },
+ {
+ name: 'Izora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbo'
+ },
+ {
+ name: 'Tsucuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbq'
+ },
+ {
+ name: 'Cashibo-Cacataibo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbr'
+ },
+ {
+ name: 'Cashinahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbs'
+ },
+ {
+ name: 'Chayahuita',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbt'
+ },
+ {
+ name: 'Candoshi-Shapra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbu'
+ },
+ {
+ name: 'Cacua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbv'
+ },
+ {
+ name: 'Kinabalian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cbw'
+ },
+ {
+ name: 'Carabayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cby'
+ },
+ {
+ name: 'Chamicuro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccc'
+ },
+ {
+ name: 'Cafundo Creole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccd'
+ },
+ {
+ name: 'Chopi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cce'
+ },
+ {
+ name: 'Samba Daka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccg'
+ },
+ {
+ name: 'Atsam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cch'
+ },
+ {
+ name: 'Kasanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccj'
+ },
+ {
+ name: 'Cutchi-Swahili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccl'
+ },
+ {
+ name: 'Malaccan Creole Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccm'
+ },
+ {
+ name: 'Comaltepec Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cco'
+ },
+ {
+ name: 'Chakma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ccp'
+ },
+ {
+ name: 'Cacaopera',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ccr'
+ },
+ {
+ name: 'Choni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cda'
+ },
+ {
+ name: 'Chenchu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cde'
+ },
+ {
+ name: 'Chiru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdf'
+ },
+ {
+ name: 'Chambeali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdh'
+ },
+ {
+ name: 'Chodri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdi'
+ },
+ {
+ name: 'Churahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdj'
+ },
+ {
+ name: 'Chepang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdm'
+ },
+ {
+ name: 'Chaudangsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdn'
+ },
+ {
+ name: 'Min Dong Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdo'
+ },
+ {
+ name: 'Cinda-Regi-Tiyal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdr'
+ },
+ {
+ name: 'Chadian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cds'
+ },
+ {
+ name: 'Chadong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdy'
+ },
+ {
+ name: 'Koda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cdz'
+ },
+ {
+ name: 'Lower Chehalis',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cea'
+ },
+ {
+ name: 'Cebuano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ceb',
+ iso6392B: 'ceb',
+ iso6392T: 'ceb'
+ },
+ {
+ name: 'Chamacoco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ceg'
+ },
+ {
+ name: 'Eastern Khumi Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cek'
+ },
+ {
+ name: 'Cen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cen'
+ },
+ {
+ name: 'Czech',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ces',
+ iso6392B: 'cze',
+ iso6392T: 'ces',
+ iso6391: 'cs'
+ },
+ {
+ name: 'Centúúm',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cet'
+ },
+ {
+ name: 'Ekai Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cey'
+ },
+ {
+ name: 'Dijim-Bwilim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cfa'
+ },
+ {
+ name: 'Cara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cfd'
+ },
+ {
+ name: 'Como Karim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cfg'
+ },
+ {
+ name: 'Falam Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cfm'
+ },
+ {
+ name: 'Changriwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cga'
+ },
+ {
+ name: 'Kagayanen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cgc'
+ },
+ {
+ name: 'Chiga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cgg'
+ },
+ {
+ name: 'Chocangacakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cgk'
+ },
+ {
+ name: 'Chamorro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cha',
+ iso6392B: 'cha',
+ iso6392T: 'cha',
+ iso6391: 'ch'
+ },
+ {
+ name: 'Chibcha',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'chb',
+ iso6392B: 'chb',
+ iso6392T: 'chb'
+ },
+ {
+ name: 'Catawba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'chc'
+ },
+ {
+ name: 'Highland Oaxaca Chontal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chd'
+ },
+ {
+ name: 'Chechen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'che',
+ iso6392B: 'che',
+ iso6392T: 'che',
+ iso6391: 'ce'
+ },
+ {
+ name: 'Tabasco Chontal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chf'
+ },
+ {
+ name: 'Chagatai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'chg',
+ iso6392B: 'chg',
+ iso6392T: 'chg'
+ },
+ {
+ name: 'Chinook',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'chh'
+ },
+ {
+ name: 'Ojitlán Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chj'
+ },
+ {
+ name: 'Chuukese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chk',
+ iso6392B: 'chk',
+ iso6392T: 'chk'
+ },
+ {
+ name: 'Cahuilla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chl'
+ },
+ {
+ name: 'Mari (Russia)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'chm',
+ iso6392B: 'chm',
+ iso6392T: 'chm'
+ },
+ {
+ name: 'Chinook jargon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chn',
+ iso6392B: 'chn',
+ iso6392T: 'chn'
+ },
+ {
+ name: 'Choctaw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cho',
+ iso6392B: 'cho',
+ iso6392T: 'cho'
+ },
+ {
+ name: 'Chipewyan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chp',
+ iso6392B: 'chp',
+ iso6392T: 'chp'
+ },
+ {
+ name: 'Quiotepec Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chq'
+ },
+ {
+ name: 'Cherokee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chr',
+ iso6392B: 'chr',
+ iso6392T: 'chr'
+ },
+ {
+ name: 'Cholón',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cht'
+ },
+ {
+ name: 'Church Slavic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'chu',
+ iso6392B: 'chu',
+ iso6392T: 'chu',
+ iso6391: 'cu'
+ },
+ {
+ name: 'Chuvash',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chv',
+ iso6392B: 'chv',
+ iso6392T: 'chv',
+ iso6391: 'cv'
+ },
+ {
+ name: 'Chuwabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chw'
+ },
+ {
+ name: 'Chantyal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chx'
+ },
+ {
+ name: 'Cheyenne',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chy',
+ iso6392B: 'chy',
+ iso6392T: 'chy'
+ },
+ {
+ name: 'Ozumacín Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'chz'
+ },
+ {
+ name: 'Cia-Cia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cia'
+ },
+ {
+ name: 'Ci Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cib'
+ },
+ {
+ name: 'Chickasaw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cic'
+ },
+ {
+ name: 'Chimariko',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cid'
+ },
+ {
+ name: 'Cineni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cie'
+ },
+ {
+ name: 'Chinali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cih'
+ },
+ {
+ name: 'Chitkuli Kinnauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cik'
+ },
+ {
+ name: 'Cimbrian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cim'
+ },
+ {
+ name: 'Cinta Larga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cin'
+ },
+ {
+ name: 'Chiapanec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cip'
+ },
+ {
+ name: 'Tiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cir'
+ },
+ {
+ name: 'Chippewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ciw'
+ },
+ {
+ name: 'Chaima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ciy'
+ },
+ {
+ name: 'Western Cham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cja'
+ },
+ {
+ name: 'Chru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cje'
+ },
+ {
+ name: 'Upper Chehalis',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cjh'
+ },
+ {
+ name: 'Chamalal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cji'
+ },
+ {
+ name: 'Chokwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjk'
+ },
+ {
+ name: 'Eastern Cham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjm'
+ },
+ {
+ name: 'Chenapian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjn'
+ },
+ {
+ name: 'Ashéninka Pajonal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjo'
+ },
+ {
+ name: 'Cabécar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjp'
+ },
+ {
+ name: 'Shor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjs'
+ },
+ {
+ name: 'Chuave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjv'
+ },
+ {
+ name: 'Jinyu Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cjy'
+ },
+ {
+ name: 'Central Kurdish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckb'
+ },
+ {
+ name: 'Chak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckh'
+ },
+ {
+ name: 'Cibak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckl'
+ },
+ {
+ name: 'Chakavian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckm'
+ },
+ {
+ name: 'Kaang Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckn'
+ },
+ {
+ name: 'Anufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cko'
+ },
+ {
+ name: 'Kajakse',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckq'
+ },
+ {
+ name: 'Kairak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckr'
+ },
+ {
+ name: 'Tayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cks'
+ },
+ {
+ name: 'Chukot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckt'
+ },
+ {
+ name: 'Koasati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cku'
+ },
+ {
+ name: 'Kavalan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckv'
+ },
+ {
+ name: 'Caka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckx'
+ },
+ {
+ name: 'Cakfem-Mushere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cky'
+ },
+ {
+ name: 'Cakchiquel-Quiché Mixed Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ckz'
+ },
+ {
+ name: 'Ron',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cla'
+ },
+ {
+ name: 'Chilcotin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clc'
+ },
+ {
+ name: 'Chaldean Neo-Aramaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cld'
+ },
+ {
+ name: 'Lealao Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cle'
+ },
+ {
+ name: 'Chilisso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clh'
+ },
+ {
+ name: 'Chakali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cli'
+ },
+ {
+ name: 'Laitu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clj'
+ },
+ {
+ name: 'Idu-Mishmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clk'
+ },
+ {
+ name: 'Chala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cll'
+ },
+ {
+ name: 'Clallam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clm'
+ },
+ {
+ name: 'Lowland Oaxaca Chontal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clo'
+ },
+ {
+ name: 'Lautu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clt'
+ },
+ {
+ name: 'Caluyanun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clu'
+ },
+ {
+ name: 'Chulym',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'clw'
+ },
+ {
+ name: 'Eastern Highland Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cly'
+ },
+ {
+ name: 'Maa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cma'
+ },
+ {
+ name: 'Cerma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cme'
+ },
+ {
+ name: 'Classical Mongolian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'cmg'
+ },
+ {
+ name: 'Emberá-Chamí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cmi'
+ },
+ {
+ name: 'Campalagian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cml'
+ },
+ {
+ name: 'Michigamea',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cmm'
+ },
+ {
+ name: 'Mandarin Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cmn'
+ },
+ {
+ name: 'Central Mnong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cmo'
+ },
+ {
+ name: 'Mro-Khimi Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cmr'
+ },
+ {
+ name: 'Messapic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'cms'
+ },
+ {
+ name: 'Camtho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cmt'
+ },
+ {
+ name: 'Changthang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cna'
+ },
+ {
+ name: 'Chinbon Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnb'
+ },
+ {
+ name: 'Côông',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnc'
+ },
+ {
+ name: 'Northern Qiang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cng'
+ },
+ {
+ name: 'Hakha Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnh'
+ },
+ {
+ name: 'Asháninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cni'
+ },
+ {
+ name: 'Khumi Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnk'
+ },
+ {
+ name: 'Lalana Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnl'
+ },
+ {
+ name: 'Con',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cno'
+ },
+ {
+ name: 'Northern Ping Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnp'
+ },
+ {
+ name: 'Montenegrin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnr',
+ iso6392B: 'cnr',
+ iso6392T: 'cnr'
+ },
+ {
+ name: 'Central Asmat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cns'
+ },
+ {
+ name: 'Tepetotutla Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnt'
+ },
+ {
+ name: 'Chenoua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnu'
+ },
+ {
+ name: 'Ngawn Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cnw'
+ },
+ {
+ name: 'Middle Cornish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'cnx'
+ },
+ {
+ name: 'Cocos Islands Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coa'
+ },
+ {
+ name: 'Chicomuceltec',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cob'
+ },
+ {
+ name: 'Cocopa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coc'
+ },
+ {
+ name: 'Cocama-Cocamilla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cod'
+ },
+ {
+ name: 'Koreguaje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coe'
+ },
+ {
+ name: 'Colorado',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cof'
+ },
+ {
+ name: 'Chong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cog'
+ },
+ {
+ name: 'Chonyi-Dzihana-Kauma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coh'
+ },
+ {
+ name: 'Cochimi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'coj'
+ },
+ {
+ name: 'Santa Teresa Cora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cok'
+ },
+ {
+ name: 'Columbia-Wenatchi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'col'
+ },
+ {
+ name: 'Comanche',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'com'
+ },
+ {
+ name: 'Cofán',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'con'
+ },
+ {
+ name: 'Comox',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coo'
+ },
+ {
+ name: 'Coptic',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cop',
+ iso6392B: 'cop',
+ iso6392T: 'cop'
+ },
+ {
+ name: 'Coquille',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'coq'
+ },
+ {
+ name: 'Cornish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cor',
+ iso6392B: 'cor',
+ iso6392T: 'cor',
+ iso6391: 'kw'
+ },
+ {
+ name: 'Corsican',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cos',
+ iso6392B: 'cos',
+ iso6392T: 'cos',
+ iso6391: 'co'
+ },
+ {
+ name: 'Caquinte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cot'
+ },
+ {
+ name: 'Wamey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cou'
+ },
+ {
+ name: 'Cao Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cov'
+ },
+ {
+ name: 'Cowlitz',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cow'
+ },
+ {
+ name: 'Nanti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cox'
+ },
+ {
+ name: 'Chochotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'coz'
+ },
+ {
+ name: 'Palantla Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpa'
+ },
+ {
+ name: 'Ucayali-Yurúa Ashéninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpb'
+ },
+ {
+ name: 'Ajyíninka Apurucayali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpc'
+ },
+ {
+ name: 'Cappadocian Greek',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cpg'
+ },
+ {
+ name: 'Chinese Pidgin English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpi'
+ },
+ {
+ name: 'Cherepon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpn'
+ },
+ {
+ name: 'Kpeego',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpo'
+ },
+ {
+ name: 'Capiznon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cps'
+ },
+ {
+ name: 'Pichis Ashéninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpu'
+ },
+ {
+ name: 'Pu-Xian Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpx'
+ },
+ {
+ name: 'South Ucayali Ashéninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cpy'
+ },
+ {
+ name: 'Chuanqiandian Cluster Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cqd'
+ },
+ {
+ name: 'Chara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cra'
+ },
+ {
+ name: 'Island Carib',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'crb'
+ },
+ {
+ name: 'Lonwolwol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crc'
+ },
+ {
+ name: "Coeur d'Alene",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crd'
+ },
+ {
+ name: 'Cree',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'cre',
+ iso6392B: 'cre',
+ iso6392T: 'cre',
+ iso6391: 'cr'
+ },
+ {
+ name: 'Caramanta',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'crf'
+ },
+ {
+ name: 'Michif',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crg'
+ },
+ {
+ name: 'Crimean Tatar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crh',
+ iso6392B: 'crh',
+ iso6392T: 'crh'
+ },
+ {
+ name: 'Sãotomense',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cri'
+ },
+ {
+ name: 'Southern East Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crj'
+ },
+ {
+ name: 'Plains Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crk'
+ },
+ {
+ name: 'Northern East Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crl'
+ },
+ {
+ name: 'Moose Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crm'
+ },
+ {
+ name: 'El Nayar Cora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crn'
+ },
+ {
+ name: 'Crow',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cro'
+ },
+ {
+ name: "Iyo'wujwa Chorote",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crq'
+ },
+ {
+ name: 'Carolina Algonquian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'crr'
+ },
+ {
+ name: 'Seselwa Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crs'
+ },
+ {
+ name: "Iyojwa'ja Chorote",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crt'
+ },
+ {
+ name: 'Chaura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crv'
+ },
+ {
+ name: 'Chrau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crw'
+ },
+ {
+ name: 'Carrier',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'crx'
+ },
+ {
+ name: 'Cori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cry'
+ },
+ {
+ name: 'Cruzeño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'crz'
+ },
+ {
+ name: 'Chiltepec Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csa'
+ },
+ {
+ name: 'Kashubian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csb',
+ iso6392B: 'csb',
+ iso6392T: 'csb'
+ },
+ {
+ name: 'Catalan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csc'
+ },
+ {
+ name: 'Chiangmai Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csd'
+ },
+ {
+ name: 'Czech Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cse'
+ },
+ {
+ name: 'Cuba Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csf'
+ },
+ {
+ name: 'Chilean Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csg'
+ },
+ {
+ name: 'Asho Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csh'
+ },
+ {
+ name: 'Coast Miwok',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'csi'
+ },
+ {
+ name: 'Songlai Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csj'
+ },
+ {
+ name: 'Jola-Kasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csk'
+ },
+ {
+ name: 'Chinese Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csl'
+ },
+ {
+ name: 'Central Sierra Miwok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csm'
+ },
+ {
+ name: 'Colombian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csn'
+ },
+ {
+ name: 'Sochiapam Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cso'
+ },
+ {
+ name: 'Southern Ping Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csp'
+ },
+ {
+ name: 'Croatia Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csq'
+ },
+ {
+ name: 'Costa Rican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csr'
+ },
+ {
+ name: 'Southern Ohlone',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'css'
+ },
+ {
+ name: 'Northern Ohlone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cst'
+ },
+ {
+ name: 'Sumtu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csv'
+ },
+ {
+ name: 'Swampy Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csw'
+ },
+ {
+ name: 'Siyin Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csy'
+ },
+ {
+ name: 'Coos',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'csz'
+ },
+ {
+ name: 'Tataltepec Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cta'
+ },
+ {
+ name: 'Chetco',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ctc'
+ },
+ {
+ name: 'Tedim Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctd'
+ },
+ {
+ name: 'Tepinapa Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cte'
+ },
+ {
+ name: 'Chittagonian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctg'
+ },
+ {
+ name: 'Thaiphum Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cth'
+ },
+ {
+ name: 'Tlacoatzintepec Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctl'
+ },
+ {
+ name: 'Chitimacha',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ctm'
+ },
+ {
+ name: 'Chhintange',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctn'
+ },
+ {
+ name: 'Emberá-Catío',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cto'
+ },
+ {
+ name: 'Western Highland Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctp'
+ },
+ {
+ name: 'Northern Catanduanes Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cts'
+ },
+ {
+ name: 'Wayanad Chetti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctt'
+ },
+ {
+ name: 'Chol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctu'
+ },
+ {
+ name: 'Zacatepec Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ctz'
+ },
+ {
+ name: 'Cua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cua'
+ },
+ {
+ name: 'Cubeo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cub'
+ },
+ {
+ name: 'Usila Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuc'
+ },
+ {
+ name: 'Chungmboko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cug'
+ },
+ {
+ name: 'Chuka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuh'
+ },
+ {
+ name: 'Cuiba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cui'
+ },
+ {
+ name: 'Mashco Piro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuj'
+ },
+ {
+ name: 'San Blas Kuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuk'
+ },
+ {
+ name: 'Culina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cul'
+ },
+ {
+ name: 'Cumanagoto',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cuo'
+ },
+ {
+ name: 'Cupeño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cup'
+ },
+ {
+ name: 'Cun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuq'
+ },
+ {
+ name: 'Chhulung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cur'
+ },
+ {
+ name: 'Teutila Cuicatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cut'
+ },
+ {
+ name: 'Tai Ya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuu'
+ },
+ {
+ name: 'Cuvok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuv'
+ },
+ {
+ name: 'Chukwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuw'
+ },
+ {
+ name: 'Tepeuxila Cuicatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cux'
+ },
+ {
+ name: 'Cuitlatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cuy'
+ },
+ {
+ name: 'Chug',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cvg'
+ },
+ {
+ name: 'Valle Nacional Chinantec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cvn'
+ },
+ {
+ name: 'Kabwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwa'
+ },
+ {
+ name: 'Maindo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwb'
+ },
+ {
+ name: 'Woods Cree',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwd'
+ },
+ {
+ name: 'Kwere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwe'
+ },
+ {
+ name: 'Chewong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwg'
+ },
+ {
+ name: 'Kuwaataay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cwt'
+ },
+ {
+ name: 'Nopala Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cya'
+ },
+ {
+ name: 'Cayubaba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'cyb'
+ },
+ {
+ name: 'Welsh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cym',
+ iso6392B: 'wel',
+ iso6392T: 'cym',
+ iso6391: 'cy'
+ },
+ {
+ name: 'Cuyonon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'cyo'
+ },
+ {
+ name: 'Huizhou Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'czh'
+ },
+ {
+ name: 'Knaanic',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'czk'
+ },
+ {
+ name: 'Zenzontepec Chatino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'czn'
+ },
+ {
+ name: 'Min Zhong Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'czo'
+ },
+ {
+ name: 'Zotung Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'czt'
+ },
+ {
+ name: 'Dangaléat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'daa'
+ },
+ {
+ name: 'Dambi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dac'
+ },
+ {
+ name: 'Marik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dad'
+ },
+ {
+ name: 'Duupa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dae'
+ },
+ {
+ name: 'Dagbani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dag'
+ },
+ {
+ name: 'Gwahatike',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dah'
+ },
+ {
+ name: 'Day',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dai'
+ },
+ {
+ name: 'Dar Fur Daju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'daj'
+ },
+ {
+ name: 'Dakota',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dak',
+ iso6392B: 'dak',
+ iso6392T: 'dak'
+ },
+ {
+ name: 'Dahalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dal'
+ },
+ {
+ name: 'Damakawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dam'
+ },
+ {
+ name: 'Danish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dan',
+ iso6392B: 'dan',
+ iso6392T: 'dan',
+ iso6391: 'da'
+ },
+ {
+ name: 'Daai Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dao'
+ },
+ {
+ name: 'Dandami Maria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'daq'
+ },
+ {
+ name: 'Dargwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dar',
+ iso6392B: 'dar',
+ iso6392T: 'dar'
+ },
+ {
+ name: 'Daho-Doo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'das'
+ },
+ {
+ name: 'Dar Sila Daju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dau'
+ },
+ {
+ name: 'Taita',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dav'
+ },
+ {
+ name: 'Davawenyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'daw'
+ },
+ {
+ name: 'Dayi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dax'
+ },
+ {
+ name: 'Dao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'daz'
+ },
+ {
+ name: 'Bangime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dba'
+ },
+ {
+ name: 'Deno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbb'
+ },
+ {
+ name: 'Dadiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbd'
+ },
+ {
+ name: 'Dabe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbe'
+ },
+ {
+ name: 'Edopi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbf'
+ },
+ {
+ name: 'Dogul Dom Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbg'
+ },
+ {
+ name: 'Doka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbi'
+ },
+ {
+ name: "Ida'an",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbj'
+ },
+ {
+ name: 'Dyirbal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbl'
+ },
+ {
+ name: 'Duguri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbm'
+ },
+ {
+ name: 'Duriankere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbn'
+ },
+ {
+ name: 'Dulbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbo'
+ },
+ {
+ name: 'Duwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbp'
+ },
+ {
+ name: 'Daba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbq'
+ },
+ {
+ name: 'Dabarre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbr'
+ },
+ {
+ name: 'Ben Tey Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbt'
+ },
+ {
+ name: 'Bondum Dom Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbu'
+ },
+ {
+ name: 'Dungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbv'
+ },
+ {
+ name: 'Bankan Tey Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dbw'
+ },
+ {
+ name: 'Dibiyaso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dby'
+ },
+ {
+ name: 'Deccan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dcc'
+ },
+ {
+ name: 'Negerhollands',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dcr'
+ },
+ {
+ name: 'Dadi Dadi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dda'
+ },
+ {
+ name: 'Dongotono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddd'
+ },
+ {
+ name: 'Doondo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dde'
+ },
+ {
+ name: 'Fataluku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddg'
+ },
+ {
+ name: 'West Goodenough',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddi'
+ },
+ {
+ name: 'Jaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddj'
+ },
+ {
+ name: 'Dendi (Benin)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddn'
+ },
+ {
+ name: 'Dido',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddo'
+ },
+ {
+ name: 'Dhudhuroa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ddr'
+ },
+ {
+ name: 'Donno So Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dds'
+ },
+ {
+ name: 'Dawera-Daweloor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ddw'
+ },
+ {
+ name: 'Dagik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dec'
+ },
+ {
+ name: 'Dedua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ded'
+ },
+ {
+ name: 'Dewoin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dee'
+ },
+ {
+ name: 'Dezfuli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'def'
+ },
+ {
+ name: 'Degema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'deg'
+ },
+ {
+ name: 'Dehwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'deh'
+ },
+ {
+ name: 'Demisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dei'
+ },
+ {
+ name: 'Dek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dek'
+ },
+ {
+ name: 'Delaware',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'del',
+ iso6392B: 'del',
+ iso6392T: 'del'
+ },
+ {
+ name: 'Dem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dem'
+ },
+ {
+ name: 'Slave (Athapascan)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'den',
+ iso6392B: 'den',
+ iso6392T: 'den'
+ },
+ {
+ name: 'Pidgin Delaware',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dep'
+ },
+ {
+ name: 'Dendi (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'deq'
+ },
+ {
+ name: 'Deori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'der'
+ },
+ {
+ name: 'Desano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'des'
+ },
+ {
+ name: 'German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'deu',
+ iso6392B: 'ger',
+ iso6392T: 'deu',
+ iso6391: 'de'
+ },
+ {
+ name: 'Domung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dev'
+ },
+ {
+ name: 'Dengese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dez'
+ },
+ {
+ name: 'Southern Dagaare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dga'
+ },
+ {
+ name: 'Bunoge Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgb'
+ },
+ {
+ name: 'Casiguran Dumagat Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgc'
+ },
+ {
+ name: 'Dagaari Dioula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgd'
+ },
+ {
+ name: 'Degenan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dge'
+ },
+ {
+ name: 'Doga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgg'
+ },
+ {
+ name: 'Dghwede',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgh'
+ },
+ {
+ name: 'Northern Dagara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgi'
+ },
+ {
+ name: 'Dagba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgk'
+ },
+ {
+ name: 'Andaandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgl'
+ },
+ {
+ name: 'Dagoman',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dgn'
+ },
+ {
+ name: 'Dogri (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgo'
+ },
+ {
+ name: 'Dogrib',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgr',
+ iso6392B: 'dgr',
+ iso6392T: 'dgr'
+ },
+ {
+ name: 'Dogoso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgs'
+ },
+ {
+ name: "Ndra'ngith",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dgt'
+ },
+ {
+ name: 'Daungwurrung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dgw'
+ },
+ {
+ name: 'Doghoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgx'
+ },
+ {
+ name: 'Daga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dgz'
+ },
+ {
+ name: 'Dhundari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhd'
+ },
+ {
+ name: 'Dhangu-Djangu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhg'
+ },
+ {
+ name: 'Dhimal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhi'
+ },
+ {
+ name: 'Dhalandji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhl'
+ },
+ {
+ name: 'Zemba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhm'
+ },
+ {
+ name: 'Dhanki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhn'
+ },
+ {
+ name: 'Dhodia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dho'
+ },
+ {
+ name: 'Dhargari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhr'
+ },
+ {
+ name: 'Dhaiso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhs'
+ },
+ {
+ name: 'Dhurga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dhu'
+ },
+ {
+ name: 'Dehu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhv'
+ },
+ {
+ name: 'Dhanwar (Nepal)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhw'
+ },
+ {
+ name: 'Dhungaloo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dhx'
+ },
+ {
+ name: 'Dia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dia'
+ },
+ {
+ name: 'South Central Dinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dib'
+ },
+ {
+ name: 'Lakota Dida',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dic'
+ },
+ {
+ name: 'Didinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'did'
+ },
+ {
+ name: 'Dieri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dif'
+ },
+ {
+ name: 'Digo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dig'
+ },
+ {
+ name: 'Kumiai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dih'
+ },
+ {
+ name: 'Dimbong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dii'
+ },
+ {
+ name: 'Dai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dij'
+ },
+ {
+ name: 'Southwestern Dinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dik'
+ },
+ {
+ name: 'Dilling',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dil'
+ },
+ {
+ name: 'Dime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dim'
+ },
+ {
+ name: 'Dinka',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'din',
+ iso6392B: 'din',
+ iso6392T: 'din'
+ },
+ {
+ name: 'Dibo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dio'
+ },
+ {
+ name: 'Northeastern Dinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dip'
+ },
+ {
+ name: 'Dimli (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'diq'
+ },
+ {
+ name: 'Dirim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dir'
+ },
+ {
+ name: 'Dimasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dis'
+ },
+ {
+ name: 'Diriku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'diu'
+ },
+ {
+ name: 'Dhivehi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'div',
+ iso6392B: 'div',
+ iso6392T: 'div',
+ iso6391: 'dv'
+ },
+ {
+ name: 'Northwestern Dinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'diw'
+ },
+ {
+ name: 'Dixon Reef',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dix'
+ },
+ {
+ name: 'Diuwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'diy'
+ },
+ {
+ name: 'Ding',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'diz'
+ },
+ {
+ name: 'Djadjawurrung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dja'
+ },
+ {
+ name: 'Djinba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djb'
+ },
+ {
+ name: 'Dar Daju Daju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djc'
+ },
+ {
+ name: 'Djamindjung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djd'
+ },
+ {
+ name: 'Zarma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dje'
+ },
+ {
+ name: 'Djangun',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'djf'
+ },
+ {
+ name: 'Djinang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dji'
+ },
+ {
+ name: 'Djeebbana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djj'
+ },
+ {
+ name: 'Eastern Maroon Creole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djk'
+ },
+ {
+ name: 'Jamsay Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djm'
+ },
+ {
+ name: 'Jawoyn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djn'
+ },
+ {
+ name: 'Jangkang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djo'
+ },
+ {
+ name: 'Djambarrpuyngu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'djr'
+ },
+ {
+ name: 'Kapriman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dju'
+ },
+ {
+ name: 'Djawi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'djw'
+ },
+ {
+ name: 'Dakpakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dka'
+ },
+ {
+ name: 'Dakka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dkk'
+ },
+ {
+ name: 'Kuijau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dkr'
+ },
+ {
+ name: 'Southeastern Dinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dks'
+ },
+ {
+ name: 'Mazagway',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dkx'
+ },
+ {
+ name: 'Dolgan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dlg'
+ },
+ {
+ name: 'Dahalik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dlk'
+ },
+ {
+ name: 'Dalmatian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dlm'
+ },
+ {
+ name: 'Darlong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dln'
+ },
+ {
+ name: 'Duma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dma'
+ },
+ {
+ name: 'Mombo Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmb'
+ },
+ {
+ name: 'Gavak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmc'
+ },
+ {
+ name: 'Madhi Madhi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dmd'
+ },
+ {
+ name: 'Dugwor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dme'
+ },
+ {
+ name: 'Medefaidrin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dmf'
+ },
+ {
+ name: 'Upper Kinabatangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmg'
+ },
+ {
+ name: 'Domaaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmk'
+ },
+ {
+ name: 'Dameli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dml'
+ },
+ {
+ name: 'Dama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmm'
+ },
+ {
+ name: 'Kemedzung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmo'
+ },
+ {
+ name: 'East Damar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmr'
+ },
+ {
+ name: 'Dampelas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dms'
+ },
+ {
+ name: 'Dubu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmu'
+ },
+ {
+ name: 'Dumpas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmv'
+ },
+ {
+ name: 'Mudburra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmw'
+ },
+ {
+ name: 'Dema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmx'
+ },
+ {
+ name: 'Demta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dmy'
+ },
+ {
+ name: 'Upper Grand Valley Dani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dna'
+ },
+ {
+ name: 'Daonda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnd'
+ },
+ {
+ name: 'Ndendeule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dne'
+ },
+ {
+ name: 'Dungan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dng'
+ },
+ {
+ name: 'Lower Grand Valley Dani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dni'
+ },
+ {
+ name: 'Dan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnj'
+ },
+ {
+ name: 'Dengka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnk'
+ },
+ {
+ name: 'Dzùùngoo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnn'
+ },
+ {
+ name: 'Ndrulo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dno'
+ },
+ {
+ name: 'Danaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnr'
+ },
+ {
+ name: 'Mid Grand Valley Dani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnt'
+ },
+ {
+ name: 'Danau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnu'
+ },
+ {
+ name: 'Danu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnv'
+ },
+ {
+ name: 'Western Dani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dnw'
+ },
+ {
+ name: 'Dení',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dny'
+ },
+ {
+ name: 'Dom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doa'
+ },
+ {
+ name: 'Dobu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dob'
+ },
+ {
+ name: 'Northern Dong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doc'
+ },
+ {
+ name: 'Doe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doe'
+ },
+ {
+ name: 'Domu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dof'
+ },
+ {
+ name: 'Dong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doh'
+ },
+ {
+ name: 'Dogri (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'doi',
+ iso6392B: 'doi',
+ iso6392T: 'doi'
+ },
+ {
+ name: 'Dondo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dok'
+ },
+ {
+ name: 'Doso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dol'
+ },
+ {
+ name: 'Toura (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'don'
+ },
+ {
+ name: 'Dongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doo'
+ },
+ {
+ name: 'Lukpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dop'
+ },
+ {
+ name: 'Dominican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doq'
+ },
+ {
+ name: "Dori'o",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dor'
+ },
+ {
+ name: 'Dogosé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dos'
+ },
+ {
+ name: 'Dass',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dot'
+ },
+ {
+ name: 'Dombe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dov'
+ },
+ {
+ name: 'Doyayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dow'
+ },
+ {
+ name: 'Bussa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dox'
+ },
+ {
+ name: 'Dompo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doy'
+ },
+ {
+ name: 'Dorze',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'doz'
+ },
+ {
+ name: 'Papar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dpp'
+ },
+ {
+ name: 'Dair',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drb'
+ },
+ {
+ name: 'Minderico',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drc'
+ },
+ {
+ name: 'Darmiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drd'
+ },
+ {
+ name: 'Dolpo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dre'
+ },
+ {
+ name: 'Rungus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drg'
+ },
+ {
+ name: "C'Lela",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dri'
+ },
+ {
+ name: 'Paakantyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drl'
+ },
+ {
+ name: 'West Damar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drn'
+ },
+ {
+ name: 'Daro-Matu Melanau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dro'
+ },
+ {
+ name: 'Dura',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'drq'
+ },
+ {
+ name: 'Gedeo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drs'
+ },
+ {
+ name: 'Drents',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'drt'
+ },
+ {
+ name: 'Rukai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dru'
+ },
+ {
+ name: 'Darai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dry'
+ },
+ {
+ name: 'Lower Sorbian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dsb',
+ iso6392B: 'dsb',
+ iso6392T: 'dsb'
+ },
+ {
+ name: 'Dutch Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dse'
+ },
+ {
+ name: 'Daasanach',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dsh'
+ },
+ {
+ name: 'Disa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dsi'
+ },
+ {
+ name: 'Danish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dsl'
+ },
+ {
+ name: 'Dusner',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dsn'
+ },
+ {
+ name: 'Desiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dso'
+ },
+ {
+ name: 'Tadaksahak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dsq'
+ },
+ {
+ name: 'Daur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dta'
+ },
+ {
+ name: 'Labuk-Kinabatangan Kadazan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtb'
+ },
+ {
+ name: 'Ditidaht',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtd'
+ },
+ {
+ name: 'Adithinngithigh',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dth'
+ },
+ {
+ name: 'Ana Tinga Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dti'
+ },
+ {
+ name: 'Tene Kan Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtk'
+ },
+ {
+ name: 'Tomo Kan Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtm'
+ },
+ {
+ name: 'Daatsʼíin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtn'
+ },
+ {
+ name: 'Tommo So Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dto'
+ },
+ {
+ name: 'Kadazan Dusun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtp'
+ },
+ {
+ name: 'Lotud',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtr'
+ },
+ {
+ name: 'Toro So Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dts'
+ },
+ {
+ name: 'Toro Tegu Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtt'
+ },
+ {
+ name: 'Tebul Ure Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dtu'
+ },
+ {
+ name: 'Dotyali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dty'
+ },
+ {
+ name: 'Duala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dua',
+ iso6392B: 'dua',
+ iso6392T: 'dua'
+ },
+ {
+ name: 'Dubli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dub'
+ },
+ {
+ name: 'Duna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duc'
+ },
+ {
+ name: 'Umiray Dumaget Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'due'
+ },
+ {
+ name: 'Dumbea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duf'
+ },
+ {
+ name: 'Duruma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dug'
+ },
+ {
+ name: 'Dungra Bhil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duh'
+ },
+ {
+ name: 'Dumun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dui'
+ },
+ {
+ name: 'Uyajitaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duk'
+ },
+ {
+ name: 'Alabat Island Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dul'
+ },
+ {
+ name: 'Middle Dutch (ca. 1050-1350)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'dum',
+ iso6392B: 'dum',
+ iso6392T: 'dum'
+ },
+ {
+ name: 'Dusun Deyah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dun'
+ },
+ {
+ name: 'Dupaninan Agta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duo'
+ },
+ {
+ name: 'Duano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dup'
+ },
+ {
+ name: 'Dusun Malang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duq'
+ },
+ {
+ name: 'Dii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dur'
+ },
+ {
+ name: 'Dumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dus'
+ },
+ {
+ name: 'Drung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duu'
+ },
+ {
+ name: 'Duvle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duv'
+ },
+ {
+ name: 'Dusun Witu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'duw'
+ },
+ {
+ name: 'Duungooma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dux'
+ },
+ {
+ name: 'Dicamay Agta',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'duy'
+ },
+ {
+ name: 'Duli-Gey',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'duz'
+ },
+ {
+ name: 'Duau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dva'
+ },
+ {
+ name: 'Diri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwa'
+ },
+ {
+ name: 'Dawik Kui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwk'
+ },
+ {
+ name: 'Dawro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwr'
+ },
+ {
+ name: 'Dutton World Speedwords',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'dws'
+ },
+ {
+ name: 'Dhuwal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwu'
+ },
+ {
+ name: 'Dawawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dww'
+ },
+ {
+ name: 'Dhuwaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwy'
+ },
+ {
+ name: 'Dewas Rai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dwz'
+ },
+ {
+ name: 'Dyan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dya'
+ },
+ {
+ name: 'Dyaberdyaber',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dyb'
+ },
+ {
+ name: 'Dyugun',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dyd'
+ },
+ {
+ name: 'Villa Viciosa Agta',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dyg'
+ },
+ {
+ name: 'Djimini Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dyi'
+ },
+ {
+ name: 'Yanda Dom Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dym'
+ },
+ {
+ name: 'Dyangadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dyn'
+ },
+ {
+ name: 'Jola-Fonyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dyo'
+ },
+ {
+ name: 'Dyula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dyu',
+ iso6392B: 'dyu',
+ iso6392T: 'dyu'
+ },
+ {
+ name: 'Djabugay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dyy'
+ },
+ {
+ name: 'Tunzu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dza'
+ },
+ {
+ name: 'Djiwarli',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'dze'
+ },
+ {
+ name: 'Dazaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dzg'
+ },
+ {
+ name: 'Dzalakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dzl'
+ },
+ {
+ name: 'Dzando',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dzn'
+ },
+ {
+ name: 'Dzongkha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'dzo',
+ iso6392B: 'dzo',
+ iso6392T: 'dzo',
+ iso6391: 'dz'
+ },
+ {
+ name: 'Karenggapa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'eaa'
+ },
+ {
+ name: 'Beginci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebc'
+ },
+ {
+ name: 'Ebughu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebg'
+ },
+ {
+ name: 'Eastern Bontok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebk'
+ },
+ {
+ name: 'Teke-Ebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebo'
+ },
+ {
+ name: 'Ebrié',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebr'
+ },
+ {
+ name: 'Embu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ebu'
+ },
+ {
+ name: 'Eteocretan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'ecr'
+ },
+ {
+ name: 'Ecuadorian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ecs'
+ },
+ {
+ name: 'Eteocypriot',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'ecy'
+ },
+ {
+ name: 'E',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eee'
+ },
+ {
+ name: 'Efai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'efa'
+ },
+ {
+ name: 'Efe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'efe'
+ },
+ {
+ name: 'Efik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'efi',
+ iso6392B: 'efi',
+ iso6392T: 'efi'
+ },
+ {
+ name: 'Ega',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ega'
+ },
+ {
+ name: 'Emilian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'egl'
+ },
+ {
+ name: 'Eggon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ego'
+ },
+ {
+ name: 'Egyptian (Ancient)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'egy',
+ iso6392B: 'egy',
+ iso6392T: 'egy'
+ },
+ {
+ name: 'Ehueun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ehu'
+ },
+ {
+ name: 'Eipomek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eip'
+ },
+ {
+ name: 'Eitiep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eit'
+ },
+ {
+ name: 'Askopan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eiv'
+ },
+ {
+ name: 'Ejamat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eja'
+ },
+ {
+ name: 'Ekajuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eka',
+ iso6392B: 'eka',
+ iso6392T: 'eka'
+ },
+ {
+ name: 'Ekit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eke'
+ },
+ {
+ name: 'Ekari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekg'
+ },
+ {
+ name: 'Eki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eki'
+ },
+ {
+ name: 'Standard Estonian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekk'
+ },
+ {
+ name: 'Kol (Bangladesh)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekl'
+ },
+ {
+ name: 'Elip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekm'
+ },
+ {
+ name: 'Koti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eko'
+ },
+ {
+ name: 'Ekpeye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekp'
+ },
+ {
+ name: 'Yace',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ekr'
+ },
+ {
+ name: 'Eastern Kayah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eky'
+ },
+ {
+ name: 'Elepi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ele'
+ },
+ {
+ name: 'El Hugeirat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'elh'
+ },
+ {
+ name: 'Nding',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'eli'
+ },
+ {
+ name: 'Elkei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'elk'
+ },
+ {
+ name: 'Modern Greek (1453-)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ell',
+ iso6392B: 'gre',
+ iso6392T: 'ell',
+ iso6391: 'el'
+ },
+ {
+ name: 'Eleme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'elm'
+ },
+ {
+ name: 'El Molo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'elo'
+ },
+ {
+ name: 'Elu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'elu'
+ },
+ {
+ name: 'Elamite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'elx',
+ iso6392B: 'elx',
+ iso6392T: 'elx'
+ },
+ {
+ name: 'Emai-Iuleha-Ora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ema'
+ },
+ {
+ name: 'Embaloh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emb'
+ },
+ {
+ name: 'Emerillon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eme'
+ },
+ {
+ name: 'Eastern Meohang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emg'
+ },
+ {
+ name: 'Mussau-Emira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emi'
+ },
+ {
+ name: 'Eastern Maninkakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emk'
+ },
+ {
+ name: 'Mamulique',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'emm'
+ },
+ {
+ name: 'Eman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emn'
+ },
+ {
+ name: 'Northern Emberá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emp'
+ },
+ {
+ name: 'Pacific Gulf Yupik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ems'
+ },
+ {
+ name: 'Eastern Muria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emu'
+ },
+ {
+ name: 'Emplawas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emw'
+ },
+ {
+ name: 'Erromintxela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'emx'
+ },
+ {
+ name: 'Epigraphic Mayan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'emy'
+ },
+ {
+ name: 'Apali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ena'
+ },
+ {
+ name: 'Markweeta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enb'
+ },
+ {
+ name: 'En',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enc'
+ },
+ {
+ name: 'Ende',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'end'
+ },
+ {
+ name: 'Forest Enets',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enf'
+ },
+ {
+ name: 'English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eng',
+ iso6392B: 'eng',
+ iso6392T: 'eng',
+ iso6391: 'en'
+ },
+ {
+ name: 'Tundra Enets',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enh'
+ },
+ {
+ name: 'Enlhet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enl'
+ },
+ {
+ name: 'Middle English (1100-1500)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'enm',
+ iso6392B: 'enm',
+ iso6392T: 'enm'
+ },
+ {
+ name: 'Engenni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enn'
+ },
+ {
+ name: 'Enggano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eno'
+ },
+ {
+ name: 'Enga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enq'
+ },
+ {
+ name: 'Emumu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enr'
+ },
+ {
+ name: 'Enu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enu'
+ },
+ {
+ name: 'Enwan (Edu State)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'env'
+ },
+ {
+ name: 'Enwan (Akwa Ibom State)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enw'
+ },
+ {
+ name: 'Enxet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'enx'
+ },
+ {
+ name: "Beti (Côte d'Ivoire)",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eot'
+ },
+ {
+ name: 'Epie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'epi'
+ },
+ {
+ name: 'Esperanto',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'epo',
+ iso6392B: 'epo',
+ iso6392T: 'epo',
+ iso6391: 'eo'
+ },
+ {
+ name: 'Eravallan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'era'
+ },
+ {
+ name: 'Sie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'erg'
+ },
+ {
+ name: 'Eruwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'erh'
+ },
+ {
+ name: 'Ogea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eri'
+ },
+ {
+ name: 'South Efate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'erk'
+ },
+ {
+ name: 'Horpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ero'
+ },
+ {
+ name: 'Erre',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'err'
+ },
+ {
+ name: 'Ersu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ers'
+ },
+ {
+ name: 'Eritai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ert'
+ },
+ {
+ name: 'Erokwanas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'erw'
+ },
+ {
+ name: 'Ese Ejja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ese'
+ },
+ {
+ name: 'Aheri Gondi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esg'
+ },
+ {
+ name: 'Eshtehardi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esh'
+ },
+ {
+ name: 'North Alaskan Inupiatun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esi'
+ },
+ {
+ name: 'Northwest Alaska Inupiatun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esk'
+ },
+ {
+ name: 'Egypt Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esl'
+ },
+ {
+ name: 'Esuma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'esm'
+ },
+ {
+ name: 'Salvadoran Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esn'
+ },
+ {
+ name: 'Estonian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eso'
+ },
+ {
+ name: 'Esselen',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'esq'
+ },
+ {
+ name: 'Central Siberian Yupik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ess'
+ },
+ {
+ name: 'Estonian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'est',
+ iso6392B: 'est',
+ iso6392T: 'est',
+ iso6391: 'et'
+ },
+ {
+ name: 'Central Yupik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esu'
+ },
+ {
+ name: 'Eskayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'esy'
+ },
+ {
+ name: 'Etebi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etb'
+ },
+ {
+ name: 'Etchemin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'etc'
+ },
+ {
+ name: 'Ethiopian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eth'
+ },
+ {
+ name: 'Eton (Vanuatu)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etn'
+ },
+ {
+ name: 'Eton (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eto'
+ },
+ {
+ name: 'Edolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etr'
+ },
+ {
+ name: 'Yekhee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ets'
+ },
+ {
+ name: 'Etruscan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'ett'
+ },
+ {
+ name: 'Ejagham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etu'
+ },
+ {
+ name: 'Eten',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etx'
+ },
+ {
+ name: 'Semimi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'etz'
+ },
+ {
+ name: 'Basque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eus',
+ iso6392B: 'baq',
+ iso6392T: 'eus',
+ iso6391: 'eu'
+ },
+ {
+ name: 'Even',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eve'
+ },
+ {
+ name: 'Uvbie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'evh'
+ },
+ {
+ name: 'Evenki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'evn'
+ },
+ {
+ name: 'Ewe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ewe',
+ iso6392B: 'ewe',
+ iso6392T: 'ewe',
+ iso6391: 'ee'
+ },
+ {
+ name: 'Ewondo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ewo',
+ iso6392B: 'ewo',
+ iso6392T: 'ewo'
+ },
+ {
+ name: 'Extremaduran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ext'
+ },
+ {
+ name: 'Eyak',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'eya'
+ },
+ {
+ name: 'Keiyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eyo'
+ },
+ {
+ name: 'Ezaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eza'
+ },
+ {
+ name: 'Uzekwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'eze'
+ },
+ {
+ name: 'Fasu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'faa'
+ },
+ {
+ name: "Fa d'Ambu",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fab'
+ },
+ {
+ name: 'Wagi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fad'
+ },
+ {
+ name: 'Fagani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'faf'
+ },
+ {
+ name: 'Finongan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fag'
+ },
+ {
+ name: 'Baissa Fali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fah'
+ },
+ {
+ name: 'Faiwol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fai'
+ },
+ {
+ name: 'Faita',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'faj'
+ },
+ {
+ name: 'Fang (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fak'
+ },
+ {
+ name: 'South Fali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fal'
+ },
+ {
+ name: 'Fam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fam'
+ },
+ {
+ name: 'Fang (Equatorial Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fan',
+ iso6392B: 'fan',
+ iso6392T: 'fan'
+ },
+ {
+ name: 'Faroese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fao',
+ iso6392B: 'fao',
+ iso6392T: 'fao',
+ iso6391: 'fo'
+ },
+ {
+ name: 'Paloor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fap'
+ },
+ {
+ name: 'Fataleka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'far'
+ },
+ {
+ name: 'Persian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'fas',
+ iso6392B: 'per',
+ iso6392T: 'fas',
+ iso6391: 'fa'
+ },
+ {
+ name: 'Fanti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fat',
+ iso6392B: 'fat',
+ iso6392T: 'fat'
+ },
+ {
+ name: 'Fayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fau'
+ },
+ {
+ name: 'Fala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fax'
+ },
+ {
+ name: 'Southwestern Fars',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fay'
+ },
+ {
+ name: 'Northwestern Fars',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'faz'
+ },
+ {
+ name: 'West Albay Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fbl'
+ },
+ {
+ name: 'Quebec Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fcs'
+ },
+ {
+ name: 'Feroge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fer'
+ },
+ {
+ name: 'Foia Foia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ffi'
+ },
+ {
+ name: 'Maasina Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ffm'
+ },
+ {
+ name: 'Fongoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fgr'
+ },
+ {
+ name: 'Nobiin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fia'
+ },
+ {
+ name: 'Fyer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fie'
+ },
+ {
+ name: 'Faifi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fif'
+ },
+ {
+ name: 'Fijian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fij',
+ iso6392B: 'fij',
+ iso6392T: 'fij',
+ iso6391: 'fj'
+ },
+ {
+ name: 'Filipino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fil',
+ iso6392B: 'fil',
+ iso6392T: 'fil'
+ },
+ {
+ name: 'Finnish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fin',
+ iso6392B: 'fin',
+ iso6392T: 'fin',
+ iso6391: 'fi'
+ },
+ {
+ name: 'Fipa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fip'
+ },
+ {
+ name: 'Firan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fir'
+ },
+ {
+ name: 'Tornedalen Finnish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fit'
+ },
+ {
+ name: 'Fiwaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fiw'
+ },
+ {
+ name: 'Kirya-Konzəl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fkk'
+ },
+ {
+ name: 'Kven Finnish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fkv'
+ },
+ {
+ name: "Kalispel-Pend d'Oreille",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fla'
+ },
+ {
+ name: 'Foau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'flh'
+ },
+ {
+ name: 'Fali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fli'
+ },
+ {
+ name: 'North Fali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fll'
+ },
+ {
+ name: 'Flinders Island',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'fln'
+ },
+ {
+ name: 'Fuliiru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'flr'
+ },
+ {
+ name: 'Flaaitaal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fly'
+ },
+ {
+ name: "Fe'fe'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fmp'
+ },
+ {
+ name: 'Far Western Muria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fmu'
+ },
+ {
+ name: 'Fanbak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fnb'
+ },
+ {
+ name: 'Fanagalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fng'
+ },
+ {
+ name: 'Fania',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fni'
+ },
+ {
+ name: 'Foodo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fod'
+ },
+ {
+ name: 'Foi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'foi'
+ },
+ {
+ name: 'Foma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fom'
+ },
+ {
+ name: 'Fon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fon',
+ iso6392B: 'fon',
+ iso6392T: 'fon'
+ },
+ {
+ name: 'Fore',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'for'
+ },
+ {
+ name: 'Siraya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'fos'
+ },
+ {
+ name: 'Fernando Po Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fpe'
+ },
+ {
+ name: 'Fas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fqs'
+ },
+ {
+ name: 'French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fra',
+ iso6392B: 'fre',
+ iso6392T: 'fra',
+ iso6391: 'fr'
+ },
+ {
+ name: 'Cajun French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frc'
+ },
+ {
+ name: 'Fordata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frd'
+ },
+ {
+ name: 'Frankish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'frk'
+ },
+ {
+ name: 'Middle French (ca. 1400-1600)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'frm',
+ iso6392B: 'frm',
+ iso6392T: 'frm'
+ },
+ {
+ name: 'Old French (842-ca. 1400)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'fro',
+ iso6392B: 'fro',
+ iso6392T: 'fro'
+ },
+ {
+ name: 'Arpitan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frp'
+ },
+ {
+ name: 'Forak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frq'
+ },
+ {
+ name: 'Northern Frisian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frr',
+ iso6392B: 'frr',
+ iso6392T: 'frr'
+ },
+ {
+ name: 'Eastern Frisian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frs',
+ iso6392B: 'frs',
+ iso6392T: 'frs'
+ },
+ {
+ name: 'Fortsenal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'frt'
+ },
+ {
+ name: 'Western Frisian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fry',
+ iso6392B: 'fry',
+ iso6392T: 'fry',
+ iso6391: 'fy'
+ },
+ {
+ name: 'Finnish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fse'
+ },
+ {
+ name: 'French Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fsl'
+ },
+ {
+ name: 'Finland-Swedish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fss'
+ },
+ {
+ name: 'Adamawa Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fub'
+ },
+ {
+ name: 'Pulaar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuc'
+ },
+ {
+ name: 'East Futuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fud'
+ },
+ {
+ name: 'Borgu Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fue'
+ },
+ {
+ name: 'Pular',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuf'
+ },
+ {
+ name: 'Western Niger Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuh'
+ },
+ {
+ name: 'Bagirmi Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fui'
+ },
+ {
+ name: 'Ko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuj'
+ },
+ {
+ name: 'Fulah',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'ful',
+ iso6392B: 'ful',
+ iso6392T: 'ful',
+ iso6391: 'ff'
+ },
+ {
+ name: 'Fum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fum'
+ },
+ {
+ name: 'Fulniô',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fun'
+ },
+ {
+ name: 'Central-Eastern Niger Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuq'
+ },
+ {
+ name: 'Friulian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fur',
+ iso6392B: 'fur',
+ iso6392T: 'fur'
+ },
+ {
+ name: 'Futuna-Aniwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fut'
+ },
+ {
+ name: 'Furu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuu'
+ },
+ {
+ name: 'Nigerian Fulfulde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuv'
+ },
+ {
+ name: 'Fuyug',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fuy'
+ },
+ {
+ name: 'Fur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fvr'
+ },
+ {
+ name: 'Fwâi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fwa'
+ },
+ {
+ name: 'Fwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'fwe'
+ },
+ {
+ name: 'Ga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaa',
+ iso6392B: 'gaa',
+ iso6392T: 'gaa'
+ },
+ {
+ name: 'Gabri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gab'
+ },
+ {
+ name: 'Mixed Great Andamanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gac'
+ },
+ {
+ name: 'Gaddang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gad'
+ },
+ {
+ name: 'Guarequena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gae'
+ },
+ {
+ name: 'Gende',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaf'
+ },
+ {
+ name: 'Gagauz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gag'
+ },
+ {
+ name: 'Alekano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gah'
+ },
+ {
+ name: 'Borei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gai'
+ },
+ {
+ name: 'Gadsup',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaj'
+ },
+ {
+ name: 'Gamkonora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gak'
+ },
+ {
+ name: 'Galolen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gal'
+ },
+ {
+ name: 'Kandawo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gam'
+ },
+ {
+ name: 'Gan Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gan'
+ },
+ {
+ name: 'Gants',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gao'
+ },
+ {
+ name: 'Gal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gap'
+ },
+ {
+ name: "Gata'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaq'
+ },
+ {
+ name: 'Galeya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gar'
+ },
+ {
+ name: 'Adiwasi Garasia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gas'
+ },
+ {
+ name: 'Kenati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gat'
+ },
+ {
+ name: 'Mudhili Gadaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gau'
+ },
+ {
+ name: 'Nobonob',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaw'
+ },
+ {
+ name: 'Borana-Arsi-Guji Oromo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gax'
+ },
+ {
+ name: 'Gayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gay',
+ iso6392B: 'gay',
+ iso6392T: 'gay'
+ },
+ {
+ name: 'West Central Oromo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gaz'
+ },
+ {
+ name: 'Gbaya (Central African Republic)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'gba',
+ iso6392B: 'gba',
+ iso6392T: 'gba'
+ },
+ {
+ name: 'Kaytetye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbb'
+ },
+ {
+ name: 'Karajarri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbd'
+ },
+ {
+ name: 'Niksek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbe'
+ },
+ {
+ name: 'Gaikundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbf'
+ },
+ {
+ name: 'Gbanziri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbg'
+ },
+ {
+ name: 'Defi Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbh'
+ },
+ {
+ name: 'Galela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbi'
+ },
+ {
+ name: 'Bodo Gadaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbj'
+ },
+ {
+ name: 'Gaddi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbk'
+ },
+ {
+ name: 'Gamit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbl'
+ },
+ {
+ name: 'Garhwali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbm'
+ },
+ {
+ name: "Mo'da",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbn'
+ },
+ {
+ name: 'Northern Grebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbo'
+ },
+ {
+ name: 'Gbaya-Bossangoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbp'
+ },
+ {
+ name: 'Gbaya-Bozoum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbq'
+ },
+ {
+ name: 'Gbagyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbr'
+ },
+ {
+ name: 'Gbesi Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbs'
+ },
+ {
+ name: 'Gagadu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbu'
+ },
+ {
+ name: 'Gbanu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbv'
+ },
+ {
+ name: 'Gabi-Gabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbw'
+ },
+ {
+ name: 'Eastern Xwla Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbx'
+ },
+ {
+ name: 'Gbari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gby'
+ },
+ {
+ name: 'Zoroastrian Dari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gbz'
+ },
+ {
+ name: 'Mali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gcc'
+ },
+ {
+ name: 'Ganggalida',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gcd'
+ },
+ {
+ name: 'Galice',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gce'
+ },
+ {
+ name: 'Guadeloupean Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gcf'
+ },
+ {
+ name: 'Grenadian Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gcl'
+ },
+ {
+ name: 'Gaina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gcn'
+ },
+ {
+ name: 'Guianese Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gcr'
+ },
+ {
+ name: 'Colonia Tovar German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gct'
+ },
+ {
+ name: 'Gade Lohar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gda'
+ },
+ {
+ name: 'Pottangi Ollar Gadaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdb'
+ },
+ {
+ name: 'Gugu Badhun',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gdc'
+ },
+ {
+ name: 'Gedaged',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdd'
+ },
+ {
+ name: 'Gude',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gde'
+ },
+ {
+ name: 'Guduf-Gava',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdf'
+ },
+ {
+ name: "Ga'dang",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdg'
+ },
+ {
+ name: 'Gadjerawang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdh'
+ },
+ {
+ name: 'Gundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdi'
+ },
+ {
+ name: 'Gurdjar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdj'
+ },
+ {
+ name: 'Gadang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdk'
+ },
+ {
+ name: 'Dirasha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdl'
+ },
+ {
+ name: 'Laal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdm'
+ },
+ {
+ name: 'Umanakaina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdn'
+ },
+ {
+ name: 'Ghodoberi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdo'
+ },
+ {
+ name: 'Mehri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdq'
+ },
+ {
+ name: 'Wipi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdr'
+ },
+ {
+ name: 'Ghandruk Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gds'
+ },
+ {
+ name: 'Kungardutyi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gdt'
+ },
+ {
+ name: 'Gudu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdu'
+ },
+ {
+ name: 'Godwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gdx'
+ },
+ {
+ name: 'Geruma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gea'
+ },
+ {
+ name: 'Kire',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'geb'
+ },
+ {
+ name: 'Gboloo Grebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gec'
+ },
+ {
+ name: 'Gade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ged'
+ },
+ {
+ name: 'Gerai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gef'
+ },
+ {
+ name: 'Gengle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'geg'
+ },
+ {
+ name: 'Hutterite German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'geh'
+ },
+ {
+ name: 'Gebe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gei'
+ },
+ {
+ name: 'Gen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gej'
+ },
+ {
+ name: 'Ywom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gek'
+ },
+ {
+ name: "ut-Ma'in",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gel'
+ },
+ {
+ name: 'Geme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'geq'
+ },
+ {
+ name: 'Geser-Gorom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ges'
+ },
+ {
+ name: 'Eviya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gev'
+ },
+ {
+ name: 'Gera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gew'
+ },
+ {
+ name: 'Garre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gex'
+ },
+ {
+ name: 'Enya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gey'
+ },
+ {
+ name: 'Geez',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'gez',
+ iso6392B: 'gez',
+ iso6392T: 'gez'
+ },
+ {
+ name: 'Patpatar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gfk'
+ },
+ {
+ name: 'Gafat',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gft'
+ },
+ {
+ name: 'Gao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gga'
+ },
+ {
+ name: 'Gbii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggb'
+ },
+ {
+ name: 'Gugadj',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ggd'
+ },
+ {
+ name: 'Gurr-goni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gge'
+ },
+ {
+ name: 'Gurgula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggg'
+ },
+ {
+ name: 'Kungarakany',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ggk'
+ },
+ {
+ name: 'Ganglau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggl'
+ },
+ {
+ name: 'Gitua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggt'
+ },
+ {
+ name: 'Gagu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggu'
+ },
+ {
+ name: 'Gogodala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ggw'
+ },
+ {
+ name: 'Ghadamès',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gha'
+ },
+ {
+ name: 'Hiberno-Scottish Gaelic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ghc'
+ },
+ {
+ name: 'Southern Ghale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghe'
+ },
+ {
+ name: 'Northern Ghale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghh'
+ },
+ {
+ name: 'Geko Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghk'
+ },
+ {
+ name: 'Ghulfan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghl'
+ },
+ {
+ name: 'Ghanongga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghn'
+ },
+ {
+ name: 'Ghomara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gho'
+ },
+ {
+ name: 'Ghera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghr'
+ },
+ {
+ name: 'Guhu-Samane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ghs'
+ },
+ {
+ name: 'Kuke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ght'
+ },
+ {
+ name: 'Kija',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gia'
+ },
+ {
+ name: 'Gibanawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gib'
+ },
+ {
+ name: 'Gail',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gic'
+ },
+ {
+ name: 'Gidar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gid'
+ },
+ {
+ name: 'Gaɓogbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gie'
+ },
+ {
+ name: 'Goaria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gig'
+ },
+ {
+ name: 'Githabul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gih'
+ },
+ {
+ name: 'Gilbertese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gil',
+ iso6392B: 'gil',
+ iso6392T: 'gil'
+ },
+ {
+ name: 'Gimi (Eastern Highlands)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gim'
+ },
+ {
+ name: 'Hinukh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gin'
+ },
+ {
+ name: 'Gimi (West New Britain)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gip'
+ },
+ {
+ name: 'Green Gelao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'giq'
+ },
+ {
+ name: 'Red Gelao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gir'
+ },
+ {
+ name: 'North Giziga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gis'
+ },
+ {
+ name: 'Gitxsan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'git'
+ },
+ {
+ name: 'Mulao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'giu'
+ },
+ {
+ name: 'White Gelao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'giw'
+ },
+ {
+ name: 'Gilima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gix'
+ },
+ {
+ name: 'Giyug',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'giy'
+ },
+ {
+ name: 'South Giziga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'giz'
+ },
+ {
+ name: 'Geji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gji'
+ },
+ {
+ name: 'Kachi Koli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gjk'
+ },
+ {
+ name: 'Gunditjmara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gjm'
+ },
+ {
+ name: 'Gonja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gjn'
+ },
+ {
+ name: 'Gurindji Kriol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gjr'
+ },
+ {
+ name: 'Gujari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gju'
+ },
+ {
+ name: 'Guya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gka'
+ },
+ {
+ name: 'Magɨ (Madang Province)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gkd'
+ },
+ {
+ name: 'Ndai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gke'
+ },
+ {
+ name: 'Gokana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gkn'
+ },
+ {
+ name: 'Kok-Nar',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gko'
+ },
+ {
+ name: 'Guinea Kpelle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gkp'
+ },
+ {
+ name: 'ǂUngkue',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gku'
+ },
+ {
+ name: 'Scottish Gaelic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gla',
+ iso6392B: 'gla',
+ iso6392T: 'gla',
+ iso6391: 'gd'
+ },
+ {
+ name: 'Bon Gula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glc'
+ },
+ {
+ name: 'Nanai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gld'
+ },
+ {
+ name: 'Irish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gle',
+ iso6392B: 'gle',
+ iso6392T: 'gle',
+ iso6391: 'ga'
+ },
+ {
+ name: 'Galician',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glg',
+ iso6392B: 'glg',
+ iso6392T: 'glg',
+ iso6391: 'gl'
+ },
+ {
+ name: 'Northwest Pashai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glh'
+ },
+ {
+ name: 'Gula Iro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glj'
+ },
+ {
+ name: 'Gilaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glk'
+ },
+ {
+ name: 'Garlali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gll'
+ },
+ {
+ name: 'Galambu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glo'
+ },
+ {
+ name: 'Glaro-Twabo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glr'
+ },
+ {
+ name: 'Gula (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glu'
+ },
+ {
+ name: 'Manx',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glv',
+ iso6392B: 'glv',
+ iso6392T: 'glv',
+ iso6391: 'gv'
+ },
+ {
+ name: 'Glavda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'glw'
+ },
+ {
+ name: 'Gule',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gly'
+ },
+ {
+ name: 'Gambera',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gma'
+ },
+ {
+ name: "Gula'alaa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmb'
+ },
+ {
+ name: 'Mághdì',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmd'
+ },
+ {
+ name: 'Magɨyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmg'
+ },
+ {
+ name: 'Middle High German (ca. 1050-1500)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'gmh',
+ iso6392B: 'gmh',
+ iso6392T: 'gmh'
+ },
+ {
+ name: 'Middle Low German',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'gml'
+ },
+ {
+ name: 'Gbaya-Mbodomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmm'
+ },
+ {
+ name: 'Gimnime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmn'
+ },
+ {
+ name: 'Mirning',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmr'
+ },
+ {
+ name: 'Gumalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmu'
+ },
+ {
+ name: 'Gamo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmv'
+ },
+ {
+ name: 'Magoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmx'
+ },
+ {
+ name: 'Mycenaean Greek',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'gmy'
+ },
+ {
+ name: 'Mgbolizhia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gmz'
+ },
+ {
+ name: 'Kaansa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gna'
+ },
+ {
+ name: 'Gangte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnb'
+ },
+ {
+ name: 'Guanche',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gnc'
+ },
+ {
+ name: 'Zulgo-Gemzek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnd'
+ },
+ {
+ name: 'Ganang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gne'
+ },
+ {
+ name: 'Ngangam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gng'
+ },
+ {
+ name: 'Lere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnh'
+ },
+ {
+ name: 'Gooniyandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gni'
+ },
+ {
+ name: 'Ngen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnj'
+ },
+ {
+ name: 'ǁGana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnk'
+ },
+ {
+ name: 'Gangulu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gnl'
+ },
+ {
+ name: 'Ginuman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnm'
+ },
+ {
+ name: 'Gumatj',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnn'
+ },
+ {
+ name: 'Northern Gondi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gno'
+ },
+ {
+ name: 'Gana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnq'
+ },
+ {
+ name: 'Gureng Gureng',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gnr'
+ },
+ {
+ name: 'Guntai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnt'
+ },
+ {
+ name: 'Gnau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnu'
+ },
+ {
+ name: 'Western Bolivian Guaraní',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnw'
+ },
+ {
+ name: 'Ganzi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gnz'
+ },
+ {
+ name: 'Guro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goa'
+ },
+ {
+ name: 'Playero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gob'
+ },
+ {
+ name: 'Gorakor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goc'
+ },
+ {
+ name: 'Godié',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'god'
+ },
+ {
+ name: 'Gongduk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goe'
+ },
+ {
+ name: 'Gofa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gof'
+ },
+ {
+ name: 'Gogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gog'
+ },
+ {
+ name: 'Old High German (ca. 750-1050)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'goh',
+ iso6392B: 'goh',
+ iso6392T: 'goh'
+ },
+ {
+ name: 'Gobasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goi'
+ },
+ {
+ name: 'Gowlan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goj'
+ },
+ {
+ name: 'Gowli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gok'
+ },
+ {
+ name: 'Gola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gol'
+ },
+ {
+ name: 'Goan Konkani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gom'
+ },
+ {
+ name: 'Gondi',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'gon',
+ iso6392B: 'gon',
+ iso6392T: 'gon'
+ },
+ {
+ name: 'Gone Dau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goo'
+ },
+ {
+ name: 'Yeretuar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gop'
+ },
+ {
+ name: 'Gorap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goq'
+ },
+ {
+ name: 'Gorontalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gor',
+ iso6392B: 'gor',
+ iso6392T: 'gor'
+ },
+ {
+ name: 'Gronings',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gos'
+ },
+ {
+ name: 'Gothic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'got',
+ iso6392B: 'got',
+ iso6392T: 'got'
+ },
+ {
+ name: 'Gavar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gou'
+ },
+ {
+ name: 'Gorowa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gow'
+ },
+ {
+ name: 'Gobu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gox'
+ },
+ {
+ name: 'Goundo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goy'
+ },
+ {
+ name: 'Gozarkhani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'goz'
+ },
+ {
+ name: 'Gupa-Abawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gpa'
+ },
+ {
+ name: 'Ghanaian Pidgin English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gpe'
+ },
+ {
+ name: 'Taiap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gpn'
+ },
+ {
+ name: "Ga'anda",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gqa'
+ },
+ {
+ name: 'Guiqiong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gqi'
+ },
+ {
+ name: 'Guana (Brazil)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gqn'
+ },
+ {
+ name: 'Gor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gqr'
+ },
+ {
+ name: 'Qau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gqu'
+ },
+ {
+ name: 'Rajput Garasia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gra'
+ },
+ {
+ name: 'Grebo',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'grb',
+ iso6392B: 'grb',
+ iso6392T: 'grb'
+ },
+ {
+ name: 'Ancient Greek (to 1453)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'grc',
+ iso6392B: 'grc',
+ iso6392T: 'grc'
+ },
+ {
+ name: 'Guruntum-Mbaaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grd'
+ },
+ {
+ name: 'Madi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grg'
+ },
+ {
+ name: 'Gbiri-Niragu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grh'
+ },
+ {
+ name: 'Ghari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gri'
+ },
+ {
+ name: 'Southern Grebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grj'
+ },
+ {
+ name: 'Kota Marudu Talantang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grm'
+ },
+ {
+ name: 'Guarani',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'grn',
+ iso6392B: 'grn',
+ iso6392T: 'grn',
+ iso6391: 'gn'
+ },
+ {
+ name: 'Groma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gro'
+ },
+ {
+ name: 'Gorovu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grq'
+ },
+ {
+ name: 'Taznatit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grr'
+ },
+ {
+ name: 'Gresi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grs'
+ },
+ {
+ name: 'Garo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grt'
+ },
+ {
+ name: 'Kistane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gru'
+ },
+ {
+ name: 'Central Grebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grv'
+ },
+ {
+ name: 'Gweda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grw'
+ },
+ {
+ name: 'Guriaso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grx'
+ },
+ {
+ name: 'Barclayville Grebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gry'
+ },
+ {
+ name: 'Guramalum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'grz'
+ },
+ {
+ name: 'Ghanaian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gse'
+ },
+ {
+ name: 'German Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsg'
+ },
+ {
+ name: 'Gusilay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsl'
+ },
+ {
+ name: 'Guatemalan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsm'
+ },
+ {
+ name: 'Nema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsn'
+ },
+ {
+ name: 'Southwest Gbaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gso'
+ },
+ {
+ name: 'Wasembo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsp'
+ },
+ {
+ name: 'Greek Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gss'
+ },
+ {
+ name: 'Swiss German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gsw',
+ iso6392B: 'gsw',
+ iso6392T: 'gsw'
+ },
+ {
+ name: 'Guató',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gta'
+ },
+ {
+ name: 'Aghu-Tharnggala',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gtu'
+ },
+ {
+ name: 'Shiki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gua'
+ },
+ {
+ name: 'Guajajára',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gub'
+ },
+ {
+ name: 'Wayuu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guc'
+ },
+ {
+ name: 'Yocoboué Dida',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gud'
+ },
+ {
+ name: 'Gurindji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gue'
+ },
+ {
+ name: 'Gupapuyngu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guf'
+ },
+ {
+ name: 'Paraguayan Guaraní',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gug'
+ },
+ {
+ name: 'Guahibo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guh'
+ },
+ {
+ name: 'Eastern Bolivian Guaraní',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gui'
+ },
+ {
+ name: 'Gujarati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guj',
+ iso6392B: 'guj',
+ iso6392T: 'guj',
+ iso6391: 'gu'
+ },
+ {
+ name: 'Gumuz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guk'
+ },
+ {
+ name: 'Sea Island Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gul'
+ },
+ {
+ name: 'Guambiano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gum'
+ },
+ {
+ name: 'Mbyá Guaraní',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gun'
+ },
+ {
+ name: 'Guayabero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guo'
+ },
+ {
+ name: 'Gunwinggu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gup'
+ },
+ {
+ name: 'Aché',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guq'
+ },
+ {
+ name: 'Farefare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gur'
+ },
+ {
+ name: 'Guinean Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gus'
+ },
+ {
+ name: 'Maléku Jaíka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gut'
+ },
+ {
+ name: 'Yanomamö',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guu'
+ },
+ {
+ name: 'Gun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guw'
+ },
+ {
+ name: 'Gourmanchéma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gux'
+ },
+ {
+ name: 'Gusii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'guz'
+ },
+ {
+ name: 'Guana (Paraguay)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gva'
+ },
+ {
+ name: 'Guanano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvc'
+ },
+ {
+ name: 'Duwet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gve'
+ },
+ {
+ name: 'Golin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvf'
+ },
+ {
+ name: 'Guajá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvj'
+ },
+ {
+ name: 'Gulay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvl'
+ },
+ {
+ name: 'Gurmana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvm'
+ },
+ {
+ name: 'Kuku-Yalanji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvn'
+ },
+ {
+ name: 'Gavião Do Jiparaná',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvo'
+ },
+ {
+ name: 'Pará Gavião',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvp'
+ },
+ {
+ name: 'Gurung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvr'
+ },
+ {
+ name: 'Gumawana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gvs'
+ },
+ {
+ name: 'Guyani',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gvy'
+ },
+ {
+ name: 'Mbato',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwa'
+ },
+ {
+ name: 'Gwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwb'
+ },
+ {
+ name: 'Gawri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwc'
+ },
+ {
+ name: 'Gawwada',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwd'
+ },
+ {
+ name: 'Gweno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwe'
+ },
+ {
+ name: 'Gowro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwf'
+ },
+ {
+ name: 'Moo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwg'
+ },
+ {
+ name: 'Gwichʼin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwi',
+ iso6392B: 'gwi',
+ iso6392T: 'gwi'
+ },
+ {
+ name: 'ǀGwi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwj'
+ },
+ {
+ name: 'Awngthim',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gwm'
+ },
+ {
+ name: 'Gwandara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwn'
+ },
+ {
+ name: 'Gwere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwr'
+ },
+ {
+ name: 'Gawar-Bati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwt'
+ },
+ {
+ name: 'Guwamu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gwu'
+ },
+ {
+ name: 'Kwini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gww'
+ },
+ {
+ name: 'Gua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gwx'
+ },
+ {
+ name: 'Wè Southern',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gxx'
+ },
+ {
+ name: 'Northwest Gbaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gya'
+ },
+ {
+ name: 'Garus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyb'
+ },
+ {
+ name: 'Kayardild',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyd'
+ },
+ {
+ name: 'Gyem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gye'
+ },
+ {
+ name: 'Gungabula',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gyf'
+ },
+ {
+ name: 'Gbayi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyg'
+ },
+ {
+ name: 'Gyele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyi'
+ },
+ {
+ name: 'Gayil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyl'
+ },
+ {
+ name: 'Ngäbere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gym'
+ },
+ {
+ name: 'Guyanese Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyn'
+ },
+ {
+ name: 'Gyalsumdo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyo'
+ },
+ {
+ name: 'Guarayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gyr'
+ },
+ {
+ name: 'Gunya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'gyy'
+ },
+ {
+ name: 'Ganza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gza'
+ },
+ {
+ name: 'Gazi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gzi'
+ },
+ {
+ name: 'Gane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'gzn'
+ },
+ {
+ name: 'Han',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haa'
+ },
+ {
+ name: 'Hanoi Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hab'
+ },
+ {
+ name: 'Gurani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hac'
+ },
+ {
+ name: 'Hatam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'had'
+ },
+ {
+ name: 'Eastern Oromo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hae'
+ },
+ {
+ name: 'Haiphong Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haf'
+ },
+ {
+ name: 'Hanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hag'
+ },
+ {
+ name: 'Hahon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hah'
+ },
+ {
+ name: 'Haida',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'hai',
+ iso6392B: 'hai',
+ iso6392T: 'hai'
+ },
+ {
+ name: 'Hajong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haj'
+ },
+ {
+ name: 'Hakka Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hak'
+ },
+ {
+ name: 'Halang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hal'
+ },
+ {
+ name: 'Hewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ham'
+ },
+ {
+ name: 'Hangaza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'han'
+ },
+ {
+ name: 'Hakö',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hao'
+ },
+ {
+ name: 'Hupla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hap'
+ },
+ {
+ name: 'Ha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haq'
+ },
+ {
+ name: 'Harari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'har'
+ },
+ {
+ name: 'Haisla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'has'
+ },
+ {
+ name: 'Haitian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hat',
+ iso6392B: 'hat',
+ iso6392T: 'hat',
+ iso6391: 'ht'
+ },
+ {
+ name: 'Hausa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hau',
+ iso6392B: 'hau',
+ iso6392T: 'hau',
+ iso6391: 'ha'
+ },
+ {
+ name: 'Havu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hav'
+ },
+ {
+ name: 'Hawaiian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haw',
+ iso6392B: 'haw',
+ iso6392T: 'haw'
+ },
+ {
+ name: 'Southern Haida',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hax'
+ },
+ {
+ name: 'Haya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hay'
+ },
+ {
+ name: 'Hazaragi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'haz'
+ },
+ {
+ name: 'Hamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hba'
+ },
+ {
+ name: 'Huba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hbb'
+ },
+ {
+ name: 'Heiban',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hbn'
+ },
+ {
+ name: 'Ancient Hebrew',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'hbo'
+ },
+ {
+ name: 'Serbo-Croatian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'hbs',
+ iso6391: 'sh'
+ },
+ {
+ name: 'Habu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hbu'
+ },
+ {
+ name: 'Andaman Creole Hindi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hca'
+ },
+ {
+ name: 'Huichol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hch'
+ },
+ {
+ name: 'Northern Haida',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hdn'
+ },
+ {
+ name: 'Honduras Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hds'
+ },
+ {
+ name: 'Hadiyya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hdy'
+ },
+ {
+ name: 'Northern Qiandong Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hea'
+ },
+ {
+ name: 'Hebrew',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'heb',
+ iso6392B: 'heb',
+ iso6392T: 'heb',
+ iso6391: 'he'
+ },
+ {
+ name: 'Herdé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hed'
+ },
+ {
+ name: 'Helong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'heg'
+ },
+ {
+ name: 'Hehe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'heh'
+ },
+ {
+ name: 'Heiltsuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hei'
+ },
+ {
+ name: 'Hemba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hem'
+ },
+ {
+ name: 'Herero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'her',
+ iso6392B: 'her',
+ iso6392T: 'her',
+ iso6391: 'hz'
+ },
+ {
+ name: 'Haiǁom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hgm'
+ },
+ {
+ name: 'Haigwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hgw'
+ },
+ {
+ name: 'Hoia Hoia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hhi'
+ },
+ {
+ name: 'Kerak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hhr'
+ },
+ {
+ name: 'Hoyahoya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hhy'
+ },
+ {
+ name: 'Lamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hia'
+ },
+ {
+ name: 'Hibito',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hib'
+ },
+ {
+ name: 'Hidatsa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hid'
+ },
+ {
+ name: 'Fiji Hindi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hif'
+ },
+ {
+ name: 'Kamwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hig'
+ },
+ {
+ name: 'Pamosu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hih'
+ },
+ {
+ name: 'Hinduri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hii'
+ },
+ {
+ name: 'Hijuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hij'
+ },
+ {
+ name: 'Seit-Kaitetu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hik'
+ },
+ {
+ name: 'Hiligaynon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hil',
+ iso6392B: 'hil',
+ iso6392T: 'hil'
+ },
+ {
+ name: 'Hindi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hin',
+ iso6392B: 'hin',
+ iso6392T: 'hin',
+ iso6391: 'hi'
+ },
+ {
+ name: 'Tsoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hio'
+ },
+ {
+ name: 'Himarimã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hir'
+ },
+ {
+ name: 'Hittite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'hit',
+ iso6392B: 'hit',
+ iso6392T: 'hit'
+ },
+ {
+ name: 'Hiw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hiw'
+ },
+ {
+ name: 'Hixkaryána',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hix'
+ },
+ {
+ name: 'Haji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hji'
+ },
+ {
+ name: 'Kahe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hka'
+ },
+ {
+ name: 'Hunde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hke'
+ },
+ {
+ name: 'Hunjara-Kaina Ke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hkk'
+ },
+ {
+ name: 'Mel-Khaonh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hkn'
+ },
+ {
+ name: 'Hong Kong Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hks'
+ },
+ {
+ name: 'Halia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hla'
+ },
+ {
+ name: 'Halbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hlb'
+ },
+ {
+ name: 'Halang Doan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hld'
+ },
+ {
+ name: 'Hlersu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hle'
+ },
+ {
+ name: 'Matu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hlt'
+ },
+ {
+ name: 'Hieroglyphic Luwian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'hlu'
+ },
+ {
+ name: 'Southern Mashan Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hma'
+ },
+ {
+ name: 'Humburi Senni Songhay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmb'
+ },
+ {
+ name: 'Central Huishui Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmc'
+ },
+ {
+ name: 'Large Flowery Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmd'
+ },
+ {
+ name: 'Eastern Huishui Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hme'
+ },
+ {
+ name: 'Hmong Don',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmf'
+ },
+ {
+ name: 'Southwestern Guiyang Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmg'
+ },
+ {
+ name: 'Southwestern Huishui Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmh'
+ },
+ {
+ name: 'Northern Huishui Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmi'
+ },
+ {
+ name: 'Ge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmj'
+ },
+ {
+ name: 'Maek',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'hmk'
+ },
+ {
+ name: 'Luopohe Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hml'
+ },
+ {
+ name: 'Central Mashan Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmm'
+ },
+ {
+ name: 'Hmong',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'hmn',
+ iso6392B: 'hmn',
+ iso6392T: 'hmn'
+ },
+ {
+ name: 'Hiri Motu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmo',
+ iso6392B: 'hmo',
+ iso6392T: 'hmo',
+ iso6391: 'ho'
+ },
+ {
+ name: 'Northern Mashan Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmp'
+ },
+ {
+ name: 'Eastern Qiandong Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmq'
+ },
+ {
+ name: 'Hmar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmr'
+ },
+ {
+ name: 'Southern Qiandong Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hms'
+ },
+ {
+ name: 'Hamtai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmt'
+ },
+ {
+ name: 'Hamap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmu'
+ },
+ {
+ name: 'Hmong Dô',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmv'
+ },
+ {
+ name: 'Western Mashan Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmw'
+ },
+ {
+ name: 'Southern Guiyang Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmy'
+ },
+ {
+ name: 'Hmong Shua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hmz'
+ },
+ {
+ name: 'Mina (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hna'
+ },
+ {
+ name: 'Southern Hindko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hnd'
+ },
+ {
+ name: 'Chhattisgarhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hne'
+ },
+ {
+ name: 'Hungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hng'
+ },
+ {
+ name: 'ǁAni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hnh'
+ },
+ {
+ name: 'Hani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hni'
+ },
+ {
+ name: 'Hmong Njua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hnj'
+ },
+ {
+ name: 'Hanunoo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hnn'
+ },
+ {
+ name: 'Northern Hindko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hno'
+ },
+ {
+ name: 'Caribbean Hindustani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hns'
+ },
+ {
+ name: 'Hung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hnu'
+ },
+ {
+ name: 'Hoava',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoa'
+ },
+ {
+ name: 'Mari (Madang Province)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hob'
+ },
+ {
+ name: 'Ho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoc'
+ },
+ {
+ name: 'Holma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hod'
+ },
+ {
+ name: 'Horom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoe'
+ },
+ {
+ name: 'Hobyót',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoh'
+ },
+ {
+ name: 'Holikachuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoi'
+ },
+ {
+ name: 'Hadothi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoj'
+ },
+ {
+ name: 'Holu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hol'
+ },
+ {
+ name: 'Homa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hom'
+ },
+ {
+ name: 'Holoholo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoo'
+ },
+ {
+ name: 'Hopi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hop'
+ },
+ {
+ name: 'Horo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hor'
+ },
+ {
+ name: 'Ho Chi Minh City Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hos'
+ },
+ {
+ name: 'Hote',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hot'
+ },
+ {
+ name: 'Hovongan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hov'
+ },
+ {
+ name: 'Honi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'how'
+ },
+ {
+ name: 'Holiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoy'
+ },
+ {
+ name: 'Hozo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hoz'
+ },
+ {
+ name: 'Hpon',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hpo'
+ },
+ {
+ name: "Hawai'i Sign Language (HSL)",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hps'
+ },
+ {
+ name: 'Hrangkhol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hra'
+ },
+ {
+ name: 'Niwer Mil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrc'
+ },
+ {
+ name: 'Hre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hre'
+ },
+ {
+ name: 'Haruku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrk'
+ },
+ {
+ name: 'Horned Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrm'
+ },
+ {
+ name: 'Haroi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hro'
+ },
+ {
+ name: 'Nhirrpi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hrp'
+ },
+ {
+ name: 'Hértevin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrt'
+ },
+ {
+ name: 'Hruso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hru'
+ },
+ {
+ name: 'Croatian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrv',
+ iso6392B: 'hrv',
+ iso6392T: 'hrv',
+ iso6391: 'hr'
+ },
+ {
+ name: 'Warwar Feni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrw'
+ },
+ {
+ name: 'Hunsrik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrx'
+ },
+ {
+ name: 'Harzani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hrz'
+ },
+ {
+ name: 'Upper Sorbian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hsb',
+ iso6392B: 'hsb',
+ iso6392T: 'hsb'
+ },
+ {
+ name: 'Hungarian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hsh'
+ },
+ {
+ name: 'Hausa Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hsl'
+ },
+ {
+ name: 'Xiang Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hsn'
+ },
+ {
+ name: 'Harsusi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hss'
+ },
+ {
+ name: 'Hoti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'hti'
+ },
+ {
+ name: 'Minica Huitoto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hto'
+ },
+ {
+ name: 'Hadza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hts'
+ },
+ {
+ name: 'Hitu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'htu'
+ },
+ {
+ name: 'Middle Hittite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'htx'
+ },
+ {
+ name: 'Huambisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hub'
+ },
+ {
+ name: 'ǂHua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huc'
+ },
+ {
+ name: 'Huaulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hud'
+ },
+ {
+ name: 'San Francisco Del Mar Huave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hue'
+ },
+ {
+ name: 'Humene',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huf'
+ },
+ {
+ name: 'Huachipaeri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hug'
+ },
+ {
+ name: 'Huilliche',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huh'
+ },
+ {
+ name: 'Huli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hui'
+ },
+ {
+ name: 'Northern Guiyang Hmong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huj'
+ },
+ {
+ name: 'Hulung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'huk'
+ },
+ {
+ name: 'Hula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hul'
+ },
+ {
+ name: 'Hungana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hum'
+ },
+ {
+ name: 'Hungarian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hun',
+ iso6392B: 'hun',
+ iso6392T: 'hun',
+ iso6391: 'hu'
+ },
+ {
+ name: 'Hu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huo'
+ },
+ {
+ name: 'Hupa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hup',
+ iso6392B: 'hup',
+ iso6392T: 'hup'
+ },
+ {
+ name: 'Tsat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huq'
+ },
+ {
+ name: 'Halkomelem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hur'
+ },
+ {
+ name: 'Huastec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hus'
+ },
+ {
+ name: 'Humla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hut'
+ },
+ {
+ name: 'Murui Huitoto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huu'
+ },
+ {
+ name: 'San Mateo Del Mar Huave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huv'
+ },
+ {
+ name: 'Hukumina',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'huw'
+ },
+ {
+ name: 'Nüpode Huitoto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hux'
+ },
+ {
+ name: 'Hulaulá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huy'
+ },
+ {
+ name: 'Hunzib',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'huz'
+ },
+ {
+ name: 'Haitian Vodoun Culture Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hvc'
+ },
+ {
+ name: 'San Dionisio Del Mar Huave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hve'
+ },
+ {
+ name: 'Haveke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hvk'
+ },
+ {
+ name: 'Sabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hvn'
+ },
+ {
+ name: 'Santa María Del Mar Huave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hvv'
+ },
+ {
+ name: 'Wané',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hwa'
+ },
+ {
+ name: "Hawai'i Creole English",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hwc'
+ },
+ {
+ name: 'Hwana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hwo'
+ },
+ {
+ name: 'Hya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hya'
+ },
+ {
+ name: 'Armenian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hye',
+ iso6392B: 'arm',
+ iso6392T: 'hye',
+ iso6391: 'hy'
+ },
+ {
+ name: 'Western Armenian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'hyw'
+ },
+ {
+ name: 'Iaai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iai'
+ },
+ {
+ name: 'Iatmul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ian'
+ },
+ {
+ name: 'Purari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iar'
+ },
+ {
+ name: 'Iban',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iba',
+ iso6392B: 'iba',
+ iso6392T: 'iba'
+ },
+ {
+ name: 'Ibibio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibb'
+ },
+ {
+ name: 'Iwaidja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibd'
+ },
+ {
+ name: 'Akpes',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibe'
+ },
+ {
+ name: 'Ibanag',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibg'
+ },
+ {
+ name: 'Bih',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibh'
+ },
+ {
+ name: 'Ibaloi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibl'
+ },
+ {
+ name: 'Agoi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibm'
+ },
+ {
+ name: 'Ibino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibn'
+ },
+ {
+ name: 'Igbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibo',
+ iso6392B: 'ibo',
+ iso6392T: 'ibo',
+ iso6391: 'ig'
+ },
+ {
+ name: 'Ibuoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibr'
+ },
+ {
+ name: 'Ibu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ibu'
+ },
+ {
+ name: 'Ibani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iby'
+ },
+ {
+ name: 'Ede Ica',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ica'
+ },
+ {
+ name: 'Etkywan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ich'
+ },
+ {
+ name: 'Icelandic Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'icl'
+ },
+ {
+ name: 'Islander Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'icr'
+ },
+ {
+ name: 'Idakho-Isukha-Tiriki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ida'
+ },
+ {
+ name: 'Indo-Portuguese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idb'
+ },
+ {
+ name: 'Idon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idc'
+ },
+ {
+ name: 'Ede Idaca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idd'
+ },
+ {
+ name: 'Idere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ide'
+ },
+ {
+ name: 'Idi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idi'
+ },
+ {
+ name: 'Ido',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'ido',
+ iso6392B: 'ido',
+ iso6392T: 'ido',
+ iso6391: 'io'
+ },
+ {
+ name: 'Indri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idr'
+ },
+ {
+ name: 'Idesa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ids'
+ },
+ {
+ name: 'Idaté',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idt'
+ },
+ {
+ name: 'Idoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'idu'
+ },
+ {
+ name: 'Amganad Ifugao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ifa'
+ },
+ {
+ name: 'Batad Ifugao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ifb'
+ },
+ {
+ name: 'Ifè',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ife'
+ },
+ {
+ name: 'Ifo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'iff'
+ },
+ {
+ name: 'Tuwali Ifugao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ifk'
+ },
+ {
+ name: 'Teke-Fuumu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ifm'
+ },
+ {
+ name: 'Mayoyao Ifugao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ifu'
+ },
+ {
+ name: 'Keley-I Kallahan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ify'
+ },
+ {
+ name: 'Ebira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igb'
+ },
+ {
+ name: 'Igede',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ige'
+ },
+ {
+ name: 'Igana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igg'
+ },
+ {
+ name: 'Igala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igl'
+ },
+ {
+ name: 'Kanggape',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igm'
+ },
+ {
+ name: 'Ignaciano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ign'
+ },
+ {
+ name: 'Isebe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igo'
+ },
+ {
+ name: 'Interglossa',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'igs'
+ },
+ {
+ name: 'Igwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'igw'
+ },
+ {
+ name: 'Iha Based Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ihb'
+ },
+ {
+ name: 'Ihievbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ihi'
+ },
+ {
+ name: 'Iha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ihp'
+ },
+ {
+ name: 'Bidhawal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ihw'
+ },
+ {
+ name: 'Sichuan Yi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iii',
+ iso6392B: 'iii',
+ iso6392T: 'iii',
+ iso6391: 'ii'
+ },
+ {
+ name: 'Thiin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'iin'
+ },
+ {
+ name: 'Izon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ijc'
+ },
+ {
+ name: 'Biseni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ije'
+ },
+ {
+ name: 'Ede Ije',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ijj'
+ },
+ {
+ name: 'Kalabari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ijn'
+ },
+ {
+ name: 'Southeast Ijo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ijs'
+ },
+ {
+ name: 'Eastern Canadian Inuktitut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ike'
+ },
+ {
+ name: 'Iko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iki'
+ },
+ {
+ name: 'Ika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikk'
+ },
+ {
+ name: 'Ikulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikl'
+ },
+ {
+ name: 'Olulumo-Ikom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iko'
+ },
+ {
+ name: 'Ikpeshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikp'
+ },
+ {
+ name: 'Ikaranggal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ikr'
+ },
+ {
+ name: 'Inuit Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iks'
+ },
+ {
+ name: 'Inuinnaqtun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikt'
+ },
+ {
+ name: 'Inuktitut',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'iku',
+ iso6392B: 'iku',
+ iso6392T: 'iku',
+ iso6391: 'iu'
+ },
+ {
+ name: 'Iku-Gora-Ankwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikv'
+ },
+ {
+ name: 'Ikwere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikw'
+ },
+ {
+ name: 'Ik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikx'
+ },
+ {
+ name: 'Ikizu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ikz'
+ },
+ {
+ name: 'Ile Ape',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ila'
+ },
+ {
+ name: 'Ila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilb'
+ },
+ {
+ name: 'Interlingue',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'ile',
+ iso6392B: 'ile',
+ iso6392T: 'ile',
+ iso6391: 'ie'
+ },
+ {
+ name: 'Garig-Ilgar',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ilg'
+ },
+ {
+ name: 'Ili Turki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ili'
+ },
+ {
+ name: 'Ilongot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilk'
+ },
+ {
+ name: 'Iranun (Malaysia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilm'
+ },
+ {
+ name: 'Iloko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilo',
+ iso6392B: 'ilo',
+ iso6392T: 'ilo'
+ },
+ {
+ name: 'Iranun (Philippines)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilp'
+ },
+ {
+ name: 'International Sign',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ils'
+ },
+ {
+ name: "Ili'uun",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilu'
+ },
+ {
+ name: 'Ilue',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ilv'
+ },
+ {
+ name: 'Mala Malasar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ima'
+ },
+ {
+ name: 'Anamgura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'imi'
+ },
+ {
+ name: 'Miluk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'iml'
+ },
+ {
+ name: 'Imonda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'imn'
+ },
+ {
+ name: 'Imbongu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'imo'
+ },
+ {
+ name: 'Imroing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'imr'
+ },
+ {
+ name: 'Marsian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'ims'
+ },
+ {
+ name: 'Milyan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'imy'
+ },
+ {
+ name: 'Interlingua (International Auxiliary Language Association)',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'ina',
+ iso6392B: 'ina',
+ iso6392T: 'ina',
+ iso6391: 'ia'
+ },
+ {
+ name: 'Inga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inb'
+ },
+ {
+ name: 'Indonesian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ind',
+ iso6392B: 'ind',
+ iso6392T: 'ind',
+ iso6391: 'id'
+ },
+ {
+ name: "Degexit'an",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ing'
+ },
+ {
+ name: 'Ingush',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inh',
+ iso6392B: 'inh',
+ iso6392T: 'inh'
+ },
+ {
+ name: 'Jungle Inga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inj'
+ },
+ {
+ name: 'Indonesian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inl'
+ },
+ {
+ name: 'Minaean',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'inm'
+ },
+ {
+ name: 'Isinai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inn'
+ },
+ {
+ name: 'Inoke-Yate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ino'
+ },
+ {
+ name: 'Iñapari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'inp'
+ },
+ {
+ name: 'Indian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ins'
+ },
+ {
+ name: 'Intha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'int'
+ },
+ {
+ name: 'Ineseño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'inz'
+ },
+ {
+ name: 'Inor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ior'
+ },
+ {
+ name: 'Tuma-Irumu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iou'
+ },
+ {
+ name: 'Iowa-Oto',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'iow'
+ },
+ {
+ name: 'Ipili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ipi'
+ },
+ {
+ name: 'Inupiaq',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'ipk',
+ iso6392B: 'ipk',
+ iso6392T: 'ipk',
+ iso6391: 'ik'
+ },
+ {
+ name: 'Ipiko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ipo'
+ },
+ {
+ name: 'Iquito',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iqu'
+ },
+ {
+ name: 'Ikwo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iqw'
+ },
+ {
+ name: 'Iresim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ire'
+ },
+ {
+ name: 'Irarutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'irh'
+ },
+ {
+ name: 'Rigwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iri'
+ },
+ {
+ name: 'Iraqw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'irk'
+ },
+ {
+ name: 'Irántxe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'irn'
+ },
+ {
+ name: 'Ir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'irr'
+ },
+ {
+ name: 'Irula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iru'
+ },
+ {
+ name: 'Kamberau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'irx'
+ },
+ {
+ name: 'Iraya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iry'
+ },
+ {
+ name: 'Isabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isa'
+ },
+ {
+ name: 'Isconahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isc'
+ },
+ {
+ name: 'Isnag',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isd'
+ },
+ {
+ name: 'Italian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ise'
+ },
+ {
+ name: 'Irish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isg'
+ },
+ {
+ name: 'Esan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ish'
+ },
+ {
+ name: 'Nkem-Nkum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isi'
+ },
+ {
+ name: 'Ishkashimi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isk'
+ },
+ {
+ name: 'Icelandic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isl',
+ iso6392B: 'ice',
+ iso6392T: 'isl',
+ iso6391: 'is'
+ },
+ {
+ name: 'Masimasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ism'
+ },
+ {
+ name: 'Isanzu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isn'
+ },
+ {
+ name: 'Isoko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iso'
+ },
+ {
+ name: 'Israeli Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isr'
+ },
+ {
+ name: 'Istriot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ist'
+ },
+ {
+ name: 'Isu (Menchum Division)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'isu'
+ },
+ {
+ name: 'Italian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ita',
+ iso6392B: 'ita',
+ iso6392T: 'ita',
+ iso6391: 'it'
+ },
+ {
+ name: 'Binongan Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itb'
+ },
+ {
+ name: 'Southern Tidung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itd'
+ },
+ {
+ name: 'Itene',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ite'
+ },
+ {
+ name: 'Inlaod Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iti'
+ },
+ {
+ name: 'Judeo-Italian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itk'
+ },
+ {
+ name: 'Itelmen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itl'
+ },
+ {
+ name: 'Itu Mbon Uzo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itm'
+ },
+ {
+ name: 'Itonama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ito'
+ },
+ {
+ name: 'Iteri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itr'
+ },
+ {
+ name: 'Isekiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'its'
+ },
+ {
+ name: 'Maeng Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itt'
+ },
+ {
+ name: 'Itawit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itv'
+ },
+ {
+ name: 'Ito',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itw'
+ },
+ {
+ name: 'Itik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itx'
+ },
+ {
+ name: 'Moyadan Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ity'
+ },
+ {
+ name: 'Itzá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'itz'
+ },
+ {
+ name: 'Iu Mien',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ium'
+ },
+ {
+ name: 'Ibatan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ivb'
+ },
+ {
+ name: 'Ivatan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ivv'
+ },
+ {
+ name: 'I-Wak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iwk'
+ },
+ {
+ name: 'Iwam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iwm'
+ },
+ {
+ name: 'Iwur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iwo'
+ },
+ {
+ name: 'Sepik Iwam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iws'
+ },
+ {
+ name: 'Ixcatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ixc'
+ },
+ {
+ name: 'Ixil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ixl'
+ },
+ {
+ name: 'Iyayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iya'
+ },
+ {
+ name: 'Mesaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iyo'
+ },
+ {
+ name: 'Yaka (Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'iyx'
+ },
+ {
+ name: 'Ingrian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'izh'
+ },
+ {
+ name: 'Izere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'izr'
+ },
+ {
+ name: 'Izii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'izz'
+ },
+ {
+ name: 'Jamamadí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jaa'
+ },
+ {
+ name: 'Hyam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jab'
+ },
+ {
+ name: "Popti'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jac'
+ },
+ {
+ name: 'Jahanka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jad'
+ },
+ {
+ name: 'Yabem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jae'
+ },
+ {
+ name: 'Jara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jaf'
+ },
+ {
+ name: 'Jah Hut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jah'
+ },
+ {
+ name: 'Zazao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jaj'
+ },
+ {
+ name: 'Jakun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jak'
+ },
+ {
+ name: 'Yalahatan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jal'
+ },
+ {
+ name: 'Jamaican Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jam'
+ },
+ {
+ name: 'Jandai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jan'
+ },
+ {
+ name: 'Yanyuwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jao'
+ },
+ {
+ name: 'Yaqay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jaq'
+ },
+ {
+ name: 'New Caledonian Javanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jas'
+ },
+ {
+ name: 'Jakati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jat'
+ },
+ {
+ name: 'Yaur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jau'
+ },
+ {
+ name: 'Javanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jav',
+ iso6392B: 'jav',
+ iso6392T: 'jav',
+ iso6391: 'jv'
+ },
+ {
+ name: 'Jambi Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jax'
+ },
+ {
+ name: 'Yan-nhangu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jay'
+ },
+ {
+ name: 'Jawe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jaz'
+ },
+ {
+ name: 'Judeo-Berber',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbe'
+ },
+ {
+ name: 'Badjiri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jbi'
+ },
+ {
+ name: 'Arandai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbj'
+ },
+ {
+ name: 'Barikewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbk'
+ },
+ {
+ name: 'Nafusi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbn'
+ },
+ {
+ name: 'Lojban',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'jbo',
+ iso6392B: 'jbo',
+ iso6392T: 'jbo'
+ },
+ {
+ name: 'Jofotek-Bromnya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbr'
+ },
+ {
+ name: 'Jabutí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbt'
+ },
+ {
+ name: 'Jukun Takum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jbu'
+ },
+ {
+ name: 'Yawijibaya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jbw'
+ },
+ {
+ name: 'Jamaican Country Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jcs'
+ },
+ {
+ name: 'Krymchak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jct'
+ },
+ {
+ name: 'Jad',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jda'
+ },
+ {
+ name: 'Jadgali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jdg'
+ },
+ {
+ name: 'Judeo-Tat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jdt'
+ },
+ {
+ name: 'Jebero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jeb'
+ },
+ {
+ name: 'Jerung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jee'
+ },
+ {
+ name: 'Jeh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jeh'
+ },
+ {
+ name: 'Yei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jei'
+ },
+ {
+ name: 'Jeri Kuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jek'
+ },
+ {
+ name: 'Yelmek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jel'
+ },
+ {
+ name: 'Dza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jen'
+ },
+ {
+ name: 'Jere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jer'
+ },
+ {
+ name: 'Manem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jet'
+ },
+ {
+ name: 'Jonkor Bourmataguil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jeu'
+ },
+ {
+ name: 'Ngbee',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jgb'
+ },
+ {
+ name: 'Judeo-Georgian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jge'
+ },
+ {
+ name: 'Gwak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jgk'
+ },
+ {
+ name: 'Ngomba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jgo'
+ },
+ {
+ name: 'Jehai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jhi'
+ },
+ {
+ name: 'Jhankot Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jhs'
+ },
+ {
+ name: 'Jina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jia'
+ },
+ {
+ name: 'Jibu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jib'
+ },
+ {
+ name: 'Tol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jic'
+ },
+ {
+ name: 'Bu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jid'
+ },
+ {
+ name: 'Jilbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jie'
+ },
+ {
+ name: 'Jingulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jig'
+ },
+ {
+ name: 'sTodsde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jih'
+ },
+ {
+ name: 'Jiiddu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jii'
+ },
+ {
+ name: 'Jilim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jil'
+ },
+ {
+ name: 'Jimi (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jim'
+ },
+ {
+ name: 'Jiamao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jio'
+ },
+ {
+ name: 'Guanyinqiao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jiq'
+ },
+ {
+ name: 'Jita',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jit'
+ },
+ {
+ name: 'Youle Jinuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jiu'
+ },
+ {
+ name: 'Shuar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jiv'
+ },
+ {
+ name: 'Buyuan Jinuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jiy'
+ },
+ {
+ name: 'Jejueo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jje'
+ },
+ {
+ name: 'Bankal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jjr'
+ },
+ {
+ name: 'Kaera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jka'
+ },
+ {
+ name: 'Mobwa Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jkm'
+ },
+ {
+ name: 'Kubo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jko'
+ },
+ {
+ name: 'Paku Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jkp'
+ },
+ {
+ name: 'Koro (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jkr'
+ },
+ {
+ name: 'Labir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jku'
+ },
+ {
+ name: 'Ngile',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jle'
+ },
+ {
+ name: 'Jamaican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jls'
+ },
+ {
+ name: 'Dima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jma'
+ },
+ {
+ name: 'Zumbun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmb'
+ },
+ {
+ name: 'Machame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmc'
+ },
+ {
+ name: 'Yamdena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmd'
+ },
+ {
+ name: 'Jimi (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmi'
+ },
+ {
+ name: 'Jumli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jml'
+ },
+ {
+ name: 'Makuri Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmn'
+ },
+ {
+ name: 'Kamara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmr'
+ },
+ {
+ name: 'Mashi (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jms'
+ },
+ {
+ name: 'Mouwase',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmw'
+ },
+ {
+ name: 'Western Juxtlahuaca Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jmx'
+ },
+ {
+ name: 'Jangshung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jna'
+ },
+ {
+ name: 'Jandavra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jnd'
+ },
+ {
+ name: 'Yangman',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jng'
+ },
+ {
+ name: 'Janji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jni'
+ },
+ {
+ name: 'Yemsa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jnj'
+ },
+ {
+ name: 'Rawat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jnl'
+ },
+ {
+ name: 'Jaunsari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jns'
+ },
+ {
+ name: 'Joba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'job'
+ },
+ {
+ name: 'Wojenaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jod'
+ },
+ {
+ name: 'Jogi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jog'
+ },
+ {
+ name: 'Jorá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jor'
+ },
+ {
+ name: 'Jordanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jos'
+ },
+ {
+ name: 'Jowulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jow'
+ },
+ {
+ name: 'Jewish Palestinian Aramaic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'jpa'
+ },
+ {
+ name: 'Japanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jpn',
+ iso6392B: 'jpn',
+ iso6392T: 'jpn',
+ iso6391: 'ja'
+ },
+ {
+ name: 'Judeo-Persian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jpr',
+ iso6392B: 'jpr',
+ iso6392T: 'jpr'
+ },
+ {
+ name: 'Jaqaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jqr'
+ },
+ {
+ name: 'Jarai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jra'
+ },
+ {
+ name: 'Judeo-Arabic',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'jrb',
+ iso6392B: 'jrb',
+ iso6392T: 'jrb'
+ },
+ {
+ name: 'Jiru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jrr'
+ },
+ {
+ name: 'Jorto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jrt'
+ },
+ {
+ name: 'Japrería',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jru'
+ },
+ {
+ name: 'Japanese Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jsl'
+ },
+ {
+ name: 'Júma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jua'
+ },
+ {
+ name: 'Wannu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jub'
+ },
+ {
+ name: 'Jurchen',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'juc'
+ },
+ {
+ name: 'Worodougou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jud'
+ },
+ {
+ name: 'Hõne',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juh'
+ },
+ {
+ name: 'Ngadjuri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'jui'
+ },
+ {
+ name: 'Wapan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juk'
+ },
+ {
+ name: 'Jirel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jul'
+ },
+ {
+ name: 'Jumjum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jum'
+ },
+ {
+ name: 'Juang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jun'
+ },
+ {
+ name: 'Jiba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juo'
+ },
+ {
+ name: 'Hupdë',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jup'
+ },
+ {
+ name: 'Jurúna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jur'
+ },
+ {
+ name: 'Jumla Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jus'
+ },
+ {
+ name: 'Jutish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'jut'
+ },
+ {
+ name: 'Ju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juu'
+ },
+ {
+ name: 'Wãpha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juw'
+ },
+ {
+ name: 'Juray',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'juy'
+ },
+ {
+ name: 'Javindo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jvd'
+ },
+ {
+ name: 'Caribbean Javanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jvn'
+ },
+ {
+ name: 'Jwira-Pepesa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jwi'
+ },
+ {
+ name: 'Jiarong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jya'
+ },
+ {
+ name: 'Judeo-Yemeni Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jye'
+ },
+ {
+ name: 'Jaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'jyy'
+ },
+ {
+ name: 'Kara-Kalpak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kaa',
+ iso6392B: 'kaa',
+ iso6392T: 'kaa'
+ },
+ {
+ name: 'Kabyle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kab',
+ iso6392B: 'kab',
+ iso6392T: 'kab'
+ },
+ {
+ name: 'Kachin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kac',
+ iso6392B: 'kac',
+ iso6392T: 'kac'
+ },
+ {
+ name: 'Adara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kad'
+ },
+ {
+ name: 'Ketangalan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kae'
+ },
+ {
+ name: 'Katso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kaf'
+ },
+ {
+ name: 'Kajaman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kag'
+ },
+ {
+ name: 'Kara (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kah'
+ },
+ {
+ name: 'Karekare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kai'
+ },
+ {
+ name: 'Jju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kaj'
+ },
+ {
+ name: 'Kalanguya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kak'
+ },
+ {
+ name: 'Kalaallisut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kal',
+ iso6392B: 'kal',
+ iso6392T: 'kal',
+ iso6391: 'kl'
+ },
+ {
+ name: 'Kamba (Kenya)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kam',
+ iso6392B: 'kam',
+ iso6392T: 'kam'
+ },
+ {
+ name: 'Kannada',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kan',
+ iso6392B: 'kan',
+ iso6392T: 'kan',
+ iso6391: 'kn'
+ },
+ {
+ name: 'Xaasongaxango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kao'
+ },
+ {
+ name: 'Bezhta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kap'
+ },
+ {
+ name: 'Capanahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kaq'
+ },
+ {
+ name: 'Kashmiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kas',
+ iso6392B: 'kas',
+ iso6392T: 'kas',
+ iso6391: 'ks'
+ },
+ {
+ name: 'Georgian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kat',
+ iso6392B: 'geo',
+ iso6392T: 'kat',
+ iso6391: 'ka'
+ },
+ {
+ name: 'Kanuri',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kau',
+ iso6392B: 'kau',
+ iso6392T: 'kau',
+ iso6391: 'kr'
+ },
+ {
+ name: 'Katukína',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kav'
+ },
+ {
+ name: 'Kawi',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'kaw',
+ iso6392B: 'kaw',
+ iso6392T: 'kaw'
+ },
+ {
+ name: 'Kao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kax'
+ },
+ {
+ name: 'Kamayurá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kay'
+ },
+ {
+ name: 'Kazakh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kaz',
+ iso6392B: 'kaz',
+ iso6392T: 'kaz',
+ iso6391: 'kk'
+ },
+ {
+ name: 'Kalarko',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kba'
+ },
+ {
+ name: 'Kaxuiâna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kbb'
+ },
+ {
+ name: 'Kadiwéu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbc'
+ },
+ {
+ name: 'Kabardian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbd',
+ iso6392B: 'kbd',
+ iso6392T: 'kbd'
+ },
+ {
+ name: 'Kanju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbe'
+ },
+ {
+ name: 'Khamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbg'
+ },
+ {
+ name: 'Camsá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbh'
+ },
+ {
+ name: 'Kaptiau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbi'
+ },
+ {
+ name: 'Kari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbj'
+ },
+ {
+ name: 'Grass Koiari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbk'
+ },
+ {
+ name: 'Kanembu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbl'
+ },
+ {
+ name: 'Iwal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbm'
+ },
+ {
+ name: 'Kare (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbn'
+ },
+ {
+ name: 'Keliko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbo'
+ },
+ {
+ name: 'Kabiyè',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbp'
+ },
+ {
+ name: 'Kamano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbq'
+ },
+ {
+ name: 'Kafa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbr'
+ },
+ {
+ name: 'Kande',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbs'
+ },
+ {
+ name: 'Abadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbt'
+ },
+ {
+ name: 'Kabutra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbu'
+ },
+ {
+ name: 'Dera (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbv'
+ },
+ {
+ name: 'Kaiep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbw'
+ },
+ {
+ name: 'Ap Ma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbx'
+ },
+ {
+ name: 'Manga Kanuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kby'
+ },
+ {
+ name: 'Duhwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kbz'
+ },
+ {
+ name: 'Khanty',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kca'
+ },
+ {
+ name: 'Kawacha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcb'
+ },
+ {
+ name: 'Lubila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcc'
+ },
+ {
+ name: 'Ngkâlmpw Kanum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcd'
+ },
+ {
+ name: 'Kaivi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kce'
+ },
+ {
+ name: 'Ukaan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcf'
+ },
+ {
+ name: 'Tyap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcg'
+ },
+ {
+ name: 'Vono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kch'
+ },
+ {
+ name: 'Kamantan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kci'
+ },
+ {
+ name: 'Kobiana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcj'
+ },
+ {
+ name: 'Kalanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kck'
+ },
+ {
+ name: 'Kela (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcl'
+ },
+ {
+ name: 'Gula (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcm'
+ },
+ {
+ name: 'Nubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcn'
+ },
+ {
+ name: 'Kinalakna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kco'
+ },
+ {
+ name: 'Kanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcp'
+ },
+ {
+ name: 'Kamo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcq'
+ },
+ {
+ name: 'Katla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcr'
+ },
+ {
+ name: 'Koenoem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcs'
+ },
+ {
+ name: 'Kaian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kct'
+ },
+ {
+ name: 'Kami (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcu'
+ },
+ {
+ name: 'Kete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcv'
+ },
+ {
+ name: 'Kabwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcw'
+ },
+ {
+ name: 'Kachama-Ganjule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcx'
+ },
+ {
+ name: 'Korandje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcy'
+ },
+ {
+ name: 'Konongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kcz'
+ },
+ {
+ name: 'Worimi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kda'
+ },
+ {
+ name: 'Kutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdc'
+ },
+ {
+ name: 'Yankunytjatjara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdd'
+ },
+ {
+ name: 'Makonde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kde'
+ },
+ {
+ name: 'Mamusi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdf'
+ },
+ {
+ name: 'Seba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdg'
+ },
+ {
+ name: 'Tem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdh'
+ },
+ {
+ name: 'Kumam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdi'
+ },
+ {
+ name: 'Karamojong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdj'
+ },
+ {
+ name: 'Numèè',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdk'
+ },
+ {
+ name: 'Tsikimba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdl'
+ },
+ {
+ name: 'Kagoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdm'
+ },
+ {
+ name: 'Kunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdn'
+ },
+ {
+ name: 'Kaningdon-Nindem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdp'
+ },
+ {
+ name: 'Koch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdq'
+ },
+ {
+ name: 'Karaim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdr'
+ },
+ {
+ name: 'Kuy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdt'
+ },
+ {
+ name: 'Kadaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdu'
+ },
+ {
+ name: 'Koneraw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdw'
+ },
+ {
+ name: 'Kam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdx'
+ },
+ {
+ name: 'Keder',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdy'
+ },
+ {
+ name: 'Kwaja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kdz'
+ },
+ {
+ name: 'Kabuverdianu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kea'
+ },
+ {
+ name: 'Kélé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keb'
+ },
+ {
+ name: 'Keiga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kec'
+ },
+ {
+ name: 'Kerewe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ked'
+ },
+ {
+ name: 'Eastern Keres',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kee'
+ },
+ {
+ name: 'Kpessi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kef'
+ },
+ {
+ name: 'Tese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keg'
+ },
+ {
+ name: 'Keak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keh'
+ },
+ {
+ name: 'Kei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kei'
+ },
+ {
+ name: 'Kadar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kej'
+ },
+ {
+ name: 'Kekchí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kek'
+ },
+ {
+ name: 'Kela (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kel'
+ },
+ {
+ name: 'Kemak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kem'
+ },
+ {
+ name: 'Kenyang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ken'
+ },
+ {
+ name: 'Kakwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keo'
+ },
+ {
+ name: 'Kaikadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kep'
+ },
+ {
+ name: 'Kamar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keq'
+ },
+ {
+ name: 'Kera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ker'
+ },
+ {
+ name: 'Kugbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kes'
+ },
+ {
+ name: 'Ket',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ket'
+ },
+ {
+ name: 'Akebu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'keu'
+ },
+ {
+ name: 'Kanikkaran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kev'
+ },
+ {
+ name: 'West Kewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kew'
+ },
+ {
+ name: 'Kukna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kex'
+ },
+ {
+ name: 'Kupia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'key'
+ },
+ {
+ name: 'Kukele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kez'
+ },
+ {
+ name: 'Kodava',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfa'
+ },
+ {
+ name: 'Northwestern Kolami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfb'
+ },
+ {
+ name: 'Konda-Dora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfc'
+ },
+ {
+ name: 'Korra Koraga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfd'
+ },
+ {
+ name: 'Kota (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfe'
+ },
+ {
+ name: 'Koya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kff'
+ },
+ {
+ name: 'Kudiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfg'
+ },
+ {
+ name: 'Kurichiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfh'
+ },
+ {
+ name: 'Kannada Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfi'
+ },
+ {
+ name: 'Kemiehua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfj'
+ },
+ {
+ name: 'Kinnauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfk'
+ },
+ {
+ name: 'Kung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfl'
+ },
+ {
+ name: 'Khunsari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfm'
+ },
+ {
+ name: 'Kuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfn'
+ },
+ {
+ name: "Koro (Côte d'Ivoire)",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfo'
+ },
+ {
+ name: 'Korwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfp'
+ },
+ {
+ name: 'Korku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfq'
+ },
+ {
+ name: 'Kachhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfr'
+ },
+ {
+ name: 'Bilaspuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfs'
+ },
+ {
+ name: 'Kanjari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kft'
+ },
+ {
+ name: 'Katkari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfu'
+ },
+ {
+ name: 'Kurmukar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfv'
+ },
+ {
+ name: 'Kharam Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfw'
+ },
+ {
+ name: 'Kullu Pahari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfx'
+ },
+ {
+ name: 'Kumaoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfy'
+ },
+ {
+ name: 'Koromfé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kfz'
+ },
+ {
+ name: 'Koyaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kga'
+ },
+ {
+ name: 'Kawe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgb'
+ },
+ {
+ name: 'Komering',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kge'
+ },
+ {
+ name: 'Kube',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgf'
+ },
+ {
+ name: 'Kusunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgg'
+ },
+ {
+ name: 'Selangor Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgi'
+ },
+ {
+ name: 'Gamale Kham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgj'
+ },
+ {
+ name: 'Kaiwá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgk'
+ },
+ {
+ name: 'Kunggari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kgl'
+ },
+ {
+ name: 'Karipúna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kgm'
+ },
+ {
+ name: 'Karingani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgn'
+ },
+ {
+ name: 'Krongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgo'
+ },
+ {
+ name: 'Kaingang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgp'
+ },
+ {
+ name: 'Kamoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgq'
+ },
+ {
+ name: 'Abun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgr'
+ },
+ {
+ name: 'Kumbainggar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgs'
+ },
+ {
+ name: 'Somyev',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgt'
+ },
+ {
+ name: 'Kobol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgu'
+ },
+ {
+ name: 'Karas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgv'
+ },
+ {
+ name: 'Karon Dori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgw'
+ },
+ {
+ name: 'Kamaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgx'
+ },
+ {
+ name: 'Kyerung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kgy'
+ },
+ {
+ name: 'Khasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kha',
+ iso6392B: 'kha',
+ iso6392T: 'kha'
+ },
+ {
+ name: 'Lü',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khb'
+ },
+ {
+ name: 'Tukang Besi North',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khc'
+ },
+ {
+ name: 'Bädi Kanum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khd'
+ },
+ {
+ name: 'Korowai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khe'
+ },
+ {
+ name: 'Khuen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khf'
+ },
+ {
+ name: 'Khams Tibetan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khg'
+ },
+ {
+ name: 'Kehu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khh'
+ },
+ {
+ name: 'Kuturmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khj'
+ },
+ {
+ name: 'Halh Mongolian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khk'
+ },
+ {
+ name: 'Lusi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khl'
+ },
+ {
+ name: 'Khmer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khm',
+ iso6392B: 'khm',
+ iso6392T: 'khm',
+ iso6391: 'km'
+ },
+ {
+ name: 'Khandesi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khn'
+ },
+ {
+ name: 'Khotanese',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'kho',
+ iso6392B: 'kho',
+ iso6392T: 'kho'
+ },
+ {
+ name: 'Kapori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khp'
+ },
+ {
+ name: 'Koyra Chiini Songhay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khq'
+ },
+ {
+ name: 'Kharia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khr'
+ },
+ {
+ name: 'Kasua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khs'
+ },
+ {
+ name: 'Khamti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kht'
+ },
+ {
+ name: 'Nkhumbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khu'
+ },
+ {
+ name: 'Khvarshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khv'
+ },
+ {
+ name: 'Khowar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khw'
+ },
+ {
+ name: 'Kanu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khx'
+ },
+ {
+ name: 'Kele (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khy'
+ },
+ {
+ name: 'Keapara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'khz'
+ },
+ {
+ name: 'Kim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kia'
+ },
+ {
+ name: 'Koalib',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kib'
+ },
+ {
+ name: 'Kickapoo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kic'
+ },
+ {
+ name: 'Koshin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kid'
+ },
+ {
+ name: 'Kibet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kie'
+ },
+ {
+ name: 'Eastern Parbate Kham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kif'
+ },
+ {
+ name: 'Kimaama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kig'
+ },
+ {
+ name: 'Kilmeri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kih'
+ },
+ {
+ name: 'Kitsai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kii'
+ },
+ {
+ name: 'Kilivila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kij'
+ },
+ {
+ name: 'Kikuyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kik',
+ iso6392B: 'kik',
+ iso6392T: 'kik',
+ iso6391: 'ki'
+ },
+ {
+ name: 'Kariya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kil'
+ },
+ {
+ name: 'Karagas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kim'
+ },
+ {
+ name: 'Kinyarwanda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kin',
+ iso6392B: 'kin',
+ iso6392T: 'kin',
+ iso6391: 'rw'
+ },
+ {
+ name: 'Kiowa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kio'
+ },
+ {
+ name: 'Sheshi Kham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kip'
+ },
+ {
+ name: 'Kosadle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiq'
+ },
+ {
+ name: 'Kirghiz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kir',
+ iso6392B: 'kir',
+ iso6392T: 'kir',
+ iso6391: 'ky'
+ },
+ {
+ name: 'Kis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kis'
+ },
+ {
+ name: 'Agob',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kit'
+ },
+ {
+ name: 'Kirmanjki (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiu'
+ },
+ {
+ name: 'Kimbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiv'
+ },
+ {
+ name: 'Northeast Kiwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiw'
+ },
+ {
+ name: 'Khiamniungan Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kix'
+ },
+ {
+ name: 'Kirikiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiy'
+ },
+ {
+ name: 'Kisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kiz'
+ },
+ {
+ name: 'Mlap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kja'
+ },
+ {
+ name: "Q'anjob'al",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjb'
+ },
+ {
+ name: 'Coastal Konjo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjc'
+ },
+ {
+ name: 'Southern Kiwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjd'
+ },
+ {
+ name: 'Kisar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kje'
+ },
+ {
+ name: 'Khmu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjg'
+ },
+ {
+ name: 'Khakas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjh'
+ },
+ {
+ name: 'Zabana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kji'
+ },
+ {
+ name: 'Khinalugh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjj'
+ },
+ {
+ name: 'Highland Konjo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjk'
+ },
+ {
+ name: 'Western Parbate Kham',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjl'
+ },
+ {
+ name: 'Kháng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjm'
+ },
+ {
+ name: 'Kunjen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjn'
+ },
+ {
+ name: 'Harijan Kinnauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjo'
+ },
+ {
+ name: 'Pwo Eastern Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjp'
+ },
+ {
+ name: 'Western Keres',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjq'
+ },
+ {
+ name: 'Kurudu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjr'
+ },
+ {
+ name: 'East Kewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjs'
+ },
+ {
+ name: 'Phrae Pwo Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjt'
+ },
+ {
+ name: 'Kashaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kju'
+ },
+ {
+ name: 'Kaikavian Literary Language',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'kjv'
+ },
+ {
+ name: 'Ramopa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjx'
+ },
+ {
+ name: 'Erave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjy'
+ },
+ {
+ name: 'Bumthangkha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kjz'
+ },
+ {
+ name: 'Kakanda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kka'
+ },
+ {
+ name: 'Kwerisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkb'
+ },
+ {
+ name: 'Odoodee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkc'
+ },
+ {
+ name: 'Kinuku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkd'
+ },
+ {
+ name: 'Kakabe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kke'
+ },
+ {
+ name: 'Kalaktang Monpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkf'
+ },
+ {
+ name: 'Mabaka Valley Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkg'
+ },
+ {
+ name: 'Khün',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkh'
+ },
+ {
+ name: 'Kagulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kki'
+ },
+ {
+ name: 'Kako',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkj'
+ },
+ {
+ name: 'Kokota',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkk'
+ },
+ {
+ name: 'Kosarek Yale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkl'
+ },
+ {
+ name: 'Kiong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkm'
+ },
+ {
+ name: 'Kon Keu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkn'
+ },
+ {
+ name: 'Karko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kko'
+ },
+ {
+ name: 'Gugubera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkp'
+ },
+ {
+ name: 'Kaeku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkq'
+ },
+ {
+ name: 'Kir-Balar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkr'
+ },
+ {
+ name: 'Giiwo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kks'
+ },
+ {
+ name: 'Koi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkt'
+ },
+ {
+ name: 'Tumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kku'
+ },
+ {
+ name: 'Kangean',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkv'
+ },
+ {
+ name: 'Teke-Kukuya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkw'
+ },
+ {
+ name: 'Kohin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkx'
+ },
+ {
+ name: 'Guugu Yimidhirr',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kky'
+ },
+ {
+ name: 'Kaska',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kkz'
+ },
+ {
+ name: 'Klamath-Modoc',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kla'
+ },
+ {
+ name: 'Kiliwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klb'
+ },
+ {
+ name: 'Kolbila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klc'
+ },
+ {
+ name: 'Gamilaraay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kld'
+ },
+ {
+ name: 'Kulung (Nepal)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kle'
+ },
+ {
+ name: 'Kendeje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klf'
+ },
+ {
+ name: 'Tagakaulo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klg'
+ },
+ {
+ name: 'Weliki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klh'
+ },
+ {
+ name: 'Kalumpang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kli'
+ },
+ {
+ name: 'Khalaj',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klj'
+ },
+ {
+ name: 'Kono (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klk'
+ },
+ {
+ name: 'Kagan Kalagan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kll'
+ },
+ {
+ name: 'Migum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klm'
+ },
+ {
+ name: 'Kalenjin',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kln'
+ },
+ {
+ name: 'Kapya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klo'
+ },
+ {
+ name: 'Kamasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klp'
+ },
+ {
+ name: 'Rumu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klq'
+ },
+ {
+ name: 'Khaling',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klr'
+ },
+ {
+ name: 'Kalasha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kls'
+ },
+ {
+ name: 'Nukna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klt'
+ },
+ {
+ name: 'Klao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klu'
+ },
+ {
+ name: 'Maskelynes',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klv'
+ },
+ {
+ name: 'Tado',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klw'
+ },
+ {
+ name: 'Koluwawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klx'
+ },
+ {
+ name: 'Kalao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kly'
+ },
+ {
+ name: 'Kabola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'klz'
+ },
+ {
+ name: 'Konni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kma'
+ },
+ {
+ name: 'Kimbundu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmb',
+ iso6392B: 'kmb',
+ iso6392T: 'kmb'
+ },
+ {
+ name: 'Southern Dong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmc'
+ },
+ {
+ name: 'Majukayang Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmd'
+ },
+ {
+ name: 'Bakole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kme'
+ },
+ {
+ name: 'Kare (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmf'
+ },
+ {
+ name: 'Kâte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmg'
+ },
+ {
+ name: 'Kalam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmh'
+ },
+ {
+ name: 'Kami (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmi'
+ },
+ {
+ name: 'Kumarbhag Paharia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmj'
+ },
+ {
+ name: 'Limos Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmk'
+ },
+ {
+ name: 'Tanudan Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kml'
+ },
+ {
+ name: 'Kom (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmm'
+ },
+ {
+ name: 'Awtuw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmn'
+ },
+ {
+ name: 'Kwoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmo'
+ },
+ {
+ name: 'Gimme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmp'
+ },
+ {
+ name: 'Kwama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmq'
+ },
+ {
+ name: 'Northern Kurdish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmr'
+ },
+ {
+ name: 'Kamasau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kms'
+ },
+ {
+ name: 'Kemtuik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmt'
+ },
+ {
+ name: 'Kanite',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmu'
+ },
+ {
+ name: 'Karipúna Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmv'
+ },
+ {
+ name: 'Komo (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmw'
+ },
+ {
+ name: 'Waboda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmx'
+ },
+ {
+ name: 'Koma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmy'
+ },
+ {
+ name: 'Khorasani Turkish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kmz'
+ },
+ {
+ name: 'Dera (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kna'
+ },
+ {
+ name: 'Lubuagan Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knb'
+ },
+ {
+ name: 'Central Kanuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knc'
+ },
+ {
+ name: 'Konda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knd'
+ },
+ {
+ name: 'Kankanaey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kne'
+ },
+ {
+ name: 'Mankanya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knf'
+ },
+ {
+ name: 'Koongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kng'
+ },
+ {
+ name: 'Kanufi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kni'
+ },
+ {
+ name: 'Western Kanjobal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knj'
+ },
+ {
+ name: 'Kuranko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knk'
+ },
+ {
+ name: 'Keninjal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knl'
+ },
+ {
+ name: 'Kanamarí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knm'
+ },
+ {
+ name: 'Konkani (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knn'
+ },
+ {
+ name: 'Kono (Sierra Leone)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kno'
+ },
+ {
+ name: 'Kwanja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knp'
+ },
+ {
+ name: 'Kintaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knq'
+ },
+ {
+ name: 'Kaningra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knr'
+ },
+ {
+ name: 'Kensiu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kns'
+ },
+ {
+ name: 'Panoan Katukína',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knt'
+ },
+ {
+ name: 'Kono (Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knu'
+ },
+ {
+ name: 'Tabo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knv'
+ },
+ {
+ name: 'Kung-Ekoka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knw'
+ },
+ {
+ name: 'Kendayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knx'
+ },
+ {
+ name: 'Kanyok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kny'
+ },
+ {
+ name: 'Kalamsé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'knz'
+ },
+ {
+ name: 'Konomala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koa'
+ },
+ {
+ name: 'Kpati',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'koc'
+ },
+ {
+ name: 'Kodi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kod'
+ },
+ {
+ name: 'Kacipo-Balesi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koe'
+ },
+ {
+ name: 'Kubi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kof'
+ },
+ {
+ name: 'Cogui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kog'
+ },
+ {
+ name: 'Koyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koh'
+ },
+ {
+ name: 'Komi-Permyak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koi'
+ },
+ {
+ name: 'Konkani (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kok',
+ iso6392B: 'kok',
+ iso6392T: 'kok'
+ },
+ {
+ name: 'Kol (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kol'
+ },
+ {
+ name: 'Komi',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kom',
+ iso6392B: 'kom',
+ iso6392T: 'kom',
+ iso6391: 'kv'
+ },
+ {
+ name: 'Kongo',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kon',
+ iso6392B: 'kon',
+ iso6392T: 'kon',
+ iso6391: 'kg'
+ },
+ {
+ name: 'Konzo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koo'
+ },
+ {
+ name: 'Waube',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kop'
+ },
+ {
+ name: 'Kota (Gabon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koq'
+ },
+ {
+ name: 'Korean',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kor',
+ iso6392B: 'kor',
+ iso6392T: 'kor',
+ iso6391: 'ko'
+ },
+ {
+ name: 'Kosraean',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kos',
+ iso6392B: 'kos',
+ iso6392T: 'kos'
+ },
+ {
+ name: 'Lagwan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kot'
+ },
+ {
+ name: 'Koke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kou'
+ },
+ {
+ name: 'Kudu-Camo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kov'
+ },
+ {
+ name: 'Kugama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kow'
+ },
+ {
+ name: 'Koyukon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koy'
+ },
+ {
+ name: 'Korak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'koz'
+ },
+ {
+ name: 'Kutto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpa'
+ },
+ {
+ name: 'Mullu Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpb'
+ },
+ {
+ name: 'Curripaco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpc'
+ },
+ {
+ name: 'Koba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpd'
+ },
+ {
+ name: 'Kpelle',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kpe',
+ iso6392B: 'kpe',
+ iso6392T: 'kpe'
+ },
+ {
+ name: 'Komba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpf'
+ },
+ {
+ name: 'Kapingamarangi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpg'
+ },
+ {
+ name: 'Kplang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kph'
+ },
+ {
+ name: 'Kofei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpi'
+ },
+ {
+ name: 'Karajá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpj'
+ },
+ {
+ name: 'Kpan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpk'
+ },
+ {
+ name: 'Kpala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpl'
+ },
+ {
+ name: 'Koho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpm'
+ },
+ {
+ name: 'Kepkiriwát',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kpn'
+ },
+ {
+ name: 'Ikposo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpo'
+ },
+ {
+ name: 'Korupun-Sela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpq'
+ },
+ {
+ name: 'Korafe-Yegha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpr'
+ },
+ {
+ name: 'Tehit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kps'
+ },
+ {
+ name: 'Karata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpt'
+ },
+ {
+ name: 'Kafoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpu'
+ },
+ {
+ name: 'Komi-Zyrian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpv'
+ },
+ {
+ name: 'Kobon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpw'
+ },
+ {
+ name: 'Mountain Koiali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpx'
+ },
+ {
+ name: 'Koryak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpy'
+ },
+ {
+ name: 'Kupsabiny',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kpz'
+ },
+ {
+ name: 'Mum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqa'
+ },
+ {
+ name: 'Kovai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqb'
+ },
+ {
+ name: 'Doromu-Koki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqc'
+ },
+ {
+ name: 'Koy Sanjaq Surat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqd'
+ },
+ {
+ name: 'Kalagan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqe'
+ },
+ {
+ name: 'Kakabai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqf'
+ },
+ {
+ name: 'Khe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqg'
+ },
+ {
+ name: 'Kisankasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqh'
+ },
+ {
+ name: 'Koitabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqi'
+ },
+ {
+ name: 'Koromira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqj'
+ },
+ {
+ name: 'Kotafon Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqk'
+ },
+ {
+ name: 'Kyenele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kql'
+ },
+ {
+ name: 'Khisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqm'
+ },
+ {
+ name: 'Kaonde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqn'
+ },
+ {
+ name: 'Eastern Krahn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqo'
+ },
+ {
+ name: 'Kimré',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqp'
+ },
+ {
+ name: 'Krenak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqq'
+ },
+ {
+ name: 'Kimaragang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqr'
+ },
+ {
+ name: 'Northern Kissi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqs'
+ },
+ {
+ name: 'Klias River Kadazan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqt'
+ },
+ {
+ name: 'Seroa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kqu'
+ },
+ {
+ name: 'Okolod',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqv'
+ },
+ {
+ name: 'Kandas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqw'
+ },
+ {
+ name: 'Mser',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqx'
+ },
+ {
+ name: 'Koorete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kqy'
+ },
+ {
+ name: 'Korana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kqz'
+ },
+ {
+ name: 'Kumhali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kra'
+ },
+ {
+ name: 'Karkin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'krb'
+ },
+ {
+ name: 'Karachay-Balkar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krc',
+ iso6392B: 'krc',
+ iso6392T: 'krc'
+ },
+ {
+ name: 'Kairui-Midiki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krd'
+ },
+ {
+ name: 'Panará',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kre'
+ },
+ {
+ name: 'Koro (Vanuatu)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krf'
+ },
+ {
+ name: 'Kurama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krh'
+ },
+ {
+ name: 'Krio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kri'
+ },
+ {
+ name: 'Kinaray-A',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krj'
+ },
+ {
+ name: 'Kerek',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'krk'
+ },
+ {
+ name: 'Karelian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krl',
+ iso6392B: 'krl',
+ iso6392T: 'krl'
+ },
+ {
+ name: 'Sapo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krn'
+ },
+ {
+ name: 'Korop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krp'
+ },
+ {
+ name: 'Krung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krr'
+ },
+ {
+ name: 'Gbaya (Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krs'
+ },
+ {
+ name: 'Tumari Kanuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krt'
+ },
+ {
+ name: 'Kurukh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kru',
+ iso6392B: 'kru',
+ iso6392T: 'kru'
+ },
+ {
+ name: 'Kavet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krv'
+ },
+ {
+ name: 'Western Krahn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krw'
+ },
+ {
+ name: 'Karon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krx'
+ },
+ {
+ name: 'Kryts',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kry'
+ },
+ {
+ name: 'Sota Kanum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'krz'
+ },
+ {
+ name: 'Shuwa-Zamani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksa'
+ },
+ {
+ name: 'Shambala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksb'
+ },
+ {
+ name: 'Southern Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksc'
+ },
+ {
+ name: 'Kuanua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksd'
+ },
+ {
+ name: 'Kuni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kse'
+ },
+ {
+ name: 'Bafia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksf'
+ },
+ {
+ name: 'Kusaghe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksg'
+ },
+ {
+ name: 'Kölsch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksh'
+ },
+ {
+ name: 'Krisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksi'
+ },
+ {
+ name: 'Uare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksj'
+ },
+ {
+ name: 'Kansa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksk'
+ },
+ {
+ name: 'Kumalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksl'
+ },
+ {
+ name: 'Kumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksm'
+ },
+ {
+ name: 'Kasiguranin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksn'
+ },
+ {
+ name: 'Kofa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kso'
+ },
+ {
+ name: 'Kaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksp'
+ },
+ {
+ name: 'Kwaami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksq'
+ },
+ {
+ name: 'Borong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksr'
+ },
+ {
+ name: 'Southern Kisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kss'
+ },
+ {
+ name: 'Winyé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kst'
+ },
+ {
+ name: 'Khamyang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksu'
+ },
+ {
+ name: 'Kusu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksv'
+ },
+ {
+ name: "S'gaw Karen",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksw'
+ },
+ {
+ name: 'Kedang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksx'
+ },
+ {
+ name: 'Kharia Thar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksy'
+ },
+ {
+ name: 'Kodaku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ksz'
+ },
+ {
+ name: 'Katua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kta'
+ },
+ {
+ name: 'Kambaata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktb'
+ },
+ {
+ name: 'Kholok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktc'
+ },
+ {
+ name: 'Kokata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktd'
+ },
+ {
+ name: 'Nubri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kte'
+ },
+ {
+ name: 'Kwami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktf'
+ },
+ {
+ name: 'Kalkutung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ktg'
+ },
+ {
+ name: 'Karanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kth'
+ },
+ {
+ name: 'North Muyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kti'
+ },
+ {
+ name: 'Plapo Krumen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktj'
+ },
+ {
+ name: 'Kaniet',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ktk'
+ },
+ {
+ name: 'Koroshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktl'
+ },
+ {
+ name: 'Kurti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktm'
+ },
+ {
+ name: 'Karitiâna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktn'
+ },
+ {
+ name: 'Kuot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kto'
+ },
+ {
+ name: 'Kaduo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktp'
+ },
+ {
+ name: 'Katabaga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ktq'
+ },
+ {
+ name: 'South Muyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kts'
+ },
+ {
+ name: 'Ketum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktt'
+ },
+ {
+ name: 'Kituba (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktu'
+ },
+ {
+ name: 'Eastern Katu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktv'
+ },
+ {
+ name: 'Kato',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ktw'
+ },
+ {
+ name: 'Kaxararí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktx'
+ },
+ {
+ name: 'Kango (Bas-Uélé District)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kty'
+ },
+ {
+ name: 'Juǀʼhoan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ktz'
+ },
+ {
+ name: 'Kuanyama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kua',
+ iso6392B: 'kua',
+ iso6392T: 'kua',
+ iso6391: 'kj'
+ },
+ {
+ name: 'Kutep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kub'
+ },
+ {
+ name: 'Kwinsu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuc'
+ },
+ {
+ name: "'Auhelawa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kud'
+ },
+ {
+ name: 'Kuman (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kue'
+ },
+ {
+ name: 'Western Katu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuf'
+ },
+ {
+ name: 'Kupa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kug'
+ },
+ {
+ name: 'Kushi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuh'
+ },
+ {
+ name: 'Kuikúro-Kalapálo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kui'
+ },
+ {
+ name: 'Kuria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuj'
+ },
+ {
+ name: "Kepo'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuk'
+ },
+ {
+ name: 'Kulere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kul'
+ },
+ {
+ name: 'Kumyk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kum',
+ iso6392B: 'kum',
+ iso6392T: 'kum'
+ },
+ {
+ name: 'Kunama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kun'
+ },
+ {
+ name: 'Kumukio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuo'
+ },
+ {
+ name: 'Kunimaipa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kup'
+ },
+ {
+ name: 'Karipuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuq'
+ },
+ {
+ name: 'Kurdish',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'kur',
+ iso6392B: 'kur',
+ iso6392T: 'kur',
+ iso6391: 'ku'
+ },
+ {
+ name: 'Kusaal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kus'
+ },
+ {
+ name: 'Kutenai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kut',
+ iso6392B: 'kut',
+ iso6392T: 'kut'
+ },
+ {
+ name: 'Upper Kuskokwim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuu'
+ },
+ {
+ name: 'Kur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuv'
+ },
+ {
+ name: 'Kpagua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuw'
+ },
+ {
+ name: 'Kukatja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kux'
+ },
+ {
+ name: "Kuuku-Ya'u",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kuy'
+ },
+ {
+ name: 'Kunza',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kuz'
+ },
+ {
+ name: 'Bagvalal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kva'
+ },
+ {
+ name: 'Kubu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvb'
+ },
+ {
+ name: 'Kove',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvc'
+ },
+ {
+ name: 'Kui (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvd'
+ },
+ {
+ name: 'Kalabakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kve'
+ },
+ {
+ name: 'Kabalai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvf'
+ },
+ {
+ name: 'Kuni-Boazi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvg'
+ },
+ {
+ name: 'Komodo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvh'
+ },
+ {
+ name: 'Kwang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvi'
+ },
+ {
+ name: 'Psikye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvj'
+ },
+ {
+ name: 'Korean Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvk'
+ },
+ {
+ name: 'Kayaw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvl'
+ },
+ {
+ name: 'Kendem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvm'
+ },
+ {
+ name: 'Border Kuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvn'
+ },
+ {
+ name: 'Dobel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvo'
+ },
+ {
+ name: 'Kompane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvp'
+ },
+ {
+ name: 'Geba Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvq'
+ },
+ {
+ name: 'Kerinci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvr'
+ },
+ {
+ name: 'Lahta Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvt'
+ },
+ {
+ name: 'Yinbaw Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvu'
+ },
+ {
+ name: 'Kola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvv'
+ },
+ {
+ name: 'Wersing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvw'
+ },
+ {
+ name: 'Parkari Koli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvx'
+ },
+ {
+ name: 'Yintale Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvy'
+ },
+ {
+ name: 'Tsakwambo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kvz'
+ },
+ {
+ name: 'Dâw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwa'
+ },
+ {
+ name: 'Kwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwb'
+ },
+ {
+ name: 'Likwala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwc'
+ },
+ {
+ name: 'Kwaio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwd'
+ },
+ {
+ name: 'Kwerba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwe'
+ },
+ {
+ name: "Kwara'ae",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwf'
+ },
+ {
+ name: 'Sara Kaba Deme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwg'
+ },
+ {
+ name: 'Kowiai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwh'
+ },
+ {
+ name: 'Awa-Cuaiquer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwi'
+ },
+ {
+ name: 'Kwanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwj'
+ },
+ {
+ name: 'Kwakiutl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwk'
+ },
+ {
+ name: 'Kofyar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwl'
+ },
+ {
+ name: 'Kwambi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwm'
+ },
+ {
+ name: 'Kwangali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwn'
+ },
+ {
+ name: 'Kwomtari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwo'
+ },
+ {
+ name: 'Kodia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwp'
+ },
+ {
+ name: 'Kwer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwr'
+ },
+ {
+ name: 'Kwese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kws'
+ },
+ {
+ name: 'Kwesten',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwt'
+ },
+ {
+ name: 'Kwakum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwu'
+ },
+ {
+ name: 'Sara Kaba Náà',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwv'
+ },
+ {
+ name: 'Kwinti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kww'
+ },
+ {
+ name: 'Khirwar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwx'
+ },
+ {
+ name: 'San Salvador Kongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kwy'
+ },
+ {
+ name: 'Kwadi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kwz'
+ },
+ {
+ name: 'Kairiru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxa'
+ },
+ {
+ name: 'Krobu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxb'
+ },
+ {
+ name: 'Konso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxc'
+ },
+ {
+ name: 'Brunei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxd'
+ },
+ {
+ name: 'Manumanaw Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxf'
+ },
+ {
+ name: 'Karo (Ethiopia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxh'
+ },
+ {
+ name: 'Keningau Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxi'
+ },
+ {
+ name: 'Kulfa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxj'
+ },
+ {
+ name: 'Zayein Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxk'
+ },
+ {
+ name: 'Northern Khmer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxm'
+ },
+ {
+ name: 'Kanowit-Tanjong Melanau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxn'
+ },
+ {
+ name: 'Kanoé',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kxo'
+ },
+ {
+ name: 'Wadiyara Koli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxp'
+ },
+ {
+ name: 'Smärky Kanum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxq'
+ },
+ {
+ name: 'Koro (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxr'
+ },
+ {
+ name: 'Kangjia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxs'
+ },
+ {
+ name: 'Koiwat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxt'
+ },
+ {
+ name: 'Kuvi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxv'
+ },
+ {
+ name: 'Konai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxw'
+ },
+ {
+ name: 'Likuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxx'
+ },
+ {
+ name: 'Kayong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxy'
+ },
+ {
+ name: 'Kerewo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kxz'
+ },
+ {
+ name: 'Kwaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kya'
+ },
+ {
+ name: 'Butbut Kalinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyb'
+ },
+ {
+ name: 'Kyaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyc'
+ },
+ {
+ name: 'Karey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyd'
+ },
+ {
+ name: 'Krache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kye'
+ },
+ {
+ name: 'Kouya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyf'
+ },
+ {
+ name: 'Keyagana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyg'
+ },
+ {
+ name: 'Karok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyh'
+ },
+ {
+ name: 'Kiput',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyi'
+ },
+ {
+ name: 'Karao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyj'
+ },
+ {
+ name: 'Kamayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyk'
+ },
+ {
+ name: 'Kalapuya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyl'
+ },
+ {
+ name: 'Kpatili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kym'
+ },
+ {
+ name: 'Northern Binukidnon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyn'
+ },
+ {
+ name: 'Kelon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyo'
+ },
+ {
+ name: 'Kang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyp'
+ },
+ {
+ name: 'Kenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyq'
+ },
+ {
+ name: 'Kuruáya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyr'
+ },
+ {
+ name: 'Baram Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kys'
+ },
+ {
+ name: 'Kayagar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyt'
+ },
+ {
+ name: 'Western Kayah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyu'
+ },
+ {
+ name: 'Kayort',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyv'
+ },
+ {
+ name: 'Kudmali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyw'
+ },
+ {
+ name: 'Rapoisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyx'
+ },
+ {
+ name: 'Kambaira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyy'
+ },
+ {
+ name: 'Kayabí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kyz'
+ },
+ {
+ name: 'Western Karaboro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kza'
+ },
+ {
+ name: 'Kaibobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzb'
+ },
+ {
+ name: 'Bondoukou Kulango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzc'
+ },
+ {
+ name: 'Kadai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzd'
+ },
+ {
+ name: 'Kosena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kze'
+ },
+ {
+ name: "Da'a Kaili",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzf'
+ },
+ {
+ name: 'Kikai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzg'
+ },
+ {
+ name: 'Kelabit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzi'
+ },
+ {
+ name: 'Kazukuru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kzk'
+ },
+ {
+ name: 'Kayeli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzl'
+ },
+ {
+ name: 'Kais',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzm'
+ },
+ {
+ name: 'Kokola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzn'
+ },
+ {
+ name: 'Kaningi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzo'
+ },
+ {
+ name: 'Kaidipang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzp'
+ },
+ {
+ name: 'Kaike',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzq'
+ },
+ {
+ name: 'Karang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzr'
+ },
+ {
+ name: 'Sugut Dusun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzs'
+ },
+ {
+ name: 'Kayupulau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzu'
+ },
+ {
+ name: 'Komyandaret',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzv'
+ },
+ {
+ name: 'Karirí-Xocó',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kzw'
+ },
+ {
+ name: 'Kamarian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'kzx'
+ },
+ {
+ name: 'Kango (Tshopo District)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzy'
+ },
+ {
+ name: 'Kalabra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'kzz'
+ },
+ {
+ name: 'Southern Subanen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'laa'
+ },
+ {
+ name: 'Linear A',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'lab'
+ },
+ {
+ name: 'Lacandon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lac'
+ },
+ {
+ name: 'Ladino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lad',
+ iso6392B: 'lad',
+ iso6392T: 'lad'
+ },
+ {
+ name: 'Pattani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lae'
+ },
+ {
+ name: 'Lafofa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'laf'
+ },
+ {
+ name: 'Langi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lag'
+ },
+ {
+ name: 'Lahnda',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'lah',
+ iso6392B: 'lah',
+ iso6392T: 'lah'
+ },
+ {
+ name: 'Lambya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lai'
+ },
+ {
+ name: 'Lango (Uganda)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'laj'
+ },
+ {
+ name: 'Laka (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lak'
+ },
+ {
+ name: 'Lalia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lal'
+ },
+ {
+ name: 'Lamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lam',
+ iso6392B: 'lam',
+ iso6392T: 'lam'
+ },
+ {
+ name: 'Laru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lan'
+ },
+ {
+ name: 'Lao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lao',
+ iso6392B: 'lao',
+ iso6392T: 'lao',
+ iso6391: 'lo'
+ },
+ {
+ name: 'Laka (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lap'
+ },
+ {
+ name: 'Qabiao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'laq'
+ },
+ {
+ name: 'Larteh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lar'
+ },
+ {
+ name: 'Lama (Togo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'las'
+ },
+ {
+ name: 'Latin',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'lat',
+ iso6392B: 'lat',
+ iso6392T: 'lat',
+ iso6391: 'la'
+ },
+ {
+ name: 'Laba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lau'
+ },
+ {
+ name: 'Latvian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'lav',
+ iso6392B: 'lav',
+ iso6392T: 'lav',
+ iso6391: 'lv'
+ },
+ {
+ name: 'Lauje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'law'
+ },
+ {
+ name: 'Tiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lax'
+ },
+ {
+ name: 'Lama Bai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lay'
+ },
+ {
+ name: 'Aribwatsa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'laz'
+ },
+ {
+ name: 'Label',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbb'
+ },
+ {
+ name: 'Lakkia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbc'
+ },
+ {
+ name: 'Lak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbe'
+ },
+ {
+ name: 'Tinani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbf'
+ },
+ {
+ name: 'Laopang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbg'
+ },
+ {
+ name: "La'bi",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbi'
+ },
+ {
+ name: 'Ladakhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbj'
+ },
+ {
+ name: 'Central Bontok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbk'
+ },
+ {
+ name: 'Libon Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbl'
+ },
+ {
+ name: 'Lodhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbm'
+ },
+ {
+ name: 'Rmeet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbn'
+ },
+ {
+ name: 'Laven',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbo'
+ },
+ {
+ name: 'Wampar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbq'
+ },
+ {
+ name: 'Lohorung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbr'
+ },
+ {
+ name: 'Libyan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbs'
+ },
+ {
+ name: 'Lachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbt'
+ },
+ {
+ name: 'Labu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbu'
+ },
+ {
+ name: 'Lavatbura-Lamusong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbv'
+ },
+ {
+ name: 'Tolaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbw'
+ },
+ {
+ name: 'Lawangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbx'
+ },
+ {
+ name: 'Lamalama',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lby'
+ },
+ {
+ name: 'Lardil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lbz'
+ },
+ {
+ name: 'Legenyem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcc'
+ },
+ {
+ name: 'Lola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcd'
+ },
+ {
+ name: 'Loncong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lce'
+ },
+ {
+ name: 'Lubu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcf'
+ },
+ {
+ name: 'Luchazi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lch'
+ },
+ {
+ name: 'Lisela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcl'
+ },
+ {
+ name: 'Tungag',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcm'
+ },
+ {
+ name: 'Western Lawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcp'
+ },
+ {
+ name: 'Luhu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcq'
+ },
+ {
+ name: 'Lisabata-Nuniali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lcs'
+ },
+ {
+ name: 'Kla-Dan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lda'
+ },
+ {
+ name: 'Dũya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldb'
+ },
+ {
+ name: 'Luri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldd'
+ },
+ {
+ name: 'Lenyima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldg'
+ },
+ {
+ name: 'Lamja-Dengsa-Tola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldh'
+ },
+ {
+ name: 'Laari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldi'
+ },
+ {
+ name: 'Lemoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldj'
+ },
+ {
+ name: 'Leelau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldk'
+ },
+ {
+ name: 'Kaan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldl'
+ },
+ {
+ name: 'Landoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldm'
+ },
+ {
+ name: 'Láadan',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'ldn'
+ },
+ {
+ name: 'Loo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldo'
+ },
+ {
+ name: 'Tso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldp'
+ },
+ {
+ name: 'Lufu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ldq'
+ },
+ {
+ name: 'Lega-Shabunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lea'
+ },
+ {
+ name: 'Lala-Bisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'leb'
+ },
+ {
+ name: 'Leco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lec'
+ },
+ {
+ name: 'Lendu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'led'
+ },
+ {
+ name: 'Lyélé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lee'
+ },
+ {
+ name: 'Lelemi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lef'
+ },
+ {
+ name: 'Lenje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'leh'
+ },
+ {
+ name: 'Lemio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lei'
+ },
+ {
+ name: 'Lengola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lej'
+ },
+ {
+ name: 'Leipon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lek'
+ },
+ {
+ name: 'Lele (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lel'
+ },
+ {
+ name: 'Nomaande',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lem'
+ },
+ {
+ name: 'Lenca',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'len'
+ },
+ {
+ name: 'Leti (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'leo'
+ },
+ {
+ name: 'Lepcha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lep'
+ },
+ {
+ name: 'Lembena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'leq'
+ },
+ {
+ name: 'Lenkau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ler'
+ },
+ {
+ name: 'Lese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'les'
+ },
+ {
+ name: 'Lesing-Gelimi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'let'
+ },
+ {
+ name: 'Kara (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'leu'
+ },
+ {
+ name: 'Lamma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lev'
+ },
+ {
+ name: 'Ledo Kaili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lew'
+ },
+ {
+ name: 'Luang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lex'
+ },
+ {
+ name: 'Lemolang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ley'
+ },
+ {
+ name: 'Lezghian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lez',
+ iso6392B: 'lez',
+ iso6392T: 'lez'
+ },
+ {
+ name: 'Lefa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lfa'
+ },
+ {
+ name: 'Lingua Franca Nova',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'lfn'
+ },
+ {
+ name: 'Lungga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lga'
+ },
+ {
+ name: 'Laghu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgb'
+ },
+ {
+ name: 'Lugbara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgg'
+ },
+ {
+ name: 'Laghuu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgh'
+ },
+ {
+ name: 'Lengilu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgi'
+ },
+ {
+ name: 'Lingarak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgk'
+ },
+ {
+ name: 'Wala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgl'
+ },
+ {
+ name: 'Lega-Mwenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgm'
+ },
+ {
+ name: "T'apo",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgn'
+ },
+ {
+ name: 'Logba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgq'
+ },
+ {
+ name: 'Lengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgr'
+ },
+ {
+ name: 'Pahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgt'
+ },
+ {
+ name: 'Longgu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgu'
+ },
+ {
+ name: 'Ligenza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lgz'
+ },
+ {
+ name: 'Laha (Viet Nam)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lha'
+ },
+ {
+ name: 'Laha (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhh'
+ },
+ {
+ name: 'Lahu Shi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhi'
+ },
+ {
+ name: 'Lahul Lohar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhl'
+ },
+ {
+ name: 'Lhomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhm'
+ },
+ {
+ name: 'Lahanan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhn'
+ },
+ {
+ name: 'Lhokpu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhp'
+ },
+ {
+ name: 'Mlahsö',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lhs'
+ },
+ {
+ name: 'Lo-Toga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lht'
+ },
+ {
+ name: 'Lahu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lhu'
+ },
+ {
+ name: 'West-Central Limba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lia'
+ },
+ {
+ name: 'Likum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lib'
+ },
+ {
+ name: 'Hlai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lic'
+ },
+ {
+ name: 'Nyindrou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lid'
+ },
+ {
+ name: 'Likila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lie'
+ },
+ {
+ name: 'Limbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lif'
+ },
+ {
+ name: 'Ligbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lig'
+ },
+ {
+ name: 'Lihir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lih'
+ },
+ {
+ name: 'Ligurian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lij'
+ },
+ {
+ name: 'Lika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lik'
+ },
+ {
+ name: 'Lillooet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lil'
+ },
+ {
+ name: 'Limburgan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lim',
+ iso6392B: 'lim',
+ iso6392T: 'lim',
+ iso6391: 'li'
+ },
+ {
+ name: 'Lingala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lin',
+ iso6392B: 'lin',
+ iso6392T: 'lin',
+ iso6391: 'ln'
+ },
+ {
+ name: 'Liki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lio'
+ },
+ {
+ name: 'Sekpele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lip'
+ },
+ {
+ name: 'Libido',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liq'
+ },
+ {
+ name: 'Liberian English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lir'
+ },
+ {
+ name: 'Lisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lis'
+ },
+ {
+ name: 'Lithuanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lit',
+ iso6392B: 'lit',
+ iso6392T: 'lit',
+ iso6391: 'lt'
+ },
+ {
+ name: 'Logorik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liu'
+ },
+ {
+ name: 'Liv',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liv'
+ },
+ {
+ name: 'Col',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liw'
+ },
+ {
+ name: 'Liabuku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lix'
+ },
+ {
+ name: 'Banda-Bambari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liy'
+ },
+ {
+ name: 'Libinza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'liz'
+ },
+ {
+ name: 'Golpa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lja'
+ },
+ {
+ name: 'Rampi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lje'
+ },
+ {
+ name: 'Laiyolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lji'
+ },
+ {
+ name: "Li'o",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ljl'
+ },
+ {
+ name: 'Lampung Api',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ljp'
+ },
+ {
+ name: 'Yirandali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ljw'
+ },
+ {
+ name: 'Yuru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ljx'
+ },
+ {
+ name: 'Lakalei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lka'
+ },
+ {
+ name: 'Kabras',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkb'
+ },
+ {
+ name: 'Kucong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkc'
+ },
+ {
+ name: 'Lakondê',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkd'
+ },
+ {
+ name: 'Kenyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lke'
+ },
+ {
+ name: 'Lakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkh'
+ },
+ {
+ name: 'Laki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lki'
+ },
+ {
+ name: 'Remun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkj'
+ },
+ {
+ name: 'Laeko-Libuat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkl'
+ },
+ {
+ name: 'Kalaamaya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lkm'
+ },
+ {
+ name: 'Lakon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkn'
+ },
+ {
+ name: 'Khayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lko'
+ },
+ {
+ name: 'Päri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkr'
+ },
+ {
+ name: 'Kisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lks'
+ },
+ {
+ name: 'Lakota',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lkt'
+ },
+ {
+ name: 'Kungkari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lku'
+ },
+ {
+ name: 'Lokoya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lky'
+ },
+ {
+ name: 'Lala-Roba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lla'
+ },
+ {
+ name: 'Lolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llb'
+ },
+ {
+ name: 'Lele (Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llc'
+ },
+ {
+ name: 'Ladin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lld'
+ },
+ {
+ name: 'Lele (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lle'
+ },
+ {
+ name: 'Hermit',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'llf'
+ },
+ {
+ name: 'Lole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llg'
+ },
+ {
+ name: 'Lamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llh'
+ },
+ {
+ name: 'Teke-Laali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lli'
+ },
+ {
+ name: 'Ladji Ladji',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'llj'
+ },
+ {
+ name: 'Lelak',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'llk'
+ },
+ {
+ name: 'Lilau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lll'
+ },
+ {
+ name: 'Lasalimu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llm'
+ },
+ {
+ name: 'Lele (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lln'
+ },
+ {
+ name: 'North Efate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llp'
+ },
+ {
+ name: 'Lolak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llq'
+ },
+ {
+ name: 'Lithuanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lls'
+ },
+ {
+ name: 'Lau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llu'
+ },
+ {
+ name: 'Lauan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'llx'
+ },
+ {
+ name: 'East Limba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lma'
+ },
+ {
+ name: 'Merei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmb'
+ },
+ {
+ name: 'Limilngan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lmc'
+ },
+ {
+ name: 'Lumun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmd'
+ },
+ {
+ name: 'Pévé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lme'
+ },
+ {
+ name: 'South Lembata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmf'
+ },
+ {
+ name: 'Lamogai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmg'
+ },
+ {
+ name: 'Lambichhong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmh'
+ },
+ {
+ name: 'Lombi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmi'
+ },
+ {
+ name: 'West Lembata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmj'
+ },
+ {
+ name: 'Lamkang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmk'
+ },
+ {
+ name: 'Hano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lml'
+ },
+ {
+ name: 'Lambadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmn'
+ },
+ {
+ name: 'Lombard',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmo'
+ },
+ {
+ name: 'Limbum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmp'
+ },
+ {
+ name: 'Lamatuka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmq'
+ },
+ {
+ name: 'Lamalera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmr'
+ },
+ {
+ name: 'Lamenu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmu'
+ },
+ {
+ name: 'Lomaiviti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmv'
+ },
+ {
+ name: 'Lake Miwok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmw'
+ },
+ {
+ name: 'Laimbue',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmx'
+ },
+ {
+ name: 'Lamboya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lmy'
+ },
+ {
+ name: 'Langbashe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lna'
+ },
+ {
+ name: 'Mbalanhu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnb'
+ },
+ {
+ name: 'Lundayeh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnd'
+ },
+ {
+ name: 'Langobardic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'lng'
+ },
+ {
+ name: 'Lanoh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnh'
+ },
+ {
+ name: "Daantanai'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lni'
+ },
+ {
+ name: 'Leningitij',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lnj'
+ },
+ {
+ name: 'South Central Banda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnl'
+ },
+ {
+ name: 'Langam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnm'
+ },
+ {
+ name: 'Lorediakarkar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnn'
+ },
+ {
+ name: 'Lango (South Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lno'
+ },
+ {
+ name: "Lamnso'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lns'
+ },
+ {
+ name: 'Longuda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnu'
+ },
+ {
+ name: 'Lanima',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lnw'
+ },
+ {
+ name: 'Lonzo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lnz'
+ },
+ {
+ name: 'Loloda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loa'
+ },
+ {
+ name: 'Lobi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lob'
+ },
+ {
+ name: 'Inonhan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loc'
+ },
+ {
+ name: 'Saluan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loe'
+ },
+ {
+ name: 'Logol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lof'
+ },
+ {
+ name: 'Logo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'log'
+ },
+ {
+ name: 'Narim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loh'
+ },
+ {
+ name: "Loma (Côte d'Ivoire)",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loi'
+ },
+ {
+ name: 'Lou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loj'
+ },
+ {
+ name: 'Loko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lok'
+ },
+ {
+ name: 'Mongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lol',
+ iso6392B: 'lol',
+ iso6392T: 'lol'
+ },
+ {
+ name: 'Loma (Liberia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lom'
+ },
+ {
+ name: 'Malawi Lomwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lon'
+ },
+ {
+ name: 'Lombo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loo'
+ },
+ {
+ name: 'Lopa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lop'
+ },
+ {
+ name: 'Lobala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loq'
+ },
+ {
+ name: 'Téén',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lor'
+ },
+ {
+ name: 'Loniu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'los'
+ },
+ {
+ name: 'Otuho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lot'
+ },
+ {
+ name: 'Louisiana Creole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lou'
+ },
+ {
+ name: 'Lopi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lov'
+ },
+ {
+ name: 'Tampias Lobu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'low'
+ },
+ {
+ name: 'Loun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lox'
+ },
+ {
+ name: 'Loke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loy'
+ },
+ {
+ name: 'Lozi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'loz',
+ iso6392B: 'loz',
+ iso6392T: 'loz'
+ },
+ {
+ name: 'Lelepa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lpa'
+ },
+ {
+ name: 'Lepki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lpe'
+ },
+ {
+ name: 'Long Phuri Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lpn'
+ },
+ {
+ name: 'Lipo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lpo'
+ },
+ {
+ name: 'Lopit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lpx'
+ },
+ {
+ name: "Rara Bakati'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lra'
+ },
+ {
+ name: 'Northern Luri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrc'
+ },
+ {
+ name: 'Laurentian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lre'
+ },
+ {
+ name: 'Laragia',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lrg'
+ },
+ {
+ name: 'Marachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lri'
+ },
+ {
+ name: 'Loarki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrk'
+ },
+ {
+ name: 'Lari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrl'
+ },
+ {
+ name: 'Marama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrm'
+ },
+ {
+ name: 'Lorang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrn'
+ },
+ {
+ name: 'Laro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lro'
+ },
+ {
+ name: 'Southern Yamphu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrr'
+ },
+ {
+ name: 'Larantuka Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrt'
+ },
+ {
+ name: 'Larevat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrv'
+ },
+ {
+ name: 'Lemerig',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lrz'
+ },
+ {
+ name: 'Lasgerdi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsa'
+ },
+ {
+ name: 'Lishana Deni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsd'
+ },
+ {
+ name: 'Lusengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lse'
+ },
+ {
+ name: 'Lish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsh'
+ },
+ {
+ name: 'Lashi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsi'
+ },
+ {
+ name: 'Latvian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsl'
+ },
+ {
+ name: 'Saamia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsm'
+ },
+ {
+ name: 'Tibetan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsn'
+ },
+ {
+ name: 'Laos Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lso'
+ },
+ {
+ name: 'Panamanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsp'
+ },
+ {
+ name: 'Aruop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsr'
+ },
+ {
+ name: 'Lasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lss'
+ },
+ {
+ name: 'Trinidad and Tobago Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lst'
+ },
+ {
+ name: 'Sivia Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsv'
+ },
+ {
+ name: 'Mauritian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lsy'
+ },
+ {
+ name: 'Late Middle Chinese',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ltc'
+ },
+ {
+ name: 'Latgalian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ltg'
+ },
+ {
+ name: 'Thur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lth'
+ },
+ {
+ name: 'Leti (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lti'
+ },
+ {
+ name: 'Latundê',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ltn'
+ },
+ {
+ name: 'Tsotso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lto'
+ },
+ {
+ name: 'Tachoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lts'
+ },
+ {
+ name: 'Latu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ltu'
+ },
+ {
+ name: 'Luxembourgish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ltz',
+ iso6392B: 'ltz',
+ iso6392T: 'ltz',
+ iso6391: 'lb'
+ },
+ {
+ name: 'Luba-Lulua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lua',
+ iso6392B: 'lua',
+ iso6392T: 'lua'
+ },
+ {
+ name: 'Luba-Katanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lub',
+ iso6392B: 'lub',
+ iso6392T: 'lub',
+ iso6391: 'lu'
+ },
+ {
+ name: 'Aringa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luc'
+ },
+ {
+ name: 'Ludian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lud'
+ },
+ {
+ name: 'Luvale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lue'
+ },
+ {
+ name: 'Laua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luf'
+ },
+ {
+ name: 'Ganda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lug',
+ iso6392B: 'lug',
+ iso6392T: 'lug',
+ iso6391: 'lg'
+ },
+ {
+ name: 'Luiseno',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'lui',
+ iso6392B: 'lui',
+ iso6392T: 'lui'
+ },
+ {
+ name: 'Luna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luj'
+ },
+ {
+ name: 'Lunanakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luk'
+ },
+ {
+ name: "Olu'bo",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lul'
+ },
+ {
+ name: 'Luimbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lum'
+ },
+ {
+ name: 'Lunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lun',
+ iso6392B: 'lun',
+ iso6392T: 'lun'
+ },
+ {
+ name: 'Luo (Kenya and Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luo',
+ iso6392B: 'luo',
+ iso6392T: 'luo'
+ },
+ {
+ name: 'Lumbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lup'
+ },
+ {
+ name: 'Lucumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luq'
+ },
+ {
+ name: 'Laura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lur'
+ },
+ {
+ name: 'Lushai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lus',
+ iso6392B: 'lus',
+ iso6392T: 'lus'
+ },
+ {
+ name: 'Lushootseed',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lut'
+ },
+ {
+ name: 'Lumba-Yakkha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luu'
+ },
+ {
+ name: 'Luwati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luv'
+ },
+ {
+ name: 'Luo (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luw'
+ },
+ {
+ name: 'Luyia',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'luy'
+ },
+ {
+ name: 'Southern Luri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'luz'
+ },
+ {
+ name: "Maku'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lva'
+ },
+ {
+ name: 'Lavi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lvi'
+ },
+ {
+ name: 'Lavukaleve',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lvk'
+ },
+ {
+ name: 'Standard Latvian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lvs'
+ },
+ {
+ name: 'Levuka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lvu'
+ },
+ {
+ name: 'Lwalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwa'
+ },
+ {
+ name: 'Lewo Eleng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwe'
+ },
+ {
+ name: 'Wanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwg'
+ },
+ {
+ name: 'White Lachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwh'
+ },
+ {
+ name: 'Eastern Lawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwl'
+ },
+ {
+ name: 'Laomian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwm'
+ },
+ {
+ name: 'Luwo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwo'
+ },
+ {
+ name: 'Malawian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lws'
+ },
+ {
+ name: 'Lewotobi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwt'
+ },
+ {
+ name: 'Lawu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lwu'
+ },
+ {
+ name: 'Lewo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lww'
+ },
+ {
+ name: 'Layakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lya'
+ },
+ {
+ name: 'Lyngngam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lyg'
+ },
+ {
+ name: 'Luyana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lyn'
+ },
+ {
+ name: 'Literary Chinese',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'lzh'
+ },
+ {
+ name: 'Litzlitz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lzl'
+ },
+ {
+ name: 'Leinong Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lzn'
+ },
+ {
+ name: 'Laz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'lzz'
+ },
+ {
+ name: 'San Jerónimo Tecóatl Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maa'
+ },
+ {
+ name: 'Yutanduchi Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mab'
+ },
+ {
+ name: 'Madurese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mad',
+ iso6392B: 'mad',
+ iso6392T: 'mad'
+ },
+ {
+ name: 'Bo-Rukul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mae'
+ },
+ {
+ name: 'Mafa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maf'
+ },
+ {
+ name: 'Magahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mag',
+ iso6392B: 'mag',
+ iso6392T: 'mag'
+ },
+ {
+ name: 'Marshallese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mah',
+ iso6392B: 'mah',
+ iso6392T: 'mah',
+ iso6391: 'mh'
+ },
+ {
+ name: 'Maithili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mai',
+ iso6392B: 'mai',
+ iso6392T: 'mai'
+ },
+ {
+ name: 'Jalapa De Díaz Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maj'
+ },
+ {
+ name: 'Makasar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mak',
+ iso6392B: 'mak',
+ iso6392T: 'mak'
+ },
+ {
+ name: 'Malayalam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mal',
+ iso6392B: 'mal',
+ iso6392T: 'mal',
+ iso6391: 'ml'
+ },
+ {
+ name: 'Mam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mam'
+ },
+ {
+ name: 'Mandingo',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'man',
+ iso6392B: 'man',
+ iso6392T: 'man'
+ },
+ {
+ name: 'Chiquihuitlán Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maq'
+ },
+ {
+ name: 'Marathi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mar',
+ iso6392B: 'mar',
+ iso6392T: 'mar',
+ iso6391: 'mr'
+ },
+ {
+ name: 'Masai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mas',
+ iso6392B: 'mas',
+ iso6392T: 'mas'
+ },
+ {
+ name: 'San Francisco Matlatzinca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mat'
+ },
+ {
+ name: 'Huautla Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mau'
+ },
+ {
+ name: 'Sateré-Mawé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mav'
+ },
+ {
+ name: 'Mampruli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maw'
+ },
+ {
+ name: 'North Moluccan Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'max'
+ },
+ {
+ name: 'Central Mazahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'maz'
+ },
+ {
+ name: 'Higaonon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mba'
+ },
+ {
+ name: 'Western Bukidnon Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbb'
+ },
+ {
+ name: 'Macushi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbc'
+ },
+ {
+ name: 'Dibabawon Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbd'
+ },
+ {
+ name: 'Molale',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mbe'
+ },
+ {
+ name: 'Baba Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbf'
+ },
+ {
+ name: 'Mangseng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbh'
+ },
+ {
+ name: 'Ilianen Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbi'
+ },
+ {
+ name: 'Nadëb',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbj'
+ },
+ {
+ name: 'Malol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbk'
+ },
+ {
+ name: 'Maxakalí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbl'
+ },
+ {
+ name: 'Ombamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbm'
+ },
+ {
+ name: 'Macaguán',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbn'
+ },
+ {
+ name: 'Mbo (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbo'
+ },
+ {
+ name: 'Malayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbp'
+ },
+ {
+ name: 'Maisin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbq'
+ },
+ {
+ name: 'Nukak Makú',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbr'
+ },
+ {
+ name: 'Sarangani Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbs'
+ },
+ {
+ name: 'Matigsalug Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbt'
+ },
+ {
+ name: 'Mbula-Bwazza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbu'
+ },
+ {
+ name: 'Mbulungish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbv'
+ },
+ {
+ name: 'Maring',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbw'
+ },
+ {
+ name: 'Mari (East Sepik Province)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbx'
+ },
+ {
+ name: 'Memoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mby'
+ },
+ {
+ name: 'Amoltepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mbz'
+ },
+ {
+ name: 'Maca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mca'
+ },
+ {
+ name: 'Machiguenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcb'
+ },
+ {
+ name: 'Bitur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcc'
+ },
+ {
+ name: 'Sharanahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcd'
+ },
+ {
+ name: 'Itundujia Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mce'
+ },
+ {
+ name: 'Matsés',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcf'
+ },
+ {
+ name: 'Mapoyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcg'
+ },
+ {
+ name: 'Maquiritari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mch'
+ },
+ {
+ name: 'Mese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mci'
+ },
+ {
+ name: 'Mvanip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcj'
+ },
+ {
+ name: 'Mbunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mck'
+ },
+ {
+ name: 'Macaguaje',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mcl'
+ },
+ {
+ name: 'Malaccan Creole Portuguese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcm'
+ },
+ {
+ name: 'Masana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcn'
+ },
+ {
+ name: 'Coatlán Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mco'
+ },
+ {
+ name: 'Makaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcp'
+ },
+ {
+ name: 'Ese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcq'
+ },
+ {
+ name: 'Menya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcr'
+ },
+ {
+ name: 'Mambai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcs'
+ },
+ {
+ name: 'Mengisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mct'
+ },
+ {
+ name: 'Cameroon Mambila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcu'
+ },
+ {
+ name: 'Minanibai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcv'
+ },
+ {
+ name: 'Mawa (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcw'
+ },
+ {
+ name: 'Mpiemo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcx'
+ },
+ {
+ name: 'South Watut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcy'
+ },
+ {
+ name: 'Mawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mcz'
+ },
+ {
+ name: 'Mada (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mda'
+ },
+ {
+ name: 'Morigi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdb'
+ },
+ {
+ name: 'Male (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdc'
+ },
+ {
+ name: 'Mbum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdd'
+ },
+ {
+ name: 'Maba (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mde'
+ },
+ {
+ name: 'Moksha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdf',
+ iso6392B: 'mdf',
+ iso6392T: 'mdf'
+ },
+ {
+ name: 'Massalat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdg'
+ },
+ {
+ name: 'Maguindanaon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdh'
+ },
+ {
+ name: 'Mamvu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdi'
+ },
+ {
+ name: 'Mangbetu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdj'
+ },
+ {
+ name: 'Mangbutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdk'
+ },
+ {
+ name: 'Maltese Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdl'
+ },
+ {
+ name: 'Mayogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdm'
+ },
+ {
+ name: 'Mbati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdn'
+ },
+ {
+ name: 'Mbala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdp'
+ },
+ {
+ name: 'Mbole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdq'
+ },
+ {
+ name: 'Mandar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdr',
+ iso6392B: 'mdr',
+ iso6392T: 'mdr'
+ },
+ {
+ name: 'Maria (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mds'
+ },
+ {
+ name: 'Mbere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdt'
+ },
+ {
+ name: 'Mboko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdu'
+ },
+ {
+ name: 'Santa Lucía Monteverde Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdv'
+ },
+ {
+ name: 'Mbosi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdw'
+ },
+ {
+ name: 'Dizin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdx'
+ },
+ {
+ name: 'Male (Ethiopia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdy'
+ },
+ {
+ name: 'Suruí Do Pará',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mdz'
+ },
+ {
+ name: 'Menka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mea'
+ },
+ {
+ name: 'Ikobi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'meb'
+ },
+ {
+ name: 'Marra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mec'
+ },
+ {
+ name: 'Melpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'med'
+ },
+ {
+ name: 'Mengen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mee'
+ },
+ {
+ name: 'Megam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mef'
+ },
+ {
+ name: 'Southwestern Tlaxiaco Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'meh'
+ },
+ {
+ name: 'Midob',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mei'
+ },
+ {
+ name: 'Meyah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mej'
+ },
+ {
+ name: 'Mekeo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mek'
+ },
+ {
+ name: 'Central Melanau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mel'
+ },
+ {
+ name: 'Mangala',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mem'
+ },
+ {
+ name: 'Mende (Sierra Leone)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'men',
+ iso6392B: 'men',
+ iso6392T: 'men'
+ },
+ {
+ name: 'Kedah Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'meo'
+ },
+ {
+ name: 'Miriwoong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mep'
+ },
+ {
+ name: 'Merey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'meq'
+ },
+ {
+ name: 'Meru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mer'
+ },
+ {
+ name: 'Masmaje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mes'
+ },
+ {
+ name: 'Mato',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'met'
+ },
+ {
+ name: 'Motu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'meu'
+ },
+ {
+ name: 'Mano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mev'
+ },
+ {
+ name: 'Maaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mew'
+ },
+ {
+ name: 'Hassaniyya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mey'
+ },
+ {
+ name: 'Menominee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mez'
+ },
+ {
+ name: 'Pattani Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfa'
+ },
+ {
+ name: 'Bangka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfb'
+ },
+ {
+ name: 'Mba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfc'
+ },
+ {
+ name: 'Mendankwe-Nkwen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfd'
+ },
+ {
+ name: 'Morisyen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfe'
+ },
+ {
+ name: 'Naki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mff'
+ },
+ {
+ name: 'Mogofin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfg'
+ },
+ {
+ name: 'Matal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfh'
+ },
+ {
+ name: 'Wandala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfi'
+ },
+ {
+ name: 'Mefele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfj'
+ },
+ {
+ name: 'North Mofu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfk'
+ },
+ {
+ name: 'Putai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfl'
+ },
+ {
+ name: 'Marghi South',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfm'
+ },
+ {
+ name: 'Cross River Mbembe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfn'
+ },
+ {
+ name: 'Mbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfo'
+ },
+ {
+ name: 'Makassar Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfp'
+ },
+ {
+ name: 'Moba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfq'
+ },
+ {
+ name: 'Marrithiyel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfr'
+ },
+ {
+ name: 'Mexican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfs'
+ },
+ {
+ name: 'Mokerang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mft'
+ },
+ {
+ name: 'Mbwela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfu'
+ },
+ {
+ name: 'Mandjak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfv'
+ },
+ {
+ name: 'Mulaha',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mfw'
+ },
+ {
+ name: 'Melo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfx'
+ },
+ {
+ name: 'Mayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfy'
+ },
+ {
+ name: 'Mabaan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mfz'
+ },
+ {
+ name: 'Middle Irish (900-1200)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'mga',
+ iso6392B: 'mga',
+ iso6392T: 'mga'
+ },
+ {
+ name: 'Mararit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgb'
+ },
+ {
+ name: 'Morokodo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgc'
+ },
+ {
+ name: 'Moru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgd'
+ },
+ {
+ name: 'Mango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mge'
+ },
+ {
+ name: 'Maklew',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgf'
+ },
+ {
+ name: 'Mpumpong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgg'
+ },
+ {
+ name: 'Makhuwa-Meetto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgh'
+ },
+ {
+ name: 'Lijili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgi'
+ },
+ {
+ name: 'Abureni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgj'
+ },
+ {
+ name: 'Mawes',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgk'
+ },
+ {
+ name: 'Maleu-Kilenge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgl'
+ },
+ {
+ name: 'Mambae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgm'
+ },
+ {
+ name: 'Mbangi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgn'
+ },
+ {
+ name: "Meta'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgo'
+ },
+ {
+ name: 'Eastern Magar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgp'
+ },
+ {
+ name: 'Malila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgq'
+ },
+ {
+ name: 'Mambwe-Lungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgr'
+ },
+ {
+ name: 'Manda (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgs'
+ },
+ {
+ name: 'Mongol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgt'
+ },
+ {
+ name: 'Mailu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgu'
+ },
+ {
+ name: 'Matengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgv'
+ },
+ {
+ name: 'Matumbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgw'
+ },
+ {
+ name: 'Mbunga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgy'
+ },
+ {
+ name: 'Mbugwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mgz'
+ },
+ {
+ name: 'Manda (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mha'
+ },
+ {
+ name: 'Mahongwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhb'
+ },
+ {
+ name: 'Mocho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhc'
+ },
+ {
+ name: 'Mbugu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhd'
+ },
+ {
+ name: 'Besisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhe'
+ },
+ {
+ name: 'Mamaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhf'
+ },
+ {
+ name: 'Margu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhg'
+ },
+ {
+ name: "Ma'di",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhi'
+ },
+ {
+ name: 'Mogholi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhj'
+ },
+ {
+ name: 'Mungaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhk'
+ },
+ {
+ name: 'Mauwake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhl'
+ },
+ {
+ name: 'Makhuwa-Moniga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhm'
+ },
+ {
+ name: 'Mócheno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhn'
+ },
+ {
+ name: 'Mashi (Zambia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mho'
+ },
+ {
+ name: 'Balinese Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhp'
+ },
+ {
+ name: 'Mandan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhq'
+ },
+ {
+ name: 'Eastern Mari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhr'
+ },
+ {
+ name: 'Buru (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhs'
+ },
+ {
+ name: 'Mandahuaca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mht'
+ },
+ {
+ name: 'Digaro-Mishmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhu'
+ },
+ {
+ name: 'Mbukushu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhw'
+ },
+ {
+ name: 'Maru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhx'
+ },
+ {
+ name: "Ma'anyan",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhy'
+ },
+ {
+ name: 'Mor (Mor Islands)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mhz'
+ },
+ {
+ name: 'Miami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mia'
+ },
+ {
+ name: 'Atatláhuca Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mib'
+ },
+ {
+ name: "Mi'kmaq",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mic',
+ iso6392B: 'mic',
+ iso6392T: 'mic'
+ },
+ {
+ name: 'Mandaic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mid'
+ },
+ {
+ name: 'Ocotepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mie'
+ },
+ {
+ name: 'Mofu-Gudur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mif'
+ },
+ {
+ name: 'San Miguel El Grande Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mig'
+ },
+ {
+ name: 'Chayuco Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mih'
+ },
+ {
+ name: 'Chigmecatitlán Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mii'
+ },
+ {
+ name: 'Abar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mij'
+ },
+ {
+ name: 'Mikasuki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mik'
+ },
+ {
+ name: 'Peñoles Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mil'
+ },
+ {
+ name: 'Alacatlatzala Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mim'
+ },
+ {
+ name: 'Minangkabau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'min',
+ iso6392B: 'min',
+ iso6392T: 'min'
+ },
+ {
+ name: 'Pinotepa Nacional Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mio'
+ },
+ {
+ name: 'Apasco-Apoala Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mip'
+ },
+ {
+ name: 'Mískito',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'miq'
+ },
+ {
+ name: 'Isthmus Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mir'
+ },
+ {
+ name: 'Uncoded languages',
+ type: 'special',
+ scope: 'special',
+ iso6393: 'mis',
+ iso6392B: 'mis',
+ iso6392T: 'mis'
+ },
+ {
+ name: 'Southern Puebla Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mit'
+ },
+ {
+ name: 'Cacaloxtepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'miu'
+ },
+ {
+ name: 'Akoye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'miw'
+ },
+ {
+ name: 'Mixtepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mix'
+ },
+ {
+ name: 'Ayutla Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'miy'
+ },
+ {
+ name: 'Coatzospan Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'miz'
+ },
+ {
+ name: 'Makalero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjb'
+ },
+ {
+ name: 'San Juan Colorado Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjc'
+ },
+ {
+ name: 'Northwest Maidu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjd'
+ },
+ {
+ name: 'Muskum',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mje'
+ },
+ {
+ name: 'Tu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjg'
+ },
+ {
+ name: 'Mwera (Nyasa)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjh'
+ },
+ {
+ name: 'Kim Mun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mji'
+ },
+ {
+ name: 'Mawak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjj'
+ },
+ {
+ name: 'Matukar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjk'
+ },
+ {
+ name: 'Mandeali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjl'
+ },
+ {
+ name: 'Medebur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjm'
+ },
+ {
+ name: 'Ma (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjn'
+ },
+ {
+ name: 'Malankuravan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjo'
+ },
+ {
+ name: 'Malapandaram',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjp'
+ },
+ {
+ name: 'Malaryan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mjq'
+ },
+ {
+ name: 'Malavedan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjr'
+ },
+ {
+ name: 'Miship',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjs'
+ },
+ {
+ name: 'Sauria Paharia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjt'
+ },
+ {
+ name: 'Manna-Dora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mju'
+ },
+ {
+ name: 'Mannan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjv'
+ },
+ {
+ name: 'Karbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjw'
+ },
+ {
+ name: 'Mahali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjx'
+ },
+ {
+ name: 'Mahican',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mjy'
+ },
+ {
+ name: 'Majhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mjz'
+ },
+ {
+ name: 'Mbre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mka'
+ },
+ {
+ name: 'Mal Paharia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkb'
+ },
+ {
+ name: 'Siliput',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkc'
+ },
+ {
+ name: 'Macedonian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkd',
+ iso6392B: 'mac',
+ iso6392T: 'mkd',
+ iso6391: 'mk'
+ },
+ {
+ name: 'Mawchi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mke'
+ },
+ {
+ name: 'Miya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkf'
+ },
+ {
+ name: 'Mak (China)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkg'
+ },
+ {
+ name: 'Dhatki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mki'
+ },
+ {
+ name: 'Mokilese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkj'
+ },
+ {
+ name: 'Byep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkk'
+ },
+ {
+ name: 'Mokole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkl'
+ },
+ {
+ name: 'Moklen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkm'
+ },
+ {
+ name: 'Kupang Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkn'
+ },
+ {
+ name: 'Mingang Doso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mko'
+ },
+ {
+ name: 'Moikodi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkp'
+ },
+ {
+ name: 'Bay Miwok',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mkq'
+ },
+ {
+ name: 'Malas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkr'
+ },
+ {
+ name: 'Silacayoapan Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mks'
+ },
+ {
+ name: 'Vamale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkt'
+ },
+ {
+ name: 'Konyanka Maninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mku'
+ },
+ {
+ name: 'Mafea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkv'
+ },
+ {
+ name: 'Kituba (Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkw'
+ },
+ {
+ name: 'Kinamiging Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkx'
+ },
+ {
+ name: 'East Makian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mky'
+ },
+ {
+ name: 'Makasae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mkz'
+ },
+ {
+ name: 'Malo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mla'
+ },
+ {
+ name: 'Mbule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlb'
+ },
+ {
+ name: 'Cao Lan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlc'
+ },
+ {
+ name: 'Manambu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mle'
+ },
+ {
+ name: 'Mal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlf'
+ },
+ {
+ name: 'Malagasy',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'mlg',
+ iso6392B: 'mlg',
+ iso6392T: 'mlg',
+ iso6391: 'mg'
+ },
+ {
+ name: 'Mape',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlh'
+ },
+ {
+ name: 'Malimpung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mli'
+ },
+ {
+ name: 'Miltu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlj'
+ },
+ {
+ name: 'Ilwana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlk'
+ },
+ {
+ name: 'Malua Bay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mll'
+ },
+ {
+ name: 'Mulam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlm'
+ },
+ {
+ name: 'Malango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mln'
+ },
+ {
+ name: 'Mlomp',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlo'
+ },
+ {
+ name: 'Bargam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlp'
+ },
+ {
+ name: 'Western Maninkakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlq'
+ },
+ {
+ name: 'Vame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlr'
+ },
+ {
+ name: 'Masalit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mls'
+ },
+ {
+ name: 'Maltese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlt',
+ iso6392B: 'mlt',
+ iso6392T: 'mlt',
+ iso6391: 'mt'
+ },
+ {
+ name: "To'abaita",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlu'
+ },
+ {
+ name: 'Motlav',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlv'
+ },
+ {
+ name: 'Moloko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlw'
+ },
+ {
+ name: 'Malfaxal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlx'
+ },
+ {
+ name: 'Malaynon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mlz'
+ },
+ {
+ name: 'Mama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mma'
+ },
+ {
+ name: 'Momina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmb'
+ },
+ {
+ name: 'Michoacán Mazahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmc'
+ },
+ {
+ name: 'Maonan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmd'
+ },
+ {
+ name: 'Mae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mme'
+ },
+ {
+ name: 'Mundat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmf'
+ },
+ {
+ name: 'North Ambrym',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmg'
+ },
+ {
+ name: 'Mehináku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmh'
+ },
+ {
+ name: 'Musar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmi'
+ },
+ {
+ name: 'Majhwar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmj'
+ },
+ {
+ name: 'Mukha-Dora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmk'
+ },
+ {
+ name: 'Man Met',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mml'
+ },
+ {
+ name: 'Maii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmm'
+ },
+ {
+ name: 'Mamanwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmn'
+ },
+ {
+ name: 'Mangga Buang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmo'
+ },
+ {
+ name: 'Siawi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmp'
+ },
+ {
+ name: 'Musak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmq'
+ },
+ {
+ name: 'Western Xiangxi Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmr'
+ },
+ {
+ name: 'Malalamai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmt'
+ },
+ {
+ name: 'Mmaala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmu'
+ },
+ {
+ name: 'Miriti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mmv'
+ },
+ {
+ name: 'Emae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmw'
+ },
+ {
+ name: 'Madak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmx'
+ },
+ {
+ name: 'Migaama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmy'
+ },
+ {
+ name: 'Mabaale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mmz'
+ },
+ {
+ name: 'Mbula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mna'
+ },
+ {
+ name: 'Muna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnb'
+ },
+ {
+ name: 'Manchu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnc',
+ iso6392B: 'mnc',
+ iso6392T: 'mnc'
+ },
+ {
+ name: 'Mondé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnd'
+ },
+ {
+ name: 'Naba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mne'
+ },
+ {
+ name: 'Mundani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnf'
+ },
+ {
+ name: 'Eastern Mnong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mng'
+ },
+ {
+ name: 'Mono (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnh'
+ },
+ {
+ name: 'Manipuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mni',
+ iso6392B: 'mni',
+ iso6392T: 'mni'
+ },
+ {
+ name: 'Munji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnj'
+ },
+ {
+ name: 'Mandinka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnk'
+ },
+ {
+ name: 'Tiale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnl'
+ },
+ {
+ name: 'Mapena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnm'
+ },
+ {
+ name: 'Southern Mnong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnn'
+ },
+ {
+ name: 'Min Bei Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnp'
+ },
+ {
+ name: 'Minriq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnq'
+ },
+ {
+ name: 'Mono (USA)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnr'
+ },
+ {
+ name: 'Mansi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mns'
+ },
+ {
+ name: 'Mer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnu'
+ },
+ {
+ name: 'Rennell-Bellona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnv'
+ },
+ {
+ name: 'Mon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnw'
+ },
+ {
+ name: 'Manikion',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnx'
+ },
+ {
+ name: 'Manyawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mny'
+ },
+ {
+ name: 'Moni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mnz'
+ },
+ {
+ name: 'Mwan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moa'
+ },
+ {
+ name: 'Mocoví',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moc'
+ },
+ {
+ name: 'Mobilian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mod'
+ },
+ {
+ name: 'Innu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moe'
+ },
+ {
+ name: 'Mongondow',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mog'
+ },
+ {
+ name: 'Mohawk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moh',
+ iso6392B: 'moh',
+ iso6392T: 'moh'
+ },
+ {
+ name: 'Mboi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moi'
+ },
+ {
+ name: 'Monzombo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moj'
+ },
+ {
+ name: 'Morori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mok'
+ },
+ {
+ name: 'Mangue',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mom'
+ },
+ {
+ name: 'Mongolian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'mon',
+ iso6392B: 'mon',
+ iso6392T: 'mon',
+ iso6391: 'mn'
+ },
+ {
+ name: 'Monom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moo'
+ },
+ {
+ name: 'Mopán Maya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mop'
+ },
+ {
+ name: 'Mor (Bomberai Peninsula)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moq'
+ },
+ {
+ name: 'Moro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mor'
+ },
+ {
+ name: 'Mossi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mos',
+ iso6392B: 'mos',
+ iso6392T: 'mos'
+ },
+ {
+ name: 'Barí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mot'
+ },
+ {
+ name: 'Mogum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mou'
+ },
+ {
+ name: 'Mohave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mov'
+ },
+ {
+ name: 'Moi (Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mow'
+ },
+ {
+ name: 'Molima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mox'
+ },
+ {
+ name: 'Shekkacho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moy'
+ },
+ {
+ name: 'Mukulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'moz'
+ },
+ {
+ name: 'Mpoto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpa'
+ },
+ {
+ name: 'Malak Malak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpb'
+ },
+ {
+ name: 'Mangarrayi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpc'
+ },
+ {
+ name: 'Machinere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpd'
+ },
+ {
+ name: 'Majang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpe'
+ },
+ {
+ name: 'Marba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpg'
+ },
+ {
+ name: 'Maung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mph'
+ },
+ {
+ name: 'Mpade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpi'
+ },
+ {
+ name: 'Martu Wangka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpj'
+ },
+ {
+ name: 'Mbara (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpk'
+ },
+ {
+ name: 'Middle Watut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpl'
+ },
+ {
+ name: 'Yosondúa Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpm'
+ },
+ {
+ name: 'Mindiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpn'
+ },
+ {
+ name: 'Miu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpo'
+ },
+ {
+ name: 'Migabac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpp'
+ },
+ {
+ name: 'Matís',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpq'
+ },
+ {
+ name: 'Vangunu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpr'
+ },
+ {
+ name: 'Dadibi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mps'
+ },
+ {
+ name: 'Mian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpt'
+ },
+ {
+ name: 'Makuráp',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpu'
+ },
+ {
+ name: 'Mungkip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpv'
+ },
+ {
+ name: 'Mapidian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpw'
+ },
+ {
+ name: 'Misima-Panaeati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpx'
+ },
+ {
+ name: 'Mapia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpy'
+ },
+ {
+ name: 'Mpi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mpz'
+ },
+ {
+ name: 'Maba (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqa'
+ },
+ {
+ name: 'Mbuko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqb'
+ },
+ {
+ name: 'Mangole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqc'
+ },
+ {
+ name: 'Matepi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqe'
+ },
+ {
+ name: 'Momuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqf'
+ },
+ {
+ name: 'Kota Bangun Kutai Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqg'
+ },
+ {
+ name: 'Tlazoyaltepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqh'
+ },
+ {
+ name: 'Mariri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqi'
+ },
+ {
+ name: 'Mamasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqj'
+ },
+ {
+ name: 'Rajah Kabunsuwan Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqk'
+ },
+ {
+ name: 'Mbelime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mql'
+ },
+ {
+ name: 'South Marquesan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqm'
+ },
+ {
+ name: 'Moronene',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqn'
+ },
+ {
+ name: 'Modole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqo'
+ },
+ {
+ name: 'Manipa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqp'
+ },
+ {
+ name: 'Minokok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqq'
+ },
+ {
+ name: 'Mander',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqr'
+ },
+ {
+ name: 'West Makian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqs'
+ },
+ {
+ name: 'Mok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqt'
+ },
+ {
+ name: 'Mandari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqu'
+ },
+ {
+ name: 'Mosimo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqv'
+ },
+ {
+ name: 'Murupi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqw'
+ },
+ {
+ name: 'Mamuju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqx'
+ },
+ {
+ name: 'Manggarai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqy'
+ },
+ {
+ name: 'Pano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mqz'
+ },
+ {
+ name: 'Mlabri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mra'
+ },
+ {
+ name: 'Marino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrb'
+ },
+ {
+ name: 'Maricopa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrc'
+ },
+ {
+ name: 'Western Magar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrd'
+ },
+ {
+ name: "Martha's Vineyard Sign Language",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mre'
+ },
+ {
+ name: 'Elseng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrf'
+ },
+ {
+ name: 'Mising',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrg'
+ },
+ {
+ name: 'Mara Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrh'
+ },
+ {
+ name: 'Maori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mri',
+ iso6392B: 'mao',
+ iso6392T: 'mri',
+ iso6391: 'mi'
+ },
+ {
+ name: 'Western Mari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrj'
+ },
+ {
+ name: 'Hmwaveke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrk'
+ },
+ {
+ name: 'Mortlockese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrl'
+ },
+ {
+ name: 'Merlav',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrm'
+ },
+ {
+ name: 'Cheke Holo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrn'
+ },
+ {
+ name: 'Mru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mro'
+ },
+ {
+ name: 'Morouas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrp'
+ },
+ {
+ name: 'North Marquesan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrq'
+ },
+ {
+ name: 'Maria (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrr'
+ },
+ {
+ name: 'Maragus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrs'
+ },
+ {
+ name: 'Marghi Central',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrt'
+ },
+ {
+ name: 'Mono (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mru'
+ },
+ {
+ name: 'Mangareva',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrv'
+ },
+ {
+ name: 'Maranao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrw'
+ },
+ {
+ name: 'Maremgi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrx'
+ },
+ {
+ name: 'Mandaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mry'
+ },
+ {
+ name: 'Marind',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mrz'
+ },
+ {
+ name: 'Malay (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'msa',
+ iso6392B: 'may',
+ iso6392T: 'msa',
+ iso6391: 'ms'
+ },
+ {
+ name: 'Masbatenyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msb'
+ },
+ {
+ name: 'Sankaran Maninka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msc'
+ },
+ {
+ name: 'Yucatec Maya Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msd'
+ },
+ {
+ name: 'Musey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mse'
+ },
+ {
+ name: 'Mekwei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msf'
+ },
+ {
+ name: 'Moraid',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msg'
+ },
+ {
+ name: 'Masikoro Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msh'
+ },
+ {
+ name: 'Sabah Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msi'
+ },
+ {
+ name: 'Ma (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msj'
+ },
+ {
+ name: 'Mansaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msk'
+ },
+ {
+ name: 'Molof',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msl'
+ },
+ {
+ name: 'Agusan Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msm'
+ },
+ {
+ name: 'Vurës',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msn'
+ },
+ {
+ name: 'Mombum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mso'
+ },
+ {
+ name: 'Maritsauá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'msp'
+ },
+ {
+ name: 'Caac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msq'
+ },
+ {
+ name: 'Mongolian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msr'
+ },
+ {
+ name: 'West Masela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mss'
+ },
+ {
+ name: 'Musom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msu'
+ },
+ {
+ name: 'Maslam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msv'
+ },
+ {
+ name: 'Mansoanka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msw'
+ },
+ {
+ name: 'Moresada',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msx'
+ },
+ {
+ name: 'Aruamu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msy'
+ },
+ {
+ name: 'Momare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'msz'
+ },
+ {
+ name: 'Cotabato Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mta'
+ },
+ {
+ name: 'Anyin Morofo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtb'
+ },
+ {
+ name: 'Munit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtc'
+ },
+ {
+ name: 'Mualang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtd'
+ },
+ {
+ name: 'Mono (Solomon Islands)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mte'
+ },
+ {
+ name: 'Murik (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtf'
+ },
+ {
+ name: 'Una',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtg'
+ },
+ {
+ name: 'Munggui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mth'
+ },
+ {
+ name: 'Maiwa (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mti'
+ },
+ {
+ name: 'Moskona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtj'
+ },
+ {
+ name: "Mbe'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtk'
+ },
+ {
+ name: 'Montol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtl'
+ },
+ {
+ name: 'Mator',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mtm'
+ },
+ {
+ name: 'Matagalpa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mtn'
+ },
+ {
+ name: 'Totontepec Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mto'
+ },
+ {
+ name: 'Wichí Lhamtés Nocten',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtp'
+ },
+ {
+ name: 'Muong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtq'
+ },
+ {
+ name: 'Mewari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtr'
+ },
+ {
+ name: 'Yora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mts'
+ },
+ {
+ name: 'Mota',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtt'
+ },
+ {
+ name: 'Tututepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtu'
+ },
+ {
+ name: "Asaro'o",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtv'
+ },
+ {
+ name: 'Southern Binukidnon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtw'
+ },
+ {
+ name: 'Tidaá Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mtx'
+ },
+ {
+ name: 'Nabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mty'
+ },
+ {
+ name: 'Mundang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mua'
+ },
+ {
+ name: 'Mubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mub'
+ },
+ {
+ name: 'Ajumbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muc'
+ },
+ {
+ name: 'Mednyj Aleut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mud'
+ },
+ {
+ name: 'Media Lengua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mue'
+ },
+ {
+ name: 'Musgu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mug'
+ },
+ {
+ name: 'Mündü',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muh'
+ },
+ {
+ name: 'Musi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mui'
+ },
+ {
+ name: 'Mabire',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muj'
+ },
+ {
+ name: 'Mugom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muk'
+ },
+ {
+ name: 'Multiple languages',
+ type: 'special',
+ scope: 'special',
+ iso6393: 'mul',
+ iso6392B: 'mul',
+ iso6392T: 'mul'
+ },
+ {
+ name: 'Maiwala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mum'
+ },
+ {
+ name: 'Nyong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muo'
+ },
+ {
+ name: 'Malvi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mup'
+ },
+ {
+ name: 'Eastern Xiangxi Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muq'
+ },
+ {
+ name: 'Murle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mur'
+ },
+ {
+ name: 'Creek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mus',
+ iso6392B: 'mus',
+ iso6392T: 'mus'
+ },
+ {
+ name: 'Western Muria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mut'
+ },
+ {
+ name: 'Yaaku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muu'
+ },
+ {
+ name: 'Muthuvan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muv'
+ },
+ {
+ name: 'Bo-Ung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mux'
+ },
+ {
+ name: 'Muyang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muy'
+ },
+ {
+ name: 'Mursi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'muz'
+ },
+ {
+ name: 'Manam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mva'
+ },
+ {
+ name: 'Mattole',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mvb'
+ },
+ {
+ name: 'Mamboru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvd'
+ },
+ {
+ name: 'Marwari (Pakistan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mve'
+ },
+ {
+ name: 'Peripheral Mongolian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvf'
+ },
+ {
+ name: 'Yucuañe Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvg'
+ },
+ {
+ name: 'Mulgi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvh'
+ },
+ {
+ name: 'Miyako',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvi'
+ },
+ {
+ name: 'Mekmek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvk'
+ },
+ {
+ name: 'Mbara (Australia)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mvl'
+ },
+ {
+ name: 'Muya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvm'
+ },
+ {
+ name: 'Minaveha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvn'
+ },
+ {
+ name: 'Marovo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvo'
+ },
+ {
+ name: 'Duri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvp'
+ },
+ {
+ name: 'Moere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvq'
+ },
+ {
+ name: 'Marau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvr'
+ },
+ {
+ name: 'Massep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvs'
+ },
+ {
+ name: 'Mpotovoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvt'
+ },
+ {
+ name: 'Marfa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvu'
+ },
+ {
+ name: 'Tagal Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvv'
+ },
+ {
+ name: 'Machinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvw'
+ },
+ {
+ name: 'Meoswar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvx'
+ },
+ {
+ name: 'Indus Kohistani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvy'
+ },
+ {
+ name: 'Mesqan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mvz'
+ },
+ {
+ name: 'Mwatebu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwa'
+ },
+ {
+ name: 'Juwal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwb'
+ },
+ {
+ name: 'Are',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwc'
+ },
+ {
+ name: 'Mwera (Chimwera)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwe'
+ },
+ {
+ name: 'Murrinh-Patha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwf'
+ },
+ {
+ name: 'Aiklep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwg'
+ },
+ {
+ name: 'Mouk-Aria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwh'
+ },
+ {
+ name: 'Labo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwi'
+ },
+ {
+ name: 'Kita Maninkakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwk'
+ },
+ {
+ name: 'Mirandese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwl',
+ iso6392B: 'mwl',
+ iso6392T: 'mwl'
+ },
+ {
+ name: 'Sar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwm'
+ },
+ {
+ name: 'Nyamwanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwn'
+ },
+ {
+ name: 'Central Maewo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwo'
+ },
+ {
+ name: 'Kala Lagaw Ya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwp'
+ },
+ {
+ name: 'Mün Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwq'
+ },
+ {
+ name: 'Marwari',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'mwr',
+ iso6392B: 'mwr',
+ iso6392T: 'mwr'
+ },
+ {
+ name: 'Mwimbi-Muthambi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mws'
+ },
+ {
+ name: 'Moken',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwt'
+ },
+ {
+ name: 'Mittu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mwu'
+ },
+ {
+ name: 'Mentawai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwv'
+ },
+ {
+ name: 'Hmong Daw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mww'
+ },
+ {
+ name: 'Moingi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mwz'
+ },
+ {
+ name: 'Northwest Oaxaca Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxa'
+ },
+ {
+ name: 'Tezoatlán Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxb'
+ },
+ {
+ name: 'Manyika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxc'
+ },
+ {
+ name: 'Modang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxd'
+ },
+ {
+ name: 'Mele-Fila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxe'
+ },
+ {
+ name: 'Malgbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxf'
+ },
+ {
+ name: 'Mbangala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxg'
+ },
+ {
+ name: 'Mvuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxh'
+ },
+ {
+ name: 'Mozarabic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'mxi'
+ },
+ {
+ name: 'Miju-Mishmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxj'
+ },
+ {
+ name: 'Monumbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxk'
+ },
+ {
+ name: 'Maxi Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxl'
+ },
+ {
+ name: 'Meramera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxm'
+ },
+ {
+ name: 'Moi (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxn'
+ },
+ {
+ name: 'Mbowe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxo'
+ },
+ {
+ name: 'Tlahuitoltepec Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxp'
+ },
+ {
+ name: 'Juquila Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxq'
+ },
+ {
+ name: 'Murik (Malaysia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxr'
+ },
+ {
+ name: 'Huitepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxs'
+ },
+ {
+ name: 'Jamiltepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxt'
+ },
+ {
+ name: 'Mada (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxu'
+ },
+ {
+ name: 'Metlatónoc Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxv'
+ },
+ {
+ name: 'Namo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxw'
+ },
+ {
+ name: 'Mahou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxx'
+ },
+ {
+ name: 'Southeastern Nochixtlán Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxy'
+ },
+ {
+ name: 'Central Masela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mxz'
+ },
+ {
+ name: 'Burmese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mya',
+ iso6392B: 'bur',
+ iso6392T: 'mya',
+ iso6391: 'my'
+ },
+ {
+ name: 'Mbay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myb'
+ },
+ {
+ name: 'Mayeka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myc'
+ },
+ {
+ name: 'Myene',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mye'
+ },
+ {
+ name: 'Bambassi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myf'
+ },
+ {
+ name: 'Manta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myg'
+ },
+ {
+ name: 'Makah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myh'
+ },
+ {
+ name: 'Mangayat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myj'
+ },
+ {
+ name: 'Mamara Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myk'
+ },
+ {
+ name: 'Moma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myl'
+ },
+ {
+ name: "Me'en",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mym'
+ },
+ {
+ name: 'Anfillo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myo'
+ },
+ {
+ name: 'Pirahã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myp'
+ },
+ {
+ name: 'Muniche',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myr'
+ },
+ {
+ name: 'Mesmes',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mys'
+ },
+ {
+ name: 'Mundurukú',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myu'
+ },
+ {
+ name: 'Erzya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myv',
+ iso6392B: 'myv',
+ iso6392T: 'myv'
+ },
+ {
+ name: 'Muyuw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myw'
+ },
+ {
+ name: 'Masaaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myx'
+ },
+ {
+ name: 'Macuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'myy'
+ },
+ {
+ name: 'Classical Mandaic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'myz'
+ },
+ {
+ name: 'Santa María Zacatepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mza'
+ },
+ {
+ name: 'Tumzabt',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzb'
+ },
+ {
+ name: 'Madagascar Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzc'
+ },
+ {
+ name: 'Malimba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzd'
+ },
+ {
+ name: 'Morawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mze'
+ },
+ {
+ name: 'Monastic Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzg'
+ },
+ {
+ name: 'Wichí Lhamtés Güisnay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzh'
+ },
+ {
+ name: 'Ixcatlán Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzi'
+ },
+ {
+ name: 'Manya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzj'
+ },
+ {
+ name: 'Nigeria Mambila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzk'
+ },
+ {
+ name: 'Mazatlán Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzl'
+ },
+ {
+ name: 'Mumuye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzm'
+ },
+ {
+ name: 'Mazanderani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzn'
+ },
+ {
+ name: 'Matipuhy',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'mzo'
+ },
+ {
+ name: 'Movima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzp'
+ },
+ {
+ name: 'Mori Atas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzq'
+ },
+ {
+ name: 'Marúbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzr'
+ },
+ {
+ name: 'Macanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzs'
+ },
+ {
+ name: 'Mintil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzt'
+ },
+ {
+ name: 'Inapang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzu'
+ },
+ {
+ name: 'Manza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzv'
+ },
+ {
+ name: 'Deg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzw'
+ },
+ {
+ name: 'Mawayana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzx'
+ },
+ {
+ name: 'Mozambican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzy'
+ },
+ {
+ name: 'Maiadomu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'mzz'
+ },
+ {
+ name: 'Namla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naa'
+ },
+ {
+ name: 'Southern Nambikuára',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nab'
+ },
+ {
+ name: 'Narak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nac'
+ },
+ {
+ name: "Naka'ela",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nae'
+ },
+ {
+ name: 'Nabak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naf'
+ },
+ {
+ name: 'Naga Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nag'
+ },
+ {
+ name: 'Nalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naj'
+ },
+ {
+ name: 'Nakanai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nak'
+ },
+ {
+ name: 'Nalik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nal'
+ },
+ {
+ name: "Ngan'gityemerri",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nam'
+ },
+ {
+ name: 'Min Nan Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nan'
+ },
+ {
+ name: 'Naaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nao'
+ },
+ {
+ name: 'Neapolitan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nap',
+ iso6392B: 'nap',
+ iso6392T: 'nap'
+ },
+ {
+ name: 'Khoekhoe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naq'
+ },
+ {
+ name: 'Iguta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nar'
+ },
+ {
+ name: 'Naasioi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nas'
+ },
+ {
+ name: 'Ca̱hungwa̱rya̱',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nat'
+ },
+ {
+ name: 'Nauru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nau',
+ iso6392B: 'nau',
+ iso6392T: 'nau',
+ iso6391: 'na'
+ },
+ {
+ name: 'Navajo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nav',
+ iso6392B: 'nav',
+ iso6392T: 'nav',
+ iso6391: 'nv'
+ },
+ {
+ name: 'Nawuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naw'
+ },
+ {
+ name: 'Nakwi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nax'
+ },
+ {
+ name: 'Ngarrindjeri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nay'
+ },
+ {
+ name: 'Coatepec Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'naz'
+ },
+ {
+ name: 'Nyemba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nba'
+ },
+ {
+ name: 'Ndoe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbb'
+ },
+ {
+ name: 'Chang Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbc'
+ },
+ {
+ name: 'Ngbinda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbd'
+ },
+ {
+ name: 'Konyak Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbe'
+ },
+ {
+ name: 'Nagarchal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbg'
+ },
+ {
+ name: 'Ngamo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbh'
+ },
+ {
+ name: 'Mao Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbi'
+ },
+ {
+ name: 'Ngarinyman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbj'
+ },
+ {
+ name: 'Nake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbk'
+ },
+ {
+ name: 'South Ndebele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbl',
+ iso6392B: 'nbl',
+ iso6392T: 'nbl',
+ iso6391: 'nr'
+ },
+ {
+ name: "Ngbaka Ma'bo",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbm'
+ },
+ {
+ name: 'Kuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbn'
+ },
+ {
+ name: 'Nkukoli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbo'
+ },
+ {
+ name: 'Nnam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbp'
+ },
+ {
+ name: 'Nggem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbq'
+ },
+ {
+ name: 'Numana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbr'
+ },
+ {
+ name: 'Namibian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbs'
+ },
+ {
+ name: 'Na',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbt'
+ },
+ {
+ name: 'Rongmei Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbu'
+ },
+ {
+ name: 'Ngamambo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbv'
+ },
+ {
+ name: 'Southern Ngbandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nbw'
+ },
+ {
+ name: 'Ningera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nby'
+ },
+ {
+ name: 'Iyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nca'
+ },
+ {
+ name: 'Central Nicobarese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncb'
+ },
+ {
+ name: 'Ponam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncc'
+ },
+ {
+ name: 'Nachering',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncd'
+ },
+ {
+ name: 'Yale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nce'
+ },
+ {
+ name: 'Notsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncf'
+ },
+ {
+ name: "Nisga'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncg'
+ },
+ {
+ name: 'Central Huasteca Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nch'
+ },
+ {
+ name: 'Classical Nahuatl',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'nci'
+ },
+ {
+ name: 'Northern Puebla Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncj'
+ },
+ {
+ name: 'Na-kara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nck'
+ },
+ {
+ name: 'Michoacán Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncl'
+ },
+ {
+ name: 'Nambo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncm'
+ },
+ {
+ name: 'Nauna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncn'
+ },
+ {
+ name: 'Sibe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nco'
+ },
+ {
+ name: 'Northern Katang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncq'
+ },
+ {
+ name: 'Ncane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncr'
+ },
+ {
+ name: 'Nicaraguan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncs'
+ },
+ {
+ name: 'Chothe Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nct'
+ },
+ {
+ name: 'Chumburung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncu'
+ },
+ {
+ name: 'Central Puebla Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ncx'
+ },
+ {
+ name: 'Natchez',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ncz'
+ },
+ {
+ name: 'Ndasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nda'
+ },
+ {
+ name: 'Kenswei Nsei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndb'
+ },
+ {
+ name: 'Ndau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndc'
+ },
+ {
+ name: 'Nde-Nsele-Nta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndd'
+ },
+ {
+ name: 'North Ndebele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nde',
+ iso6392B: 'nde',
+ iso6392T: 'nde',
+ iso6391: 'nd'
+ },
+ {
+ name: 'Nadruvian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ndf'
+ },
+ {
+ name: 'Ndengereko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndg'
+ },
+ {
+ name: 'Ndali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndh'
+ },
+ {
+ name: 'Samba Leko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndi'
+ },
+ {
+ name: 'Ndamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndj'
+ },
+ {
+ name: 'Ndaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndk'
+ },
+ {
+ name: 'Ndolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndl'
+ },
+ {
+ name: 'Ndam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndm'
+ },
+ {
+ name: 'Ngundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndn'
+ },
+ {
+ name: 'Ndonga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndo',
+ iso6392B: 'ndo',
+ iso6392T: 'ndo',
+ iso6391: 'ng'
+ },
+ {
+ name: 'Ndo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndp'
+ },
+ {
+ name: 'Ndombe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndq'
+ },
+ {
+ name: 'Ndoola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndr'
+ },
+ {
+ name: 'Low German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nds',
+ iso6392B: 'nds',
+ iso6392T: 'nds'
+ },
+ {
+ name: 'Ndunga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndt'
+ },
+ {
+ name: 'Dugun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndu'
+ },
+ {
+ name: 'Ndut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndv'
+ },
+ {
+ name: 'Ndobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndw'
+ },
+ {
+ name: 'Nduga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndx'
+ },
+ {
+ name: 'Lutos',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndy'
+ },
+ {
+ name: 'Ndogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ndz'
+ },
+ {
+ name: "Eastern Ngad'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nea'
+ },
+ {
+ name: "Toura (Côte d'Ivoire)",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'neb'
+ },
+ {
+ name: 'Nedebang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nec'
+ },
+ {
+ name: 'Nde-Gbite',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ned'
+ },
+ {
+ name: 'Nêlêmwa-Nixumwak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nee'
+ },
+ {
+ name: 'Nefamese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nef'
+ },
+ {
+ name: 'Negidal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'neg'
+ },
+ {
+ name: 'Nyenkha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'neh'
+ },
+ {
+ name: 'Neo-Hittite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'nei'
+ },
+ {
+ name: 'Neko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nej'
+ },
+ {
+ name: 'Neku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nek'
+ },
+ {
+ name: 'Nemi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nem'
+ },
+ {
+ name: 'Nengone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nen'
+ },
+ {
+ name: 'Ná-Meo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'neo'
+ },
+ {
+ name: 'Nepali (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'nep',
+ iso6392B: 'nep',
+ iso6392T: 'nep',
+ iso6391: 'ne'
+ },
+ {
+ name: 'North Central Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'neq'
+ },
+ {
+ name: 'Yahadian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ner'
+ },
+ {
+ name: 'Bhoti Kinnauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nes'
+ },
+ {
+ name: 'Nete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'net'
+ },
+ {
+ name: 'Neo',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'neu'
+ },
+ {
+ name: 'Nyaheun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nev'
+ },
+ {
+ name: 'Newari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'new',
+ iso6392B: 'new',
+ iso6392T: 'new'
+ },
+ {
+ name: 'Neme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nex'
+ },
+ {
+ name: 'Neyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ney'
+ },
+ {
+ name: 'Nez Perce',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nez'
+ },
+ {
+ name: 'Dhao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nfa'
+ },
+ {
+ name: 'Ahwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nfd'
+ },
+ {
+ name: 'Ayiwo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nfl'
+ },
+ {
+ name: 'Nafaanra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nfr'
+ },
+ {
+ name: 'Mfumte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nfu'
+ },
+ {
+ name: 'Ngbaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nga'
+ },
+ {
+ name: 'Northern Ngbandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngb'
+ },
+ {
+ name: 'Ngombe (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngc'
+ },
+ {
+ name: 'Ngando (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngd'
+ },
+ {
+ name: 'Ngemba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nge'
+ },
+ {
+ name: 'Ngbaka Manza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngg'
+ },
+ {
+ name: 'Nǁng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngh'
+ },
+ {
+ name: 'Ngizim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngi'
+ },
+ {
+ name: 'Ngie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngj'
+ },
+ {
+ name: 'Dalabon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngk'
+ },
+ {
+ name: 'Lomwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngl'
+ },
+ {
+ name: "Ngatik Men's Creole",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngm'
+ },
+ {
+ name: 'Ngwo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngn'
+ },
+ {
+ name: 'Ngoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngo'
+ },
+ {
+ name: 'Ngulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngp'
+ },
+ {
+ name: 'Ngurimi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngq'
+ },
+ {
+ name: 'Engdewu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngr'
+ },
+ {
+ name: 'Gvoko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngs'
+ },
+ {
+ name: 'Kriang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngt'
+ },
+ {
+ name: 'Guerrero Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngu'
+ },
+ {
+ name: 'Nagumi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ngv'
+ },
+ {
+ name: 'Ngwaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngw'
+ },
+ {
+ name: 'Nggwahyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngx'
+ },
+ {
+ name: 'Tibea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngy'
+ },
+ {
+ name: 'Ngungwel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ngz'
+ },
+ {
+ name: 'Nhanda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nha'
+ },
+ {
+ name: 'Beng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhb'
+ },
+ {
+ name: 'Tabasco Nahuatl',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nhc'
+ },
+ {
+ name: 'Chiripá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhd'
+ },
+ {
+ name: 'Eastern Huasteca Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhe'
+ },
+ {
+ name: 'Nhuwala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhf'
+ },
+ {
+ name: 'Tetelcingo Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhg'
+ },
+ {
+ name: 'Nahari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhh'
+ },
+ {
+ name: 'Zacatlán-Ahuacatlán-Tepetzintla Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhi'
+ },
+ {
+ name: 'Isthmus-Cosoleacaque Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhk'
+ },
+ {
+ name: 'Morelos Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhm'
+ },
+ {
+ name: 'Central Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhn'
+ },
+ {
+ name: 'Takuu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nho'
+ },
+ {
+ name: 'Isthmus-Pajapan Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhp'
+ },
+ {
+ name: 'Huaxcaleca Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhq'
+ },
+ {
+ name: 'Naro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhr'
+ },
+ {
+ name: 'Ometepec Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nht'
+ },
+ {
+ name: 'Noone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhu'
+ },
+ {
+ name: 'Temascaltepec Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhv'
+ },
+ {
+ name: 'Western Huasteca Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhw'
+ },
+ {
+ name: 'Isthmus-Mecayapan Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhx'
+ },
+ {
+ name: 'Northern Oaxaca Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhy'
+ },
+ {
+ name: 'Santa María La Alta Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nhz'
+ },
+ {
+ name: 'Nias',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nia',
+ iso6392B: 'nia',
+ iso6392T: 'nia'
+ },
+ {
+ name: 'Nakame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nib'
+ },
+ {
+ name: 'Ngandi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nid'
+ },
+ {
+ name: 'Niellim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nie'
+ },
+ {
+ name: 'Nek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nif'
+ },
+ {
+ name: 'Ngalakgan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nig'
+ },
+ {
+ name: 'Nyiha (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nih'
+ },
+ {
+ name: 'Nii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nii'
+ },
+ {
+ name: 'Ngaju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nij'
+ },
+ {
+ name: 'Southern Nicobarese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nik'
+ },
+ {
+ name: 'Nila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nil'
+ },
+ {
+ name: 'Nilamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nim'
+ },
+ {
+ name: 'Ninzo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nin'
+ },
+ {
+ name: 'Nganasan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nio'
+ },
+ {
+ name: 'Nandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niq'
+ },
+ {
+ name: 'Nimboran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nir'
+ },
+ {
+ name: 'Nimi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nis'
+ },
+ {
+ name: 'Southeastern Kolami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nit'
+ },
+ {
+ name: 'Niuean',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niu',
+ iso6392B: 'niu',
+ iso6392T: 'niu'
+ },
+ {
+ name: 'Gilyak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niv'
+ },
+ {
+ name: 'Nimo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niw'
+ },
+ {
+ name: 'Hema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nix'
+ },
+ {
+ name: 'Ngiti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niy'
+ },
+ {
+ name: 'Ningil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'niz'
+ },
+ {
+ name: 'Nzanyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nja'
+ },
+ {
+ name: 'Nocte Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njb'
+ },
+ {
+ name: 'Ndonde Hamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njd'
+ },
+ {
+ name: 'Lotha Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njh'
+ },
+ {
+ name: 'Gudanji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nji'
+ },
+ {
+ name: 'Njen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njj'
+ },
+ {
+ name: 'Njalgulgule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njl'
+ },
+ {
+ name: 'Angami Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njm'
+ },
+ {
+ name: 'Liangmai Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njn'
+ },
+ {
+ name: 'Ao Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njo'
+ },
+ {
+ name: 'Njerep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njr'
+ },
+ {
+ name: 'Nisa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njs'
+ },
+ {
+ name: 'Ndyuka-Trio Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njt'
+ },
+ {
+ name: 'Ngadjunmaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nju'
+ },
+ {
+ name: 'Kunyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njx'
+ },
+ {
+ name: 'Njyem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njy'
+ },
+ {
+ name: 'Nyishi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'njz'
+ },
+ {
+ name: 'Nkoya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nka'
+ },
+ {
+ name: 'Khoibu Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkb'
+ },
+ {
+ name: 'Nkongho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkc'
+ },
+ {
+ name: 'Koireng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkd'
+ },
+ {
+ name: 'Duke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nke'
+ },
+ {
+ name: 'Inpui Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkf'
+ },
+ {
+ name: 'Nekgini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkg'
+ },
+ {
+ name: 'Khezha Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkh'
+ },
+ {
+ name: 'Thangal Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nki'
+ },
+ {
+ name: 'Nakai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkj'
+ },
+ {
+ name: 'Nokuku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkk'
+ },
+ {
+ name: 'Namat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkm'
+ },
+ {
+ name: 'Nkangala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkn'
+ },
+ {
+ name: 'Nkonya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nko'
+ },
+ {
+ name: 'Niuatoputapu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nkp'
+ },
+ {
+ name: 'Nkami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkq'
+ },
+ {
+ name: 'Nukuoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkr'
+ },
+ {
+ name: 'North Asmat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nks'
+ },
+ {
+ name: 'Nyika (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkt'
+ },
+ {
+ name: 'Bouna Kulango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nku'
+ },
+ {
+ name: 'Nyika (Malawi and Zambia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkv'
+ },
+ {
+ name: 'Nkutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkw'
+ },
+ {
+ name: 'Nkoroo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkx'
+ },
+ {
+ name: 'Nkari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nkz'
+ },
+ {
+ name: 'Ngombale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nla'
+ },
+ {
+ name: 'Nalca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlc'
+ },
+ {
+ name: 'Dutch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nld',
+ iso6392B: 'dut',
+ iso6392T: 'nld',
+ iso6391: 'nl'
+ },
+ {
+ name: 'East Nyala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nle'
+ },
+ {
+ name: 'Gela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlg'
+ },
+ {
+ name: 'Grangali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nli'
+ },
+ {
+ name: 'Nyali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlj'
+ },
+ {
+ name: 'Ninia Yali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlk'
+ },
+ {
+ name: 'Nihali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nll'
+ },
+ {
+ name: 'Mankiyali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlm'
+ },
+ {
+ name: 'Ngul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlo'
+ },
+ {
+ name: 'Lao Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlq'
+ },
+ {
+ name: 'Nchumbulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlu'
+ },
+ {
+ name: 'Orizaba Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlv'
+ },
+ {
+ name: 'Walangama',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nlw'
+ },
+ {
+ name: 'Nahali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlx'
+ },
+ {
+ name: 'Nyamal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nly'
+ },
+ {
+ name: 'Nalögo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nlz'
+ },
+ {
+ name: 'Maram Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nma'
+ },
+ {
+ name: 'Big Nambas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmb'
+ },
+ {
+ name: 'Ngam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmc'
+ },
+ {
+ name: 'Ndumu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmd'
+ },
+ {
+ name: 'Mzieme Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nme'
+ },
+ {
+ name: 'Tangkhul Naga (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmf'
+ },
+ {
+ name: 'Kwasio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmg'
+ },
+ {
+ name: 'Monsang Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmh'
+ },
+ {
+ name: 'Nyam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmi'
+ },
+ {
+ name: 'Ngombe (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmj'
+ },
+ {
+ name: 'Namakura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmk'
+ },
+ {
+ name: 'Ndemli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nml'
+ },
+ {
+ name: 'Manangba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmm'
+ },
+ {
+ name: 'ǃXóõ',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmn'
+ },
+ {
+ name: 'Moyon Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmo'
+ },
+ {
+ name: 'Nimanbur',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nmp'
+ },
+ {
+ name: 'Nambya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmq'
+ },
+ {
+ name: 'Nimbari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nmr'
+ },
+ {
+ name: 'Letemboi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nms'
+ },
+ {
+ name: 'Namonuito',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmt'
+ },
+ {
+ name: 'Northeast Maidu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmu'
+ },
+ {
+ name: 'Ngamini',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nmv'
+ },
+ {
+ name: 'Nimoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmw'
+ },
+ {
+ name: 'Nama (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmx'
+ },
+ {
+ name: 'Namuyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmy'
+ },
+ {
+ name: 'Nawdm',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nmz'
+ },
+ {
+ name: 'Nyangumarta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nna'
+ },
+ {
+ name: 'Nande',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnb'
+ },
+ {
+ name: 'Nancere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnc'
+ },
+ {
+ name: 'West Ambae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnd'
+ },
+ {
+ name: 'Ngandyera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nne'
+ },
+ {
+ name: 'Ngaing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnf'
+ },
+ {
+ name: 'Maring Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nng'
+ },
+ {
+ name: 'Ngiemboon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnh'
+ },
+ {
+ name: 'North Nuaulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nni'
+ },
+ {
+ name: 'Nyangatom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnj'
+ },
+ {
+ name: 'Nankina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnk'
+ },
+ {
+ name: 'Northern Rengma Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnl'
+ },
+ {
+ name: 'Namia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnm'
+ },
+ {
+ name: 'Ngete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnn'
+ },
+ {
+ name: 'Norwegian Nynorsk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nno',
+ iso6392B: 'nno',
+ iso6392T: 'nno',
+ iso6391: 'nn'
+ },
+ {
+ name: 'Wancho Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnp'
+ },
+ {
+ name: 'Ngindo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnq'
+ },
+ {
+ name: 'Narungga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nnr'
+ },
+ {
+ name: 'Nanticoke',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nnt'
+ },
+ {
+ name: 'Dwang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnu'
+ },
+ {
+ name: 'Nugunu (Australia)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nnv'
+ },
+ {
+ name: 'Southern Nuni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnw'
+ },
+ {
+ name: 'Nyangga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nny'
+ },
+ {
+ name: "Nda'nda'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nnz'
+ },
+ {
+ name: 'Woun Meu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noa'
+ },
+ {
+ name: 'Norwegian Bokmål',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nob',
+ iso6392B: 'nob',
+ iso6392T: 'nob',
+ iso6391: 'nb'
+ },
+ {
+ name: 'Nuk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noc'
+ },
+ {
+ name: 'Northern Thai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nod'
+ },
+ {
+ name: 'Nimadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noe'
+ },
+ {
+ name: 'Nomane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nof'
+ },
+ {
+ name: 'Nogai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nog',
+ iso6392B: 'nog',
+ iso6392T: 'nog'
+ },
+ {
+ name: 'Nomu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noh'
+ },
+ {
+ name: 'Noiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noi'
+ },
+ {
+ name: 'Nonuya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noj'
+ },
+ {
+ name: 'Nooksack',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nok'
+ },
+ {
+ name: 'Nomlaki',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nol'
+ },
+ {
+ name: 'Nocamán',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nom'
+ },
+ {
+ name: 'Old Norse',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'non',
+ iso6392B: 'non',
+ iso6392T: 'non'
+ },
+ {
+ name: 'Numanggang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nop'
+ },
+ {
+ name: 'Ngongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noq'
+ },
+ {
+ name: 'Norwegian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'nor',
+ iso6392B: 'nor',
+ iso6392T: 'nor',
+ iso6391: 'no'
+ },
+ {
+ name: 'Eastern Nisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nos'
+ },
+ {
+ name: 'Nomatsiguenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'not'
+ },
+ {
+ name: 'Ewage-Notu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nou'
+ },
+ {
+ name: 'Novial',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'nov'
+ },
+ {
+ name: 'Nyambo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'now'
+ },
+ {
+ name: 'Noy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noy'
+ },
+ {
+ name: 'Nayi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'noz'
+ },
+ {
+ name: 'Nar Phu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npa'
+ },
+ {
+ name: 'Nupbikha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npb'
+ },
+ {
+ name: 'Ponyo-Gongwang Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npg'
+ },
+ {
+ name: 'Phom Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nph'
+ },
+ {
+ name: 'Nepali (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npi'
+ },
+ {
+ name: 'Southeastern Puebla Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npl'
+ },
+ {
+ name: 'Mondropolon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npn'
+ },
+ {
+ name: 'Pochuri Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npo'
+ },
+ {
+ name: 'Nipsan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nps'
+ },
+ {
+ name: 'Puimei Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npu'
+ },
+ {
+ name: 'Noipx',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npx'
+ },
+ {
+ name: 'Napu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'npy'
+ },
+ {
+ name: 'Southern Nago',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqg'
+ },
+ {
+ name: 'Kura Ede Nago',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqk'
+ },
+ {
+ name: 'Ngendelengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nql'
+ },
+ {
+ name: 'Ndom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqm'
+ },
+ {
+ name: 'Nen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqn'
+ },
+ {
+ name: "N'Ko",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqo',
+ iso6392B: 'nqo',
+ iso6392T: 'nqo'
+ },
+ {
+ name: 'Kyan-Karyaw Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqq'
+ },
+ {
+ name: 'Akyaung Ari Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nqy'
+ },
+ {
+ name: 'Ngom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nra'
+ },
+ {
+ name: 'Nara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrb'
+ },
+ {
+ name: 'Noric',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'nrc'
+ },
+ {
+ name: 'Southern Rengma Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nre'
+ },
+ {
+ name: 'Jèrriais',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrf'
+ },
+ {
+ name: 'Narango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrg'
+ },
+ {
+ name: 'Chokri Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nri'
+ },
+ {
+ name: 'Ngarla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrk'
+ },
+ {
+ name: 'Ngarluma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrl'
+ },
+ {
+ name: 'Narom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrm'
+ },
+ {
+ name: 'Norn',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nrn'
+ },
+ {
+ name: 'North Picene',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'nrp'
+ },
+ {
+ name: 'Norra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nrr'
+ },
+ {
+ name: 'Northern Kalapuya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nrt'
+ },
+ {
+ name: 'Narua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nru'
+ },
+ {
+ name: 'Ngurmbur',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nrx'
+ },
+ {
+ name: 'Lala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nrz'
+ },
+ {
+ name: 'Sangtam Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsa'
+ },
+ {
+ name: 'Lower Nossob',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nsb'
+ },
+ {
+ name: 'Nshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsc'
+ },
+ {
+ name: 'Southern Nisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsd'
+ },
+ {
+ name: 'Nsenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nse'
+ },
+ {
+ name: 'Northwestern Nisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsf'
+ },
+ {
+ name: 'Ngasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsg'
+ },
+ {
+ name: 'Ngoshie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsh'
+ },
+ {
+ name: 'Nigerian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsi'
+ },
+ {
+ name: 'Naskapi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsk'
+ },
+ {
+ name: 'Norwegian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsl'
+ },
+ {
+ name: 'Sumi Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsm'
+ },
+ {
+ name: 'Nehan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsn'
+ },
+ {
+ name: 'Pedi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nso',
+ iso6392B: 'nso',
+ iso6392T: 'nso'
+ },
+ {
+ name: 'Nepalese Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsp'
+ },
+ {
+ name: 'Northern Sierra Miwok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsq'
+ },
+ {
+ name: 'Maritime Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsr'
+ },
+ {
+ name: 'Nali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nss'
+ },
+ {
+ name: 'Tase Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nst'
+ },
+ {
+ name: 'Sierra Negra Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsu'
+ },
+ {
+ name: 'Southwestern Nisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsv'
+ },
+ {
+ name: 'Navut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsw'
+ },
+ {
+ name: 'Nsongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsx'
+ },
+ {
+ name: 'Nasal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsy'
+ },
+ {
+ name: 'Nisenan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nsz'
+ },
+ {
+ name: 'Northern Tidung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntd'
+ },
+ {
+ name: 'Nathembo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nte'
+ },
+ {
+ name: 'Ngantangarra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ntg'
+ },
+ {
+ name: 'Natioro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nti'
+ },
+ {
+ name: 'Ngaanyatjarra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntj'
+ },
+ {
+ name: 'Ikoma-Nata-Isenye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntk'
+ },
+ {
+ name: 'Nateni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntm'
+ },
+ {
+ name: 'Ntomba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nto'
+ },
+ {
+ name: 'Northern Tepehuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntp'
+ },
+ {
+ name: 'Delo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntr'
+ },
+ {
+ name: 'Natügu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntu'
+ },
+ {
+ name: 'Nottoway',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ntw'
+ },
+ {
+ name: 'Tangkhul Naga (Myanmar)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntx'
+ },
+ {
+ name: 'Mantsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nty'
+ },
+ {
+ name: 'Natanzi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ntz'
+ },
+ {
+ name: 'Yuanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nua'
+ },
+ {
+ name: 'Nukuini',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nuc'
+ },
+ {
+ name: 'Ngala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nud'
+ },
+ {
+ name: 'Ngundu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nue'
+ },
+ {
+ name: 'Nusu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuf'
+ },
+ {
+ name: 'Nungali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nug'
+ },
+ {
+ name: 'Ndunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuh'
+ },
+ {
+ name: 'Ngumbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nui'
+ },
+ {
+ name: 'Nyole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuj'
+ },
+ {
+ name: 'Nuu-chah-nulth',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuk'
+ },
+ {
+ name: 'Nusa Laut',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nul'
+ },
+ {
+ name: "Niuafo'ou",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'num'
+ },
+ {
+ name: 'Anong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nun'
+ },
+ {
+ name: 'Nguôn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuo'
+ },
+ {
+ name: 'Nupe-Nupe-Tako',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nup'
+ },
+ {
+ name: 'Nukumanu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuq'
+ },
+ {
+ name: 'Nukuria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nur'
+ },
+ {
+ name: 'Nuer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nus'
+ },
+ {
+ name: 'Nung (Viet Nam)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nut'
+ },
+ {
+ name: 'Ngbundu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuu'
+ },
+ {
+ name: 'Northern Nuni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuv'
+ },
+ {
+ name: 'Nguluwan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuw'
+ },
+ {
+ name: 'Mehek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nux'
+ },
+ {
+ name: 'Nunggubuyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuy'
+ },
+ {
+ name: 'Tlamacazapa Nahuatl',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nuz'
+ },
+ {
+ name: 'Nasarian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nvh'
+ },
+ {
+ name: 'Namiae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nvm'
+ },
+ {
+ name: 'Nyokon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nvo'
+ },
+ {
+ name: 'Nawathinehena',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nwa'
+ },
+ {
+ name: 'Nyabwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nwb'
+ },
+ {
+ name: 'Classical Newari',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'nwc',
+ iso6392B: 'nwc',
+ iso6392T: 'nwc'
+ },
+ {
+ name: 'Ngwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nwe'
+ },
+ {
+ name: 'Ngayawung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nwg'
+ },
+ {
+ name: 'Southwest Tanna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nwi'
+ },
+ {
+ name: 'Nyamusa-Molo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nwm'
+ },
+ {
+ name: 'Nauo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nwo'
+ },
+ {
+ name: 'Nawaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nwr'
+ },
+ {
+ name: 'Middle Newar',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'nwx'
+ },
+ {
+ name: 'Nottoway-Meherrin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nwy'
+ },
+ {
+ name: 'Nauete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxa'
+ },
+ {
+ name: 'Ngando (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxd'
+ },
+ {
+ name: 'Nage',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxe'
+ },
+ {
+ name: "Ngad'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxg'
+ },
+ {
+ name: 'Nindi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxi'
+ },
+ {
+ name: 'Koki Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxk'
+ },
+ {
+ name: 'South Nuaulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxl'
+ },
+ {
+ name: 'Numidian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'nxm'
+ },
+ {
+ name: 'Ngawun',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nxn'
+ },
+ {
+ name: 'Ndambomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxo'
+ },
+ {
+ name: 'Naxi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxq'
+ },
+ {
+ name: 'Ninggerum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxr'
+ },
+ {
+ name: 'Nafri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nxx'
+ },
+ {
+ name: 'Nyanja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nya',
+ iso6392B: 'nya',
+ iso6392T: 'nya',
+ iso6391: 'ny'
+ },
+ {
+ name: 'Nyangbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyb'
+ },
+ {
+ name: 'Nyanga-li',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyc'
+ },
+ {
+ name: 'Nyore',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyd'
+ },
+ {
+ name: 'Nyengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nye'
+ },
+ {
+ name: 'Giryama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyf'
+ },
+ {
+ name: 'Nyindu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyg'
+ },
+ {
+ name: 'Nyikina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyh'
+ },
+ {
+ name: 'Ama (Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyi'
+ },
+ {
+ name: 'Nyanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyj'
+ },
+ {
+ name: 'Nyaneka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyk'
+ },
+ {
+ name: 'Nyeu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyl'
+ },
+ {
+ name: 'Nyamwezi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nym',
+ iso6392B: 'nym',
+ iso6392T: 'nym'
+ },
+ {
+ name: 'Nyankole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyn',
+ iso6392B: 'nyn',
+ iso6392T: 'nyn'
+ },
+ {
+ name: 'Nyoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyo',
+ iso6392B: 'nyo',
+ iso6392T: 'nyo'
+ },
+ {
+ name: "Nyang'i",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nyp'
+ },
+ {
+ name: 'Nayini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyq'
+ },
+ {
+ name: 'Nyiha (Malawi)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyr'
+ },
+ {
+ name: 'Nyungar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nys'
+ },
+ {
+ name: 'Nyawaygi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nyt'
+ },
+ {
+ name: 'Nyungwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyu'
+ },
+ {
+ name: 'Nyulnyul',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nyv'
+ },
+ {
+ name: 'Nyaw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyw'
+ },
+ {
+ name: 'Nganyaywana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'nyx'
+ },
+ {
+ name: 'Nyakyusa-Ngonde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nyy'
+ },
+ {
+ name: 'Tigon Mbembe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nza'
+ },
+ {
+ name: 'Njebi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzb'
+ },
+ {
+ name: 'Nzadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzd'
+ },
+ {
+ name: 'Nzima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzi',
+ iso6392B: 'nzi',
+ iso6392T: 'nzi'
+ },
+ {
+ name: 'Nzakara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzk'
+ },
+ {
+ name: 'Zeme Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzm'
+ },
+ {
+ name: 'New Zealand Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzs'
+ },
+ {
+ name: 'Teke-Nzikou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzu'
+ },
+ {
+ name: 'Nzakambay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzy'
+ },
+ {
+ name: 'Nanga Dama Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'nzz'
+ },
+ {
+ name: 'Orok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oaa'
+ },
+ {
+ name: 'Oroch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oac'
+ },
+ {
+ name: 'Old Aramaic (up to 700 BCE)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'oar'
+ },
+ {
+ name: 'Old Avar',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'oav'
+ },
+ {
+ name: 'Obispeño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'obi'
+ },
+ {
+ name: 'Southern Bontok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'obk'
+ },
+ {
+ name: 'Oblo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'obl'
+ },
+ {
+ name: 'Moabite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'obm'
+ },
+ {
+ name: 'Obo Manobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'obo'
+ },
+ {
+ name: 'Old Burmese',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'obr'
+ },
+ {
+ name: 'Old Breton',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'obt'
+ },
+ {
+ name: 'Obulom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'obu'
+ },
+ {
+ name: 'Ocaina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oca'
+ },
+ {
+ name: 'Old Chinese',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'och'
+ },
+ {
+ name: 'Occitan (post 1500)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oci',
+ iso6392B: 'oci',
+ iso6392T: 'oci',
+ iso6391: 'oc'
+ },
+ {
+ name: 'Old Cornish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'oco'
+ },
+ {
+ name: 'Atzingo Matlatzinca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ocu'
+ },
+ {
+ name: 'Odut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oda'
+ },
+ {
+ name: 'Od',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'odk'
+ },
+ {
+ name: 'Old Dutch',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'odt'
+ },
+ {
+ name: 'Odual',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'odu'
+ },
+ {
+ name: 'Ofo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ofo'
+ },
+ {
+ name: 'Old Frisian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ofs'
+ },
+ {
+ name: 'Efutop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ofu'
+ },
+ {
+ name: 'Ogbia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ogb'
+ },
+ {
+ name: 'Ogbah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ogc'
+ },
+ {
+ name: 'Old Georgian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'oge'
+ },
+ {
+ name: 'Ogbogolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ogg'
+ },
+ {
+ name: 'Khana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ogo'
+ },
+ {
+ name: 'Ogbronuagum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ogu'
+ },
+ {
+ name: 'Old Hittite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'oht'
+ },
+ {
+ name: 'Old Hungarian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ohu'
+ },
+ {
+ name: 'Oirata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oia'
+ },
+ {
+ name: 'Inebu One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oin'
+ },
+ {
+ name: 'Northwestern Ojibwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojb'
+ },
+ {
+ name: 'Central Ojibwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojc'
+ },
+ {
+ name: 'Eastern Ojibwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojg'
+ },
+ {
+ name: 'Ojibwa',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'oji',
+ iso6392B: 'oji',
+ iso6392T: 'oji',
+ iso6391: 'oj'
+ },
+ {
+ name: 'Old Japanese',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ojp'
+ },
+ {
+ name: 'Severn Ojibwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojs'
+ },
+ {
+ name: 'Ontong Java',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojv'
+ },
+ {
+ name: 'Western Ojibwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ojw'
+ },
+ {
+ name: 'Okanagan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oka'
+ },
+ {
+ name: 'Okobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okb'
+ },
+ {
+ name: 'Okodia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okd'
+ },
+ {
+ name: 'Okpe (Southwestern Edo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oke'
+ },
+ {
+ name: 'Koko Babangk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'okg'
+ },
+ {
+ name: 'Koresh-e Rostam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okh'
+ },
+ {
+ name: 'Okiek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oki'
+ },
+ {
+ name: 'Oko-Juwoi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'okj'
+ },
+ {
+ name: 'Kwamtim One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okk'
+ },
+ {
+ name: 'Old Kentish Sign Language',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'okl'
+ },
+ {
+ name: 'Middle Korean (10th-16th cent.)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'okm'
+ },
+ {
+ name: 'Oki-No-Erabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okn'
+ },
+ {
+ name: 'Old Korean (3rd-9th cent.)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'oko'
+ },
+ {
+ name: 'Kirike',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okr'
+ },
+ {
+ name: 'Oko-Eni-Osayen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oks'
+ },
+ {
+ name: 'Oku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oku'
+ },
+ {
+ name: 'Orokaiva',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okv'
+ },
+ {
+ name: 'Okpe (Northwestern Edo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'okx'
+ },
+ {
+ name: 'Walungge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ola'
+ },
+ {
+ name: 'Mochi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'old'
+ },
+ {
+ name: 'Olekha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ole'
+ },
+ {
+ name: 'Olkol',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'olk'
+ },
+ {
+ name: 'Oloma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'olm'
+ },
+ {
+ name: 'Livvi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'olo'
+ },
+ {
+ name: 'Olrat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'olr'
+ },
+ {
+ name: 'Old Lithuanian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'olt'
+ },
+ {
+ name: 'Kuvale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'olu'
+ },
+ {
+ name: 'Omaha-Ponca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oma'
+ },
+ {
+ name: 'East Ambae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omb'
+ },
+ {
+ name: 'Mochica',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'omc'
+ },
+ {
+ name: 'Omagua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omg'
+ },
+ {
+ name: 'Omi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omi'
+ },
+ {
+ name: 'Omok',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'omk'
+ },
+ {
+ name: 'Ombo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oml'
+ },
+ {
+ name: 'Minoan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'omn'
+ },
+ {
+ name: 'Utarmbung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omo'
+ },
+ {
+ name: 'Old Manipuri',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'omp'
+ },
+ {
+ name: 'Old Marathi',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'omr'
+ },
+ {
+ name: 'Omotik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omt'
+ },
+ {
+ name: 'Omurano',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'omu'
+ },
+ {
+ name: 'South Tairora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'omw'
+ },
+ {
+ name: 'Old Mon',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'omx'
+ },
+ {
+ name: 'Ona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ona'
+ },
+ {
+ name: 'Lingao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onb'
+ },
+ {
+ name: 'Oneida',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'one'
+ },
+ {
+ name: 'Olo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ong'
+ },
+ {
+ name: 'Onin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oni'
+ },
+ {
+ name: 'Onjob',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onj'
+ },
+ {
+ name: 'Kabore One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onk'
+ },
+ {
+ name: 'Onobasulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onn'
+ },
+ {
+ name: 'Onondaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ono'
+ },
+ {
+ name: 'Sartang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onp'
+ },
+ {
+ name: 'Northern One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onr'
+ },
+ {
+ name: 'Ono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ons'
+ },
+ {
+ name: 'Ontenu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ont'
+ },
+ {
+ name: 'Unua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onu'
+ },
+ {
+ name: 'Old Nubian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'onw'
+ },
+ {
+ name: 'Onin Based Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'onx'
+ },
+ {
+ name: "Tohono O'odham",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ood'
+ },
+ {
+ name: 'Ong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oog'
+ },
+ {
+ name: 'Önge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oon'
+ },
+ {
+ name: 'Oorlams',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oor'
+ },
+ {
+ name: 'Old Ossetic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'oos'
+ },
+ {
+ name: 'Okpamheri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'opa'
+ },
+ {
+ name: 'Kopkaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'opk'
+ },
+ {
+ name: 'Oksapmin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'opm'
+ },
+ {
+ name: 'Opao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'opo'
+ },
+ {
+ name: 'Opata',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'opt'
+ },
+ {
+ name: 'Ofayé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'opy'
+ },
+ {
+ name: 'Oroha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ora'
+ },
+ {
+ name: 'Orma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orc'
+ },
+ {
+ name: 'Orejón',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ore'
+ },
+ {
+ name: 'Oring',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'org'
+ },
+ {
+ name: 'Oroqen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orh'
+ },
+ {
+ name: 'Oriya (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'ori',
+ iso6392B: 'ori',
+ iso6392T: 'ori',
+ iso6391: 'or'
+ },
+ {
+ name: 'Oromo',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'orm',
+ iso6392B: 'orm',
+ iso6392T: 'orm',
+ iso6391: 'om'
+ },
+ {
+ name: 'Orang Kanaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orn'
+ },
+ {
+ name: 'Orokolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oro'
+ },
+ {
+ name: 'Oruma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orr'
+ },
+ {
+ name: 'Orang Seletar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ors'
+ },
+ {
+ name: 'Adivasi Oriya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ort'
+ },
+ {
+ name: 'Ormuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oru'
+ },
+ {
+ name: 'Old Russian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'orv'
+ },
+ {
+ name: 'Oro Win',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orw'
+ },
+ {
+ name: 'Oro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orx'
+ },
+ {
+ name: 'Odia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ory'
+ },
+ {
+ name: 'Ormu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'orz'
+ },
+ {
+ name: 'Osage',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'osa',
+ iso6392B: 'osa',
+ iso6392T: 'osa'
+ },
+ {
+ name: 'Oscan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'osc'
+ },
+ {
+ name: 'Osing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'osi'
+ },
+ {
+ name: 'Ososo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oso'
+ },
+ {
+ name: 'Old Spanish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'osp'
+ },
+ {
+ name: 'Ossetian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oss',
+ iso6392B: 'oss',
+ iso6392T: 'oss',
+ iso6391: 'os'
+ },
+ {
+ name: 'Osatu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ost'
+ },
+ {
+ name: 'Southern One',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'osu'
+ },
+ {
+ name: 'Old Saxon',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'osx'
+ },
+ {
+ name: 'Ottoman Turkish (1500-1928)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'ota',
+ iso6392B: 'ota',
+ iso6392T: 'ota'
+ },
+ {
+ name: 'Old Tibetan',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'otb'
+ },
+ {
+ name: 'Ot Danum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otd'
+ },
+ {
+ name: 'Mezquital Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ote'
+ },
+ {
+ name: 'Oti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'oti'
+ },
+ {
+ name: 'Old Turkish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'otk'
+ },
+ {
+ name: 'Tilapa Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otl'
+ },
+ {
+ name: 'Eastern Highland Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otm'
+ },
+ {
+ name: 'Tenango Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otn'
+ },
+ {
+ name: 'Querétaro Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otq'
+ },
+ {
+ name: 'Otoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otr'
+ },
+ {
+ name: 'Estado de México Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ots'
+ },
+ {
+ name: 'Temoaya Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ott'
+ },
+ {
+ name: 'Otuke',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'otu'
+ },
+ {
+ name: 'Ottawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otw'
+ },
+ {
+ name: 'Texcatepec Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otx'
+ },
+ {
+ name: 'Old Tamil',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'oty'
+ },
+ {
+ name: 'Ixtenco Otomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'otz'
+ },
+ {
+ name: 'Tagargrent',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oua'
+ },
+ {
+ name: 'Glio-Oubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oub'
+ },
+ {
+ name: 'Oune',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oue'
+ },
+ {
+ name: 'Old Uighur',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'oui'
+ },
+ {
+ name: 'Ouma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'oum'
+ },
+ {
+ name: 'Elfdalian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ovd'
+ },
+ {
+ name: 'Owiniga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'owi'
+ },
+ {
+ name: 'Old Welsh',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'owl'
+ },
+ {
+ name: 'Oy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oyb'
+ },
+ {
+ name: 'Oyda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oyd'
+ },
+ {
+ name: 'Wayampi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oym'
+ },
+ {
+ name: "Oya'oya",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'oyy'
+ },
+ {
+ name: 'Koonzime',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ozm'
+ },
+ {
+ name: 'Parecís',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pab'
+ },
+ {
+ name: 'Pacoh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pac'
+ },
+ {
+ name: 'Paumarí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pad'
+ },
+ {
+ name: 'Pagibete',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pae'
+ },
+ {
+ name: 'Paranawát',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'paf'
+ },
+ {
+ name: 'Pangasinan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pag',
+ iso6392B: 'pag',
+ iso6392T: 'pag'
+ },
+ {
+ name: 'Tenharim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pah'
+ },
+ {
+ name: 'Pe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pai'
+ },
+ {
+ name: 'Parakanã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pak'
+ },
+ {
+ name: 'Pahlavi',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pal',
+ iso6392B: 'pal',
+ iso6392T: 'pal'
+ },
+ {
+ name: 'Pampanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pam',
+ iso6392B: 'pam',
+ iso6392T: 'pam'
+ },
+ {
+ name: 'Panjabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pan',
+ iso6392B: 'pan',
+ iso6392T: 'pan',
+ iso6391: 'pa'
+ },
+ {
+ name: 'Northern Paiute',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pao'
+ },
+ {
+ name: 'Papiamento',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pap',
+ iso6392B: 'pap',
+ iso6392T: 'pap'
+ },
+ {
+ name: 'Parya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'paq'
+ },
+ {
+ name: 'Panamint',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'par'
+ },
+ {
+ name: 'Papasena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pas'
+ },
+ {
+ name: 'Papitalai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pat'
+ },
+ {
+ name: 'Palauan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pau',
+ iso6392B: 'pau',
+ iso6392T: 'pau'
+ },
+ {
+ name: 'Pakaásnovos',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pav'
+ },
+ {
+ name: 'Pawnee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'paw'
+ },
+ {
+ name: 'Pankararé',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pax'
+ },
+ {
+ name: 'Pech',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pay'
+ },
+ {
+ name: 'Pankararú',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'paz'
+ },
+ {
+ name: 'Páez',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbb'
+ },
+ {
+ name: 'Patamona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbc'
+ },
+ {
+ name: 'Mezontla Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbe'
+ },
+ {
+ name: 'Coyotepec Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbf'
+ },
+ {
+ name: 'Paraujano',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pbg'
+ },
+ {
+ name: "E'ñapa Woromaipu",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbh'
+ },
+ {
+ name: 'Parkwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbi'
+ },
+ {
+ name: 'Mak (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbl'
+ },
+ {
+ name: 'Puebla Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbm'
+ },
+ {
+ name: 'Kpasam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbn'
+ },
+ {
+ name: 'Papel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbo'
+ },
+ {
+ name: 'Badyara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbp'
+ },
+ {
+ name: 'Pangwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbr'
+ },
+ {
+ name: 'Central Pame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbs'
+ },
+ {
+ name: 'Southern Pashto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbt'
+ },
+ {
+ name: 'Northern Pashto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbu'
+ },
+ {
+ name: 'Pnar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pbv'
+ },
+ {
+ name: 'Pyu (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pby'
+ },
+ {
+ name: 'Santa Inés Ahuatempan Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pca'
+ },
+ {
+ name: 'Pear',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcb'
+ },
+ {
+ name: 'Bouyei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcc'
+ },
+ {
+ name: 'Picard',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcd'
+ },
+ {
+ name: 'Ruching Palaung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pce'
+ },
+ {
+ name: 'Paliyan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcf'
+ },
+ {
+ name: 'Paniya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcg'
+ },
+ {
+ name: 'Pardhan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pch'
+ },
+ {
+ name: 'Duruwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pci'
+ },
+ {
+ name: 'Parenga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcj'
+ },
+ {
+ name: 'Paite Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pck'
+ },
+ {
+ name: 'Pardhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcl'
+ },
+ {
+ name: 'Nigerian Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcm'
+ },
+ {
+ name: 'Piti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcn'
+ },
+ {
+ name: 'Pacahuara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcp'
+ },
+ {
+ name: 'Pyapun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pcw'
+ },
+ {
+ name: 'Anam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pda'
+ },
+ {
+ name: 'Pennsylvania German',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdc'
+ },
+ {
+ name: 'Pa Di',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdi'
+ },
+ {
+ name: 'Podena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdn'
+ },
+ {
+ name: 'Padoe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdo'
+ },
+ {
+ name: 'Plautdietsch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdt'
+ },
+ {
+ name: 'Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pdu'
+ },
+ {
+ name: 'Peranakan Indonesian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pea'
+ },
+ {
+ name: 'Eastern Pomo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'peb'
+ },
+ {
+ name: 'Mala (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ped'
+ },
+ {
+ name: 'Taje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pee'
+ },
+ {
+ name: 'Northeastern Pomo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pef'
+ },
+ {
+ name: 'Pengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'peg'
+ },
+ {
+ name: 'Bonan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'peh'
+ },
+ {
+ name: 'Chichimeca-Jonaz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pei'
+ },
+ {
+ name: 'Northern Pomo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pej'
+ },
+ {
+ name: 'Penchal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pek'
+ },
+ {
+ name: 'Pekal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pel'
+ },
+ {
+ name: 'Phende',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pem'
+ },
+ {
+ name: 'Old Persian (ca. 600-400 B.C.)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'peo',
+ iso6392B: 'peo',
+ iso6392T: 'peo'
+ },
+ {
+ name: 'Kunja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pep'
+ },
+ {
+ name: 'Southern Pomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'peq'
+ },
+ {
+ name: 'Iranian Persian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pes'
+ },
+ {
+ name: 'Pémono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pev'
+ },
+ {
+ name: 'Petats',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pex'
+ },
+ {
+ name: 'Petjo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pey'
+ },
+ {
+ name: 'Eastern Penan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pez'
+ },
+ {
+ name: 'Pááfang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pfa'
+ },
+ {
+ name: 'Pere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pfe'
+ },
+ {
+ name: 'Pfaelzisch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pfl'
+ },
+ {
+ name: 'Sudanese Creole Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pga'
+ },
+ {
+ name: 'Gāndhārī',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'pgd'
+ },
+ {
+ name: 'Pangwali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgg'
+ },
+ {
+ name: 'Pagi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgi'
+ },
+ {
+ name: 'Rerep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgk'
+ },
+ {
+ name: 'Primitive Irish',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pgl'
+ },
+ {
+ name: 'Paelignian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pgn'
+ },
+ {
+ name: 'Pangseng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgs'
+ },
+ {
+ name: 'Pagu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgu'
+ },
+ {
+ name: 'Papua New Guinean Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pgz'
+ },
+ {
+ name: 'Pa-Hng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pha'
+ },
+ {
+ name: 'Phudagi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phd'
+ },
+ {
+ name: 'Phuong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phg'
+ },
+ {
+ name: 'Phukha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phh'
+ },
+ {
+ name: 'Phake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phk'
+ },
+ {
+ name: 'Phalura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phl'
+ },
+ {
+ name: 'Phimbi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phm'
+ },
+ {
+ name: 'Phoenician',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'phn',
+ iso6392B: 'phn',
+ iso6392T: 'phn'
+ },
+ {
+ name: 'Phunoi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pho'
+ },
+ {
+ name: "Phana'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phq'
+ },
+ {
+ name: 'Pahari-Potwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phr'
+ },
+ {
+ name: 'Phu Thai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pht'
+ },
+ {
+ name: 'Phuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phu'
+ },
+ {
+ name: 'Pahlavani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phv'
+ },
+ {
+ name: 'Phangduwali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'phw'
+ },
+ {
+ name: 'Pima Bajo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pia'
+ },
+ {
+ name: 'Yine',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pib'
+ },
+ {
+ name: 'Pinji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pic'
+ },
+ {
+ name: 'Piaroa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pid'
+ },
+ {
+ name: 'Piro',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pie'
+ },
+ {
+ name: 'Pingelapese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pif'
+ },
+ {
+ name: 'Pisabo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pig'
+ },
+ {
+ name: 'Pitcairn-Norfolk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pih'
+ },
+ {
+ name: 'Pini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pii'
+ },
+ {
+ name: 'Pijao',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pij'
+ },
+ {
+ name: 'Yom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pil'
+ },
+ {
+ name: 'Powhatan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pim'
+ },
+ {
+ name: 'Piame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pin'
+ },
+ {
+ name: 'Piapoco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pio'
+ },
+ {
+ name: 'Pero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pip'
+ },
+ {
+ name: 'Piratapuyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pir'
+ },
+ {
+ name: 'Pijin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pis'
+ },
+ {
+ name: 'Pitta Pitta',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pit'
+ },
+ {
+ name: 'Pintupi-Luritja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'piu'
+ },
+ {
+ name: 'Pileni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'piv'
+ },
+ {
+ name: 'Pimbwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'piw'
+ },
+ {
+ name: 'Piu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pix'
+ },
+ {
+ name: 'Piya-Kwonci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'piy'
+ },
+ {
+ name: 'Pije',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'piz'
+ },
+ {
+ name: 'Pitjantjatjara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pjt'
+ },
+ {
+ name: 'Ardhamāgadhī Prākrit',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'pka'
+ },
+ {
+ name: 'Pokomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkb'
+ },
+ {
+ name: 'Paekche',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pkc'
+ },
+ {
+ name: 'Pak-Tong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkg'
+ },
+ {
+ name: 'Pankhu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkh'
+ },
+ {
+ name: 'Pakanha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkn'
+ },
+ {
+ name: 'Pökoot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pko'
+ },
+ {
+ name: 'Pukapuka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkp'
+ },
+ {
+ name: 'Attapady Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkr'
+ },
+ {
+ name: 'Pakistan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pks'
+ },
+ {
+ name: 'Maleng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pkt'
+ },
+ {
+ name: 'Paku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pku'
+ },
+ {
+ name: 'Miani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pla'
+ },
+ {
+ name: 'Polonombauk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plb'
+ },
+ {
+ name: 'Central Palawano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plc'
+ },
+ {
+ name: 'Polari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pld'
+ },
+ {
+ name: "Palu'e",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ple'
+ },
+ {
+ name: 'Pilagá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plg'
+ },
+ {
+ name: 'Paulohi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plh'
+ },
+ {
+ name: 'Pali',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pli',
+ iso6392B: 'pli',
+ iso6392T: 'pli',
+ iso6391: 'pi'
+ },
+ {
+ name: 'Polci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plj'
+ },
+ {
+ name: 'Kohistani Shina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plk'
+ },
+ {
+ name: 'Shwe Palaung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pll'
+ },
+ {
+ name: 'Palenquero',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pln'
+ },
+ {
+ name: 'Oluta Popoluca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plo'
+ },
+ {
+ name: 'Palaic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'plq'
+ },
+ {
+ name: 'Palaka Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plr'
+ },
+ {
+ name: 'San Marcos Tlacoyalco Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pls'
+ },
+ {
+ name: 'Plateau Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plt'
+ },
+ {
+ name: 'Palikúr',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plu'
+ },
+ {
+ name: 'Southwest Palawano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plv'
+ },
+ {
+ name: "Brooke's Point Palawano",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plw'
+ },
+ {
+ name: 'Bolyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ply'
+ },
+ {
+ name: 'Paluan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'plz'
+ },
+ {
+ name: 'Paama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pma'
+ },
+ {
+ name: 'Pambia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmb'
+ },
+ {
+ name: 'Pallanganmiddang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pmd'
+ },
+ {
+ name: 'Pwaamei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pme'
+ },
+ {
+ name: 'Pamona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmf'
+ },
+ {
+ name: 'Māhārāṣṭri Prākrit',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'pmh'
+ },
+ {
+ name: 'Northern Pumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmi'
+ },
+ {
+ name: 'Southern Pumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmj'
+ },
+ {
+ name: 'Pamlico',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pmk'
+ },
+ {
+ name: 'Lingua Franca',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pml'
+ },
+ {
+ name: 'Pomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmm'
+ },
+ {
+ name: 'Pam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmn'
+ },
+ {
+ name: 'Pom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmo'
+ },
+ {
+ name: 'Northern Pame',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmq'
+ },
+ {
+ name: 'Paynamar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmr'
+ },
+ {
+ name: 'Piemontese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pms'
+ },
+ {
+ name: 'Tuamotuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmt'
+ },
+ {
+ name: 'Plains Miwok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmw'
+ },
+ {
+ name: 'Poumei Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmx'
+ },
+ {
+ name: 'Papuan Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pmy'
+ },
+ {
+ name: 'Southern Pame',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pmz'
+ },
+ {
+ name: 'Punan Bah-Biau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pna'
+ },
+ {
+ name: 'Western Panjabi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnb'
+ },
+ {
+ name: 'Pannei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnc'
+ },
+ {
+ name: 'Mpinda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnd'
+ },
+ {
+ name: 'Western Penan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pne'
+ },
+ {
+ name: 'Pongu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'png'
+ },
+ {
+ name: 'Penrhyn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnh'
+ },
+ {
+ name: 'Aoheng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pni'
+ },
+ {
+ name: 'Pinjarup',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pnj'
+ },
+ {
+ name: 'Paunaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnk'
+ },
+ {
+ name: 'Paleni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnl'
+ },
+ {
+ name: 'Punan Batu 1',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnm'
+ },
+ {
+ name: 'Pinai-Hagahai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnn'
+ },
+ {
+ name: 'Panobo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pno'
+ },
+ {
+ name: 'Pancana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnp'
+ },
+ {
+ name: 'Pana (Burkina Faso)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnq'
+ },
+ {
+ name: 'Panim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnr'
+ },
+ {
+ name: 'Ponosakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pns'
+ },
+ {
+ name: 'Pontic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnt'
+ },
+ {
+ name: 'Jiongnai Bunu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnu'
+ },
+ {
+ name: 'Pinigura',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnv'
+ },
+ {
+ name: 'Banyjima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnw'
+ },
+ {
+ name: 'Phong-Kniang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnx'
+ },
+ {
+ name: 'Pinyin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pny'
+ },
+ {
+ name: 'Pana (Central African Republic)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pnz'
+ },
+ {
+ name: 'Poqomam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poc'
+ },
+ {
+ name: 'San Juan Atzingo Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poe'
+ },
+ {
+ name: 'Poke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pof'
+ },
+ {
+ name: 'Potiguára',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pog'
+ },
+ {
+ name: "Poqomchi'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poh'
+ },
+ {
+ name: 'Highland Popoluca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poi'
+ },
+ {
+ name: 'Pokangá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pok'
+ },
+ {
+ name: 'Polish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pol',
+ iso6392B: 'pol',
+ iso6392T: 'pol',
+ iso6391: 'pl'
+ },
+ {
+ name: 'Southeastern Pomo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pom'
+ },
+ {
+ name: 'Pohnpeian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pon',
+ iso6392B: 'pon',
+ iso6392T: 'pon'
+ },
+ {
+ name: 'Central Pomo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'poo'
+ },
+ {
+ name: 'Pwapwâ',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pop'
+ },
+ {
+ name: 'Texistepec Popoluca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poq'
+ },
+ {
+ name: 'Portuguese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'por',
+ iso6392B: 'por',
+ iso6392T: 'por',
+ iso6391: 'pt'
+ },
+ {
+ name: 'Sayula Popoluca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pos'
+ },
+ {
+ name: 'Potawatomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pot'
+ },
+ {
+ name: 'Upper Guinea Crioulo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pov'
+ },
+ {
+ name: 'San Felipe Otlaltepec Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pow'
+ },
+ {
+ name: 'Polabian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pox'
+ },
+ {
+ name: 'Pogolo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'poy'
+ },
+ {
+ name: 'Papi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppe'
+ },
+ {
+ name: 'Paipai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppi'
+ },
+ {
+ name: 'Uma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppk'
+ },
+ {
+ name: 'Pipil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppl'
+ },
+ {
+ name: 'Papuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppm'
+ },
+ {
+ name: 'Papapana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppn'
+ },
+ {
+ name: 'Folopa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppo'
+ },
+ {
+ name: 'Pelende',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppp'
+ },
+ {
+ name: 'Pei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppq'
+ },
+ {
+ name: 'San Luís Temalacayuca Popoloca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pps'
+ },
+ {
+ name: 'Pare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ppt'
+ },
+ {
+ name: 'Papora',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ppu'
+ },
+ {
+ name: "Pa'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pqa'
+ },
+ {
+ name: 'Malecite-Passamaquoddy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pqm'
+ },
+ {
+ name: 'Parachi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prc'
+ },
+ {
+ name: 'Parsi-Dari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prd'
+ },
+ {
+ name: 'Principense',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pre'
+ },
+ {
+ name: 'Paranan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prf'
+ },
+ {
+ name: 'Prussian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prg'
+ },
+ {
+ name: 'Porohanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prh'
+ },
+ {
+ name: 'Paicî',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pri'
+ },
+ {
+ name: 'Parauk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prk'
+ },
+ {
+ name: 'Peruvian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prl'
+ },
+ {
+ name: 'Kibiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prm'
+ },
+ {
+ name: 'Prasuni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prn'
+ },
+ {
+ name: 'Old Provençal (to 1500)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'pro',
+ iso6392B: 'pro',
+ iso6392T: 'pro'
+ },
+ {
+ name: 'Parsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prp'
+ },
+ {
+ name: 'Ashéninka Perené',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prq'
+ },
+ {
+ name: 'Puri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'prr'
+ },
+ {
+ name: 'Dari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prs'
+ },
+ {
+ name: 'Phai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prt'
+ },
+ {
+ name: 'Puragi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pru'
+ },
+ {
+ name: 'Parawen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prw'
+ },
+ {
+ name: 'Purik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prx'
+ },
+ {
+ name: 'Providencia Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'prz'
+ },
+ {
+ name: 'Asue Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psa'
+ },
+ {
+ name: 'Persian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psc'
+ },
+ {
+ name: 'Plains Indian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psd'
+ },
+ {
+ name: 'Central Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pse'
+ },
+ {
+ name: 'Penang Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psg'
+ },
+ {
+ name: 'Southwest Pashai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psh'
+ },
+ {
+ name: 'Southeast Pashai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psi'
+ },
+ {
+ name: 'Puerto Rican Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psl'
+ },
+ {
+ name: 'Pauserna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'psm'
+ },
+ {
+ name: 'Panasuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psn'
+ },
+ {
+ name: 'Polish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pso'
+ },
+ {
+ name: 'Philippine Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psp'
+ },
+ {
+ name: 'Pasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psq'
+ },
+ {
+ name: 'Portuguese Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psr'
+ },
+ {
+ name: 'Kaulong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pss'
+ },
+ {
+ name: 'Central Pashto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pst'
+ },
+ {
+ name: 'Sauraseni Prākrit',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'psu'
+ },
+ {
+ name: 'Port Sandwich',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'psw'
+ },
+ {
+ name: 'Piscataway',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'psy'
+ },
+ {
+ name: 'Pai Tavytera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pta'
+ },
+ {
+ name: 'Pataxó Hã-Ha-Hãe',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pth'
+ },
+ {
+ name: 'Pindiini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pti'
+ },
+ {
+ name: 'Patani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptn'
+ },
+ {
+ name: "Zo'é",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pto'
+ },
+ {
+ name: 'Patep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptp'
+ },
+ {
+ name: 'Pattapu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptq'
+ },
+ {
+ name: 'Piamatsina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptr'
+ },
+ {
+ name: 'Enrekang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptt'
+ },
+ {
+ name: 'Bambam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptu'
+ },
+ {
+ name: 'Port Vato',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ptv'
+ },
+ {
+ name: 'Pentlatch',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ptw'
+ },
+ {
+ name: 'Pathiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pty'
+ },
+ {
+ name: 'Western Highland Purepecha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pua'
+ },
+ {
+ name: 'Purum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pub'
+ },
+ {
+ name: 'Punan Merap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puc'
+ },
+ {
+ name: 'Punan Aput',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pud'
+ },
+ {
+ name: 'Puelche',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pue'
+ },
+ {
+ name: 'Punan Merah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puf'
+ },
+ {
+ name: 'Phuie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pug'
+ },
+ {
+ name: 'Puinave',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pui'
+ },
+ {
+ name: 'Punan Tubu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puj'
+ },
+ {
+ name: 'Puma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pum'
+ },
+ {
+ name: 'Puoc',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puo'
+ },
+ {
+ name: 'Pulabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pup'
+ },
+ {
+ name: 'Puquina',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'puq'
+ },
+ {
+ name: 'Puruborá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pur'
+ },
+ {
+ name: 'Pushto',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'pus',
+ iso6392B: 'pus',
+ iso6392T: 'pus',
+ iso6391: 'ps'
+ },
+ {
+ name: 'Putoh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'put'
+ },
+ {
+ name: 'Punu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puu'
+ },
+ {
+ name: 'Puluwatese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'puw'
+ },
+ {
+ name: 'Puare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pux'
+ },
+ {
+ name: 'Purisimeño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'puy'
+ },
+ {
+ name: 'Pawaia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwa'
+ },
+ {
+ name: 'Panawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwb'
+ },
+ {
+ name: 'Gapapaiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwg'
+ },
+ {
+ name: 'Patwin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'pwi'
+ },
+ {
+ name: 'Molbog',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwm'
+ },
+ {
+ name: 'Paiwan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwn'
+ },
+ {
+ name: 'Pwo Western Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwo'
+ },
+ {
+ name: 'Powari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pwr'
+ },
+ {
+ name: 'Pwo Northern Karen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pww'
+ },
+ {
+ name: 'Quetzaltepec Mixe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pxm'
+ },
+ {
+ name: 'Pye Krumen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pye'
+ },
+ {
+ name: 'Fyam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pym'
+ },
+ {
+ name: 'Poyanáwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pyn'
+ },
+ {
+ name: 'Paraguayan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pys'
+ },
+ {
+ name: 'Puyuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pyu'
+ },
+ {
+ name: 'Pyu (Myanmar)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'pyx'
+ },
+ {
+ name: 'Pyen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pyy'
+ },
+ {
+ name: 'Para Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'pzn'
+ },
+ {
+ name: 'Quapaw',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qua'
+ },
+ {
+ name: 'Huallaga Huánuco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qub'
+ },
+ {
+ name: "K'iche'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quc'
+ },
+ {
+ name: 'Calderón Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qud'
+ },
+ {
+ name: 'Quechua',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'que',
+ iso6392B: 'que',
+ iso6392T: 'que',
+ iso6391: 'qu'
+ },
+ {
+ name: 'Lambayeque Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quf'
+ },
+ {
+ name: 'Chimborazo Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qug'
+ },
+ {
+ name: 'South Bolivian Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quh'
+ },
+ {
+ name: 'Quileute',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qui'
+ },
+ {
+ name: 'Chachapoyas Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quk'
+ },
+ {
+ name: 'North Bolivian Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qul'
+ },
+ {
+ name: 'Sipacapense',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qum'
+ },
+ {
+ name: 'Quinault',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'qun'
+ },
+ {
+ name: 'Southern Pastaza Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qup'
+ },
+ {
+ name: 'Quinqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quq'
+ },
+ {
+ name: 'Yanahuanca Pasco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qur'
+ },
+ {
+ name: 'Santiago del Estero Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qus'
+ },
+ {
+ name: 'Sacapulteco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quv'
+ },
+ {
+ name: 'Tena Lowland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quw'
+ },
+ {
+ name: 'Yauyos Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qux'
+ },
+ {
+ name: 'Ayacucho Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quy'
+ },
+ {
+ name: 'Cusco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'quz'
+ },
+ {
+ name: 'Ambo-Pasco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qva'
+ },
+ {
+ name: 'Cajamarca Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvc'
+ },
+ {
+ name: 'Eastern Apurímac Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qve'
+ },
+ {
+ name: 'Huamalíes-Dos de Mayo Huánuco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvh'
+ },
+ {
+ name: 'Imbabura Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvi'
+ },
+ {
+ name: 'Loja Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvj'
+ },
+ {
+ name: 'Cajatambo North Lima Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvl'
+ },
+ {
+ name: 'Margos-Yarowilca-Lauricocha Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvm'
+ },
+ {
+ name: 'North Junín Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvn'
+ },
+ {
+ name: 'Napo Lowland Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvo'
+ },
+ {
+ name: 'Pacaraos Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvp'
+ },
+ {
+ name: 'San Martín Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvs'
+ },
+ {
+ name: 'Huaylla Wanca Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvw'
+ },
+ {
+ name: 'Queyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvy'
+ },
+ {
+ name: 'Northern Pastaza Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qvz'
+ },
+ {
+ name: 'Corongo Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qwa'
+ },
+ {
+ name: 'Classical Quechua',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'qwc'
+ },
+ {
+ name: 'Huaylas Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qwh'
+ },
+ {
+ name: 'Kuman (Russia)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'qwm'
+ },
+ {
+ name: 'Sihuas Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qws'
+ },
+ {
+ name: 'Kwalhioqua-Tlatskanai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'qwt'
+ },
+ {
+ name: 'Chiquián Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxa'
+ },
+ {
+ name: 'Chincha Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxc'
+ },
+ {
+ name: 'Panao Huánuco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxh'
+ },
+ {
+ name: 'Salasaca Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxl'
+ },
+ {
+ name: 'Northern Conchucos Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxn'
+ },
+ {
+ name: 'Southern Conchucos Ancash Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxo'
+ },
+ {
+ name: 'Puno Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxp'
+ },
+ {
+ name: "Qashqa'i",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxq'
+ },
+ {
+ name: 'Cañar Highland Quichua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxr'
+ },
+ {
+ name: 'Southern Qiang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxs'
+ },
+ {
+ name: 'Santa Ana de Tusi Pasco Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxt'
+ },
+ {
+ name: 'Arequipa-La Unión Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxu'
+ },
+ {
+ name: 'Jauja Wanca Quechua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'qxw'
+ },
+ {
+ name: 'Quenya',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'qya'
+ },
+ {
+ name: 'Quiripi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'qyp'
+ },
+ {
+ name: 'Dungmali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'raa'
+ },
+ {
+ name: 'Camling',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rab'
+ },
+ {
+ name: 'Rasawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rac'
+ },
+ {
+ name: 'Rade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rad'
+ },
+ {
+ name: 'Western Meohang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'raf'
+ },
+ {
+ name: 'Logooli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rag'
+ },
+ {
+ name: 'Rabha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rah'
+ },
+ {
+ name: 'Ramoaaina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rai'
+ },
+ {
+ name: 'Rajasthani',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'raj',
+ iso6392B: 'raj',
+ iso6392T: 'raj'
+ },
+ {
+ name: 'Tulu-Bohuai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rak'
+ },
+ {
+ name: 'Ralte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ral'
+ },
+ {
+ name: 'Canela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ram'
+ },
+ {
+ name: 'Riantana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ran'
+ },
+ {
+ name: 'Rao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rao'
+ },
+ {
+ name: 'Rapanui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rap',
+ iso6392B: 'rap',
+ iso6392T: 'rap'
+ },
+ {
+ name: 'Saam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'raq'
+ },
+ {
+ name: 'Rarotongan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rar',
+ iso6392B: 'rar',
+ iso6392T: 'rar'
+ },
+ {
+ name: 'Tegali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ras'
+ },
+ {
+ name: 'Razajerdi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rat'
+ },
+ {
+ name: 'Raute',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rau'
+ },
+ {
+ name: 'Sampang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rav'
+ },
+ {
+ name: 'Rawang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'raw'
+ },
+ {
+ name: 'Rang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rax'
+ },
+ {
+ name: 'Rapa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ray'
+ },
+ {
+ name: 'Rahambuu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'raz'
+ },
+ {
+ name: 'Rumai Palaung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rbb'
+ },
+ {
+ name: 'Northern Bontok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rbk'
+ },
+ {
+ name: 'Miraya Bikol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rbl'
+ },
+ {
+ name: 'Barababaraba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rbp'
+ },
+ {
+ name: 'Réunion Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rcf'
+ },
+ {
+ name: 'Rudbari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rdb'
+ },
+ {
+ name: 'Rerau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rea'
+ },
+ {
+ name: 'Rembong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'reb'
+ },
+ {
+ name: 'Rejang Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ree'
+ },
+ {
+ name: 'Kara (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'reg'
+ },
+ {
+ name: 'Reli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rei'
+ },
+ {
+ name: 'Rejang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rej'
+ },
+ {
+ name: 'Rendille',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rel'
+ },
+ {
+ name: 'Remo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rem'
+ },
+ {
+ name: 'Rengao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ren'
+ },
+ {
+ name: 'Rer Bare',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rer'
+ },
+ {
+ name: 'Reshe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'res'
+ },
+ {
+ name: 'Retta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ret'
+ },
+ {
+ name: 'Reyesano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rey'
+ },
+ {
+ name: 'Roria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rga'
+ },
+ {
+ name: 'Romano-Greek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rge'
+ },
+ {
+ name: 'Rangkas',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rgk'
+ },
+ {
+ name: 'Romagnol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rgn'
+ },
+ {
+ name: 'Resígaro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rgr'
+ },
+ {
+ name: 'Southern Roglai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rgs'
+ },
+ {
+ name: 'Ringgou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rgu'
+ },
+ {
+ name: 'Rohingya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rhg'
+ },
+ {
+ name: 'Yahang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rhp'
+ },
+ {
+ name: 'Riang (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ria'
+ },
+ {
+ name: 'Tarifit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rif'
+ },
+ {
+ name: 'Riang Lang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ril'
+ },
+ {
+ name: 'Nyaturu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rim'
+ },
+ {
+ name: 'Nungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rin'
+ },
+ {
+ name: 'Ribun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rir'
+ },
+ {
+ name: 'Ritharrngu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rit'
+ },
+ {
+ name: 'Riung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'riu'
+ },
+ {
+ name: 'Rajong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rjg'
+ },
+ {
+ name: 'Raji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rji'
+ },
+ {
+ name: 'Rajbanshi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rjs'
+ },
+ {
+ name: 'Kraol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rka'
+ },
+ {
+ name: 'Rikbaktsa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rkb'
+ },
+ {
+ name: 'Rakahanga-Manihiki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rkh'
+ },
+ {
+ name: 'Rakhine',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rki'
+ },
+ {
+ name: 'Marka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rkm'
+ },
+ {
+ name: 'Rangpuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rkt'
+ },
+ {
+ name: 'Arakwal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rkw'
+ },
+ {
+ name: 'Rama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rma'
+ },
+ {
+ name: 'Rembarrnga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmb'
+ },
+ {
+ name: 'Carpathian Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmc'
+ },
+ {
+ name: 'Traveller Danish',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rmd'
+ },
+ {
+ name: 'Angloromani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rme'
+ },
+ {
+ name: 'Kalo Finnish Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmf'
+ },
+ {
+ name: 'Traveller Norwegian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmg'
+ },
+ {
+ name: 'Murkim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmh'
+ },
+ {
+ name: 'Lomavren',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmi'
+ },
+ {
+ name: 'Romkun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmk'
+ },
+ {
+ name: 'Baltic Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rml'
+ },
+ {
+ name: 'Roma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmm'
+ },
+ {
+ name: 'Balkan Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmn'
+ },
+ {
+ name: 'Sinte Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmo'
+ },
+ {
+ name: 'Rempi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmp'
+ },
+ {
+ name: 'Caló',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmq'
+ },
+ {
+ name: 'Romanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rms'
+ },
+ {
+ name: 'Domari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmt'
+ },
+ {
+ name: 'Tavringer Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmu'
+ },
+ {
+ name: 'Romanova',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'rmv'
+ },
+ {
+ name: 'Welsh Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmw'
+ },
+ {
+ name: 'Romam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmx'
+ },
+ {
+ name: 'Vlax Romani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmy'
+ },
+ {
+ name: 'Marma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rmz'
+ },
+ {
+ name: 'Ruund',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rnd'
+ },
+ {
+ name: 'Ronga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rng'
+ },
+ {
+ name: 'Ranglong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rnl'
+ },
+ {
+ name: 'Roon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rnn'
+ },
+ {
+ name: 'Rongpo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rnp'
+ },
+ {
+ name: 'Nari Nari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rnr'
+ },
+ {
+ name: 'Rungwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rnw'
+ },
+ {
+ name: "Tae'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rob'
+ },
+ {
+ name: 'Cacgia Roglai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'roc'
+ },
+ {
+ name: 'Rogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rod'
+ },
+ {
+ name: 'Ronji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'roe'
+ },
+ {
+ name: 'Rombo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rof'
+ },
+ {
+ name: 'Northern Roglai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rog'
+ },
+ {
+ name: 'Romansh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'roh',
+ iso6392B: 'roh',
+ iso6392T: 'roh',
+ iso6391: 'rm'
+ },
+ {
+ name: 'Romblomanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rol'
+ },
+ {
+ name: 'Romany',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'rom',
+ iso6392B: 'rom',
+ iso6392T: 'rom'
+ },
+ {
+ name: 'Romanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ron',
+ iso6392B: 'rum',
+ iso6392T: 'ron',
+ iso6391: 'ro'
+ },
+ {
+ name: 'Rotokas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'roo'
+ },
+ {
+ name: 'Kriol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rop'
+ },
+ {
+ name: 'Rongga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ror'
+ },
+ {
+ name: 'Runga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rou'
+ },
+ {
+ name: 'Dela-Oenale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'row'
+ },
+ {
+ name: 'Repanbitip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rpn'
+ },
+ {
+ name: 'Rapting',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rpt'
+ },
+ {
+ name: 'Ririo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rri'
+ },
+ {
+ name: 'Waima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rro'
+ },
+ {
+ name: 'Arritinngithigh',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rrt'
+ },
+ {
+ name: 'Romano-Serbian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rsb'
+ },
+ {
+ name: 'Russian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rsl'
+ },
+ {
+ name: 'Miriwoong Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rsm'
+ },
+ {
+ name: 'Rungtu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rtc'
+ },
+ {
+ name: 'Ratahan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rth'
+ },
+ {
+ name: 'Rotuman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rtm'
+ },
+ {
+ name: 'Yurats',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rts'
+ },
+ {
+ name: 'Rathawi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rtw'
+ },
+ {
+ name: 'Gungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rub'
+ },
+ {
+ name: 'Ruuli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruc'
+ },
+ {
+ name: 'Rusyn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rue'
+ },
+ {
+ name: 'Luguru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruf'
+ },
+ {
+ name: 'Roviana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rug'
+ },
+ {
+ name: 'Ruga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruh'
+ },
+ {
+ name: 'Rufiji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rui'
+ },
+ {
+ name: 'Che',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruk'
+ },
+ {
+ name: 'Rundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'run',
+ iso6392B: 'run',
+ iso6392T: 'run',
+ iso6391: 'rn'
+ },
+ {
+ name: 'Istro Romanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruo'
+ },
+ {
+ name: 'Macedo-Romanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rup',
+ iso6392B: 'rup',
+ iso6392T: 'rup'
+ },
+ {
+ name: 'Megleno Romanian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruq'
+ },
+ {
+ name: 'Russian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rus',
+ iso6392B: 'rus',
+ iso6392T: 'rus',
+ iso6391: 'ru'
+ },
+ {
+ name: 'Rutul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rut'
+ },
+ {
+ name: 'Lanas Lobu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruu'
+ },
+ {
+ name: 'Mala (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruy'
+ },
+ {
+ name: 'Ruma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ruz'
+ },
+ {
+ name: 'Rawo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rwa'
+ },
+ {
+ name: 'Rwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rwk'
+ },
+ {
+ name: 'Amba (Uganda)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rwm'
+ },
+ {
+ name: 'Rawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rwo'
+ },
+ {
+ name: 'Marwari (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rwr'
+ },
+ {
+ name: 'Ngardi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rxd'
+ },
+ {
+ name: 'Karuwali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'rxw'
+ },
+ {
+ name: 'Northern Amami-Oshima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ryn'
+ },
+ {
+ name: 'Yaeyama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rys'
+ },
+ {
+ name: 'Central Okinawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ryu'
+ },
+ {
+ name: 'Rāziḥī',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'rzh'
+ },
+ {
+ name: 'Saba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saa'
+ },
+ {
+ name: 'Buglere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sab'
+ },
+ {
+ name: 'Meskwaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sac'
+ },
+ {
+ name: 'Sandawe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sad',
+ iso6392B: 'sad',
+ iso6392T: 'sad'
+ },
+ {
+ name: 'Sabanê',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sae'
+ },
+ {
+ name: 'Safaliba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saf'
+ },
+ {
+ name: 'Sango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sag',
+ iso6392B: 'sag',
+ iso6392T: 'sag',
+ iso6391: 'sg'
+ },
+ {
+ name: 'Yakut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sah',
+ iso6392B: 'sah',
+ iso6392T: 'sah'
+ },
+ {
+ name: 'Sahu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saj'
+ },
+ {
+ name: 'Sake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sak'
+ },
+ {
+ name: 'Samaritan Aramaic',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sam',
+ iso6392B: 'sam',
+ iso6392T: 'sam'
+ },
+ {
+ name: 'Sanskrit',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'san',
+ iso6392B: 'san',
+ iso6392T: 'san',
+ iso6391: 'sa'
+ },
+ {
+ name: 'Sause',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sao'
+ },
+ {
+ name: 'Samburu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saq'
+ },
+ {
+ name: 'Saraveca',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sar'
+ },
+ {
+ name: 'Sasak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sas',
+ iso6392B: 'sas',
+ iso6392T: 'sas'
+ },
+ {
+ name: 'Santali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sat',
+ iso6392B: 'sat',
+ iso6392T: 'sat'
+ },
+ {
+ name: 'Saleman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sau'
+ },
+ {
+ name: 'Saafi-Saafi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sav'
+ },
+ {
+ name: 'Sawi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saw'
+ },
+ {
+ name: 'Sa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sax'
+ },
+ {
+ name: 'Saya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'say'
+ },
+ {
+ name: 'Saurashtra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'saz'
+ },
+ {
+ name: 'Ngambay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sba'
+ },
+ {
+ name: 'Simbo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbb'
+ },
+ {
+ name: 'Kele (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbc'
+ },
+ {
+ name: 'Southern Samo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbd'
+ },
+ {
+ name: 'Saliba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbe'
+ },
+ {
+ name: 'Chabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbf'
+ },
+ {
+ name: 'Seget',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbg'
+ },
+ {
+ name: 'Sori-Harengan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbh'
+ },
+ {
+ name: 'Seti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbi'
+ },
+ {
+ name: 'Surbakhal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbj'
+ },
+ {
+ name: 'Safwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbk'
+ },
+ {
+ name: 'Botolan Sambal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbl'
+ },
+ {
+ name: 'Sagala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbm'
+ },
+ {
+ name: 'Sindhi Bhil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbn'
+ },
+ {
+ name: 'Sabüm',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbo'
+ },
+ {
+ name: 'Sangu (Tanzania)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbp'
+ },
+ {
+ name: 'Sileibi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbq'
+ },
+ {
+ name: 'Sembakung Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbr'
+ },
+ {
+ name: 'Subiya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbs'
+ },
+ {
+ name: 'Kimki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbt'
+ },
+ {
+ name: 'Stod Bhoti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbu'
+ },
+ {
+ name: 'Sabine',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'sbv'
+ },
+ {
+ name: 'Simba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbw'
+ },
+ {
+ name: 'Seberuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbx'
+ },
+ {
+ name: 'Soli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sby'
+ },
+ {
+ name: 'Sara Kaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sbz'
+ },
+ {
+ name: 'Chut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scb'
+ },
+ {
+ name: 'Dongxiang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sce'
+ },
+ {
+ name: 'San Miguel Creole French',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scf'
+ },
+ {
+ name: 'Sanggau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scg'
+ },
+ {
+ name: 'Sakachep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sch'
+ },
+ {
+ name: 'Sri Lankan Creole Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sci'
+ },
+ {
+ name: 'Sadri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sck'
+ },
+ {
+ name: 'Shina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scl'
+ },
+ {
+ name: 'Sicilian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scn',
+ iso6392B: 'scn',
+ iso6392T: 'scn'
+ },
+ {
+ name: 'Scots',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sco',
+ iso6392B: 'sco',
+ iso6392T: 'sco'
+ },
+ {
+ name: 'Hyolmo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scp'
+ },
+ {
+ name: "Sa'och",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scq'
+ },
+ {
+ name: 'North Slavey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scs'
+ },
+ {
+ name: 'Southern Katang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sct'
+ },
+ {
+ name: 'Shumcho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scu'
+ },
+ {
+ name: 'Sheni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scv'
+ },
+ {
+ name: 'Sha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'scw'
+ },
+ {
+ name: 'Sicel',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'scx'
+ },
+ {
+ name: "Toraja-Sa'dan",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sda'
+ },
+ {
+ name: 'Shabak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdb'
+ },
+ {
+ name: 'Sassarese Sardinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdc'
+ },
+ {
+ name: 'Surubu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sde'
+ },
+ {
+ name: 'Sarli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdf'
+ },
+ {
+ name: 'Savi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdg'
+ },
+ {
+ name: 'Southern Kurdish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdh'
+ },
+ {
+ name: 'Suundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdj'
+ },
+ {
+ name: 'Sos Kundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdk'
+ },
+ {
+ name: 'Saudi Arabian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdl'
+ },
+ {
+ name: 'Gallurese Sardinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdn'
+ },
+ {
+ name: 'Bukar-Sadung Bidayuh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdo'
+ },
+ {
+ name: 'Sherdukpen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdp'
+ },
+ {
+ name: 'Semandang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdq'
+ },
+ {
+ name: 'Oraon Sadri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdr'
+ },
+ {
+ name: 'Sened',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sds'
+ },
+ {
+ name: 'Shuadit',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sdt'
+ },
+ {
+ name: 'Sarudu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdu'
+ },
+ {
+ name: 'Sibu Melanau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdx'
+ },
+ {
+ name: 'Sallands',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sdz'
+ },
+ {
+ name: 'Semai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sea'
+ },
+ {
+ name: 'Shempire Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seb'
+ },
+ {
+ name: 'Sechelt',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sec'
+ },
+ {
+ name: 'Sedang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sed'
+ },
+ {
+ name: 'Seneca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'see'
+ },
+ {
+ name: 'Cebaara Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sef'
+ },
+ {
+ name: 'Segeju',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seg'
+ },
+ {
+ name: 'Sena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seh'
+ },
+ {
+ name: 'Seri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sei'
+ },
+ {
+ name: 'Sene',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sej'
+ },
+ {
+ name: 'Sekani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sek'
+ },
+ {
+ name: 'Selkup',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sel',
+ iso6392B: 'sel',
+ iso6392T: 'sel'
+ },
+ {
+ name: 'Nanerigé Sénoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sen'
+ },
+ {
+ name: 'Suarmin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seo'
+ },
+ {
+ name: 'Sìcìté Sénoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sep'
+ },
+ {
+ name: 'Senara Sénoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seq'
+ },
+ {
+ name: 'Serrano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ser'
+ },
+ {
+ name: 'Koyraboro Senni Songhai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ses'
+ },
+ {
+ name: 'Sentani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'set'
+ },
+ {
+ name: 'Serui-Laut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'seu'
+ },
+ {
+ name: 'Nyarafolo Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sev'
+ },
+ {
+ name: 'Sewa Bay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sew'
+ },
+ {
+ name: 'Secoya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sey'
+ },
+ {
+ name: 'Senthang Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sez'
+ },
+ {
+ name: 'Langue des signes de Belgique Francophone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sfb'
+ },
+ {
+ name: 'Eastern Subanen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sfe'
+ },
+ {
+ name: 'Small Flowery Miao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sfm'
+ },
+ {
+ name: 'South African Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sfs'
+ },
+ {
+ name: 'Sehwi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sfw'
+ },
+ {
+ name: 'Old Irish (to 900)',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'sga',
+ iso6392B: 'sga',
+ iso6392T: 'sga'
+ },
+ {
+ name: 'Mag-antsi Ayta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgb'
+ },
+ {
+ name: 'Kipsigis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgc'
+ },
+ {
+ name: 'Surigaonon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgd'
+ },
+ {
+ name: 'Segai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sge'
+ },
+ {
+ name: 'Swiss-German Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgg'
+ },
+ {
+ name: 'Shughni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgh'
+ },
+ {
+ name: 'Suga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgi'
+ },
+ {
+ name: 'Surgujia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgj'
+ },
+ {
+ name: 'Sangkong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgk'
+ },
+ {
+ name: 'Singa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sgm'
+ },
+ {
+ name: 'Singpho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgp'
+ },
+ {
+ name: 'Sangisari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgr'
+ },
+ {
+ name: 'Samogitian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgs'
+ },
+ {
+ name: 'Brokpake',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgt'
+ },
+ {
+ name: 'Salas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgu'
+ },
+ {
+ name: 'Sebat Bet Gurage',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgw'
+ },
+ {
+ name: 'Sierra Leone Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgx'
+ },
+ {
+ name: 'Sanglechi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgy'
+ },
+ {
+ name: 'Sursurunga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sgz'
+ },
+ {
+ name: 'Shall-Zwall',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sha'
+ },
+ {
+ name: 'Ninam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shb'
+ },
+ {
+ name: 'Sonde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shc'
+ },
+ {
+ name: 'Kundal Shahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shd'
+ },
+ {
+ name: 'Sheko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'she'
+ },
+ {
+ name: 'Shua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shg'
+ },
+ {
+ name: 'Shoshoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shh'
+ },
+ {
+ name: 'Tachelhit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shi'
+ },
+ {
+ name: 'Shatt',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shj'
+ },
+ {
+ name: 'Shilluk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shk'
+ },
+ {
+ name: 'Shendu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shl'
+ },
+ {
+ name: 'Shahrudi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shm'
+ },
+ {
+ name: 'Shan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shn',
+ iso6392B: 'shn',
+ iso6392T: 'shn'
+ },
+ {
+ name: 'Shanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sho'
+ },
+ {
+ name: 'Shipibo-Conibo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shp'
+ },
+ {
+ name: 'Sala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shq'
+ },
+ {
+ name: 'Shi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shr'
+ },
+ {
+ name: 'Shuswap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shs'
+ },
+ {
+ name: 'Shasta',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sht'
+ },
+ {
+ name: 'Chadian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shu'
+ },
+ {
+ name: 'Shehri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shv'
+ },
+ {
+ name: 'Shwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shw'
+ },
+ {
+ name: 'She',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shx'
+ },
+ {
+ name: 'Tachawit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shy'
+ },
+ {
+ name: 'Syenara Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'shz'
+ },
+ {
+ name: 'Akkala Sami',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sia'
+ },
+ {
+ name: 'Sebop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sib'
+ },
+ {
+ name: 'Sidamo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sid',
+ iso6392B: 'sid',
+ iso6392T: 'sid'
+ },
+ {
+ name: 'Simaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sie'
+ },
+ {
+ name: 'Siamou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sif'
+ },
+ {
+ name: 'Paasaal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sig'
+ },
+ {
+ name: 'Zire',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sih'
+ },
+ {
+ name: 'Shom Peng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sii'
+ },
+ {
+ name: 'Numbami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sij'
+ },
+ {
+ name: 'Sikiana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sik'
+ },
+ {
+ name: 'Tumulung Sisaala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sil'
+ },
+ {
+ name: 'Mende (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sim'
+ },
+ {
+ name: 'Sinhala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sin',
+ iso6392B: 'sin',
+ iso6392T: 'sin',
+ iso6391: 'si'
+ },
+ {
+ name: 'Sikkimese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sip'
+ },
+ {
+ name: 'Sonia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siq'
+ },
+ {
+ name: 'Siri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sir'
+ },
+ {
+ name: 'Siuslaw',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sis'
+ },
+ {
+ name: 'Sinagen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siu'
+ },
+ {
+ name: 'Sumariup',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siv'
+ },
+ {
+ name: 'Siwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siw'
+ },
+ {
+ name: 'Sumau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'six'
+ },
+ {
+ name: 'Sivandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siy'
+ },
+ {
+ name: 'Siwi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'siz'
+ },
+ {
+ name: 'Epena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sja'
+ },
+ {
+ name: 'Sajau Basap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjb'
+ },
+ {
+ name: 'Kildin Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjd'
+ },
+ {
+ name: 'Pite Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sje'
+ },
+ {
+ name: 'Assangori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjg'
+ },
+ {
+ name: 'Kemi Sami',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sjk'
+ },
+ {
+ name: 'Sajalong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjl'
+ },
+ {
+ name: 'Mapun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjm'
+ },
+ {
+ name: 'Sindarin',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'sjn'
+ },
+ {
+ name: 'Xibe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjo'
+ },
+ {
+ name: 'Surjapuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjp'
+ },
+ {
+ name: 'Siar-Lak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjr'
+ },
+ {
+ name: 'Senhaja De Srair',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sjs'
+ },
+ {
+ name: 'Ter Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjt'
+ },
+ {
+ name: 'Ume Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sju'
+ },
+ {
+ name: 'Shawnee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sjw'
+ },
+ {
+ name: 'Skagit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ska'
+ },
+ {
+ name: 'Saek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skb'
+ },
+ {
+ name: 'Ma Manda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skc'
+ },
+ {
+ name: 'Southern Sierra Miwok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skd'
+ },
+ {
+ name: 'Seke (Vanuatu)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ske'
+ },
+ {
+ name: 'Sakirabiá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skf'
+ },
+ {
+ name: 'Sakalava Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skg'
+ },
+ {
+ name: 'Sikule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skh'
+ },
+ {
+ name: 'Sika',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ski'
+ },
+ {
+ name: 'Seke (Nepal)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skj'
+ },
+ {
+ name: 'Kutong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skm'
+ },
+ {
+ name: 'Kolibugan Subanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skn'
+ },
+ {
+ name: 'Seko Tengah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sko'
+ },
+ {
+ name: 'Sekapan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skp'
+ },
+ {
+ name: 'Sininkere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skq'
+ },
+ {
+ name: 'Saraiki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skr'
+ },
+ {
+ name: 'Maia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sks'
+ },
+ {
+ name: 'Sakata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skt'
+ },
+ {
+ name: 'Sakao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sku'
+ },
+ {
+ name: 'Skou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skv'
+ },
+ {
+ name: 'Skepi Creole Dutch',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'skw'
+ },
+ {
+ name: 'Seko Padang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skx'
+ },
+ {
+ name: 'Sikaiana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sky'
+ },
+ {
+ name: 'Sekar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'skz'
+ },
+ {
+ name: 'Sáliba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slc'
+ },
+ {
+ name: 'Sissala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sld'
+ },
+ {
+ name: 'Sholaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sle'
+ },
+ {
+ name: 'Swiss-Italian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slf'
+ },
+ {
+ name: 'Selungai Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slg'
+ },
+ {
+ name: 'Southern Puget Sound Salish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slh'
+ },
+ {
+ name: 'Lower Silesian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sli'
+ },
+ {
+ name: 'Salumá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slj'
+ },
+ {
+ name: 'Slovak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slk',
+ iso6392B: 'slo',
+ iso6392T: 'slk',
+ iso6391: 'sk'
+ },
+ {
+ name: 'Salt-Yui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sll'
+ },
+ {
+ name: 'Pangutaran Sama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slm'
+ },
+ {
+ name: 'Salinan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sln'
+ },
+ {
+ name: 'Lamaholot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slp'
+ },
+ {
+ name: 'Salchuq',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'slq'
+ },
+ {
+ name: 'Salar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slr'
+ },
+ {
+ name: 'Singapore Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sls'
+ },
+ {
+ name: 'Sila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slt'
+ },
+ {
+ name: 'Selaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slu'
+ },
+ {
+ name: 'Slovenian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slv',
+ iso6392B: 'slv',
+ iso6392T: 'slv',
+ iso6391: 'sl'
+ },
+ {
+ name: 'Sialum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slw'
+ },
+ {
+ name: 'Salampasu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slx'
+ },
+ {
+ name: 'Selayar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sly'
+ },
+ {
+ name: "Ma'ya",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'slz'
+ },
+ {
+ name: 'Southern Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sma',
+ iso6392B: 'sma',
+ iso6392T: 'sma'
+ },
+ {
+ name: 'Simbari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smb'
+ },
+ {
+ name: 'Som',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'smc'
+ },
+ {
+ name: 'Sama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smd'
+ },
+ {
+ name: 'Northern Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sme',
+ iso6392B: 'sme',
+ iso6392T: 'sme',
+ iso6391: 'se'
+ },
+ {
+ name: 'Auwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smf'
+ },
+ {
+ name: 'Simbali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smg'
+ },
+ {
+ name: 'Samei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smh'
+ },
+ {
+ name: 'Lule Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smj',
+ iso6392B: 'smj',
+ iso6392T: 'smj'
+ },
+ {
+ name: 'Bolinao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smk'
+ },
+ {
+ name: 'Central Sama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sml'
+ },
+ {
+ name: 'Musasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smm'
+ },
+ {
+ name: 'Inari Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smn',
+ iso6392B: 'smn',
+ iso6392T: 'smn'
+ },
+ {
+ name: 'Samoan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smo',
+ iso6392B: 'smo',
+ iso6392T: 'smo',
+ iso6391: 'sm'
+ },
+ {
+ name: 'Samaritan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'smp'
+ },
+ {
+ name: 'Samo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smq'
+ },
+ {
+ name: 'Simeulue',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smr'
+ },
+ {
+ name: 'Skolt Sami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sms',
+ iso6392B: 'sms',
+ iso6392T: 'sms'
+ },
+ {
+ name: 'Simte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smt'
+ },
+ {
+ name: 'Somray',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'smu'
+ },
+ {
+ name: 'Samvedi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smv'
+ },
+ {
+ name: 'Sumbawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smw'
+ },
+ {
+ name: 'Samba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smx'
+ },
+ {
+ name: 'Semnani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smy'
+ },
+ {
+ name: 'Simeku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'smz'
+ },
+ {
+ name: 'Shona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sna',
+ iso6392B: 'sna',
+ iso6392T: 'sna',
+ iso6391: 'sn'
+ },
+ {
+ name: 'Sebuyau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snb'
+ },
+ {
+ name: 'Sinaugoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snc'
+ },
+ {
+ name: 'Sindhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snd',
+ iso6392B: 'snd',
+ iso6392T: 'snd',
+ iso6391: 'sd'
+ },
+ {
+ name: 'Bau Bidayuh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sne'
+ },
+ {
+ name: 'Noon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snf'
+ },
+ {
+ name: 'Sanga (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sng'
+ },
+ {
+ name: 'Sensi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sni'
+ },
+ {
+ name: 'Riverain Sango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snj'
+ },
+ {
+ name: 'Soninke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snk',
+ iso6392B: 'snk',
+ iso6392T: 'snk'
+ },
+ {
+ name: 'Sangil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snl'
+ },
+ {
+ name: "Southern Ma'di",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snm'
+ },
+ {
+ name: 'Siona',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snn'
+ },
+ {
+ name: 'Snohomish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sno'
+ },
+ {
+ name: 'Siane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snp'
+ },
+ {
+ name: 'Sangu (Gabon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snq'
+ },
+ {
+ name: 'Sihan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snr'
+ },
+ {
+ name: 'South West Bay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sns'
+ },
+ {
+ name: 'Senggi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snu'
+ },
+ {
+ name: "Sa'ban",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snv'
+ },
+ {
+ name: 'Selee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snw'
+ },
+ {
+ name: 'Sam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snx'
+ },
+ {
+ name: 'Saniyo-Hiyewe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sny'
+ },
+ {
+ name: 'Kou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'snz'
+ },
+ {
+ name: 'Thai Song',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soa'
+ },
+ {
+ name: 'Sobei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sob'
+ },
+ {
+ name: 'So (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soc'
+ },
+ {
+ name: 'Songoora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sod'
+ },
+ {
+ name: 'Songomeno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soe'
+ },
+ {
+ name: 'Sogdian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'sog',
+ iso6392B: 'sog',
+ iso6392T: 'sog'
+ },
+ {
+ name: 'Aka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soh'
+ },
+ {
+ name: 'Sonha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soi'
+ },
+ {
+ name: 'Soi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soj'
+ },
+ {
+ name: 'Sokoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sok'
+ },
+ {
+ name: 'Solos',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sol'
+ },
+ {
+ name: 'Somali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'som',
+ iso6392B: 'som',
+ iso6392T: 'som',
+ iso6391: 'so'
+ },
+ {
+ name: 'Songo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soo'
+ },
+ {
+ name: 'Songe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sop'
+ },
+ {
+ name: 'Kanasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soq'
+ },
+ {
+ name: 'Somrai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sor'
+ },
+ {
+ name: 'Seeku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sos'
+ },
+ {
+ name: 'Southern Sotho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sot',
+ iso6392B: 'sot',
+ iso6392T: 'sot',
+ iso6391: 'st'
+ },
+ {
+ name: 'Southern Thai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sou'
+ },
+ {
+ name: 'Sonsorol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sov'
+ },
+ {
+ name: 'Sowanda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sow'
+ },
+ {
+ name: 'Swo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sox'
+ },
+ {
+ name: 'Miyobe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soy'
+ },
+ {
+ name: 'Temi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'soz'
+ },
+ {
+ name: 'Spanish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spa',
+ iso6392B: 'spa',
+ iso6392T: 'spa',
+ iso6391: 'es'
+ },
+ {
+ name: 'Sepa (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spb'
+ },
+ {
+ name: 'Sapé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spc'
+ },
+ {
+ name: 'Saep',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spd'
+ },
+ {
+ name: 'Sepa (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spe'
+ },
+ {
+ name: 'Sian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spg'
+ },
+ {
+ name: 'Saponi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spi'
+ },
+ {
+ name: 'Sengo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spk'
+ },
+ {
+ name: 'Selepet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spl'
+ },
+ {
+ name: 'Akukem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spm'
+ },
+ {
+ name: 'Sanapaná',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spn'
+ },
+ {
+ name: 'Spokane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spo'
+ },
+ {
+ name: 'Supyire Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spp'
+ },
+ {
+ name: 'Loreto-Ucayali Spanish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spq'
+ },
+ {
+ name: 'Saparua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spr'
+ },
+ {
+ name: 'Saposa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sps'
+ },
+ {
+ name: 'Spiti Bhoti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spt'
+ },
+ {
+ name: 'Sapuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spu'
+ },
+ {
+ name: 'Sambalpuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spv'
+ },
+ {
+ name: 'South Picene',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'spx'
+ },
+ {
+ name: 'Sabaot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'spy'
+ },
+ {
+ name: 'Shama-Sambuga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqa'
+ },
+ {
+ name: 'Shau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqh'
+ },
+ {
+ name: 'Albanian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'sqi',
+ iso6392B: 'alb',
+ iso6392T: 'sqi',
+ iso6391: 'sq'
+ },
+ {
+ name: 'Albanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqk'
+ },
+ {
+ name: 'Suma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqm'
+ },
+ {
+ name: 'Susquehannock',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sqn'
+ },
+ {
+ name: 'Sorkhei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqo'
+ },
+ {
+ name: 'Sou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqq'
+ },
+ {
+ name: 'Siculo Arabic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'sqr'
+ },
+ {
+ name: 'Sri Lankan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqs'
+ },
+ {
+ name: 'Soqotri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sqt'
+ },
+ {
+ name: 'Squamish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'squ'
+ },
+ {
+ name: 'Saruga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sra'
+ },
+ {
+ name: 'Sora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srb'
+ },
+ {
+ name: 'Logudorese Sardinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'src'
+ },
+ {
+ name: 'Sardinian',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'srd',
+ iso6392B: 'srd',
+ iso6392T: 'srd',
+ iso6391: 'sc'
+ },
+ {
+ name: 'Sara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sre'
+ },
+ {
+ name: 'Nafi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srf'
+ },
+ {
+ name: 'Sulod',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srg'
+ },
+ {
+ name: 'Sarikoli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srh'
+ },
+ {
+ name: 'Siriano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sri'
+ },
+ {
+ name: 'Serudung Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srk'
+ },
+ {
+ name: 'Isirawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srl'
+ },
+ {
+ name: 'Saramaccan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srm'
+ },
+ {
+ name: 'Sranan Tongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srn',
+ iso6392B: 'srn',
+ iso6392T: 'srn'
+ },
+ {
+ name: 'Campidanese Sardinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sro'
+ },
+ {
+ name: 'Serbian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srp',
+ iso6392B: 'srp',
+ iso6392T: 'srp',
+ iso6391: 'sr'
+ },
+ {
+ name: 'Sirionó',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srq'
+ },
+ {
+ name: 'Serer',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srr',
+ iso6392B: 'srr',
+ iso6392T: 'srr'
+ },
+ {
+ name: 'Sarsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srs'
+ },
+ {
+ name: 'Sauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srt'
+ },
+ {
+ name: 'Suruí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sru'
+ },
+ {
+ name: 'Southern Sorsoganon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srv'
+ },
+ {
+ name: 'Serua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srw'
+ },
+ {
+ name: 'Sirmauri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srx'
+ },
+ {
+ name: 'Sera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sry'
+ },
+ {
+ name: 'Shahmirzadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'srz'
+ },
+ {
+ name: 'Southern Sama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssb'
+ },
+ {
+ name: 'Suba-Simbiti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssc'
+ },
+ {
+ name: 'Siroi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssd'
+ },
+ {
+ name: 'Balangingi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sse'
+ },
+ {
+ name: 'Thao',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ssf'
+ },
+ {
+ name: 'Seimat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssg'
+ },
+ {
+ name: 'Shihhi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssh'
+ },
+ {
+ name: 'Sansi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssi'
+ },
+ {
+ name: 'Sausi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssj'
+ },
+ {
+ name: 'Sunam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssk'
+ },
+ {
+ name: 'Western Sisaala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssl'
+ },
+ {
+ name: 'Semnam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssm'
+ },
+ {
+ name: 'Waata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssn'
+ },
+ {
+ name: 'Sissano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sso'
+ },
+ {
+ name: 'Spanish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssp'
+ },
+ {
+ name: "So'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssq'
+ },
+ {
+ name: 'Swiss-French Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssr'
+ },
+ {
+ name: 'Sô',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sss'
+ },
+ {
+ name: 'Sinasina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sst'
+ },
+ {
+ name: 'Susuami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssu'
+ },
+ {
+ name: 'Shark Bay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssv'
+ },
+ {
+ name: 'Swati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssw',
+ iso6392B: 'ssw',
+ iso6392T: 'ssw',
+ iso6391: 'ss'
+ },
+ {
+ name: 'Samberigi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssx'
+ },
+ {
+ name: 'Saho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssy'
+ },
+ {
+ name: 'Sengseng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ssz'
+ },
+ {
+ name: 'Settla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sta'
+ },
+ {
+ name: 'Northern Subanen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stb'
+ },
+ {
+ name: 'Sentinel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'std'
+ },
+ {
+ name: 'Liana-Seti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ste'
+ },
+ {
+ name: 'Seta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stf'
+ },
+ {
+ name: 'Trieng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stg'
+ },
+ {
+ name: 'Shelta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sth'
+ },
+ {
+ name: 'Bulo Stieng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sti'
+ },
+ {
+ name: 'Matya Samo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stj'
+ },
+ {
+ name: 'Arammba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stk'
+ },
+ {
+ name: 'Stellingwerfs',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stl'
+ },
+ {
+ name: 'Setaman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stm'
+ },
+ {
+ name: 'Owa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stn'
+ },
+ {
+ name: 'Stoney',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sto'
+ },
+ {
+ name: 'Southeastern Tepehuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stp'
+ },
+ {
+ name: 'Saterfriesisch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stq'
+ },
+ {
+ name: 'Straits Salish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'str'
+ },
+ {
+ name: 'Shumashti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sts'
+ },
+ {
+ name: 'Budeh Stieng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stt'
+ },
+ {
+ name: 'Samtao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stu'
+ },
+ {
+ name: "Silt'e",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stv'
+ },
+ {
+ name: 'Satawalese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'stw'
+ },
+ {
+ name: 'Siberian Tatar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sty'
+ },
+ {
+ name: 'Sulka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sua'
+ },
+ {
+ name: 'Suku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sub'
+ },
+ {
+ name: 'Western Subanon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suc'
+ },
+ {
+ name: 'Suena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sue'
+ },
+ {
+ name: 'Suganga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sug'
+ },
+ {
+ name: 'Suki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sui'
+ },
+ {
+ name: 'Shubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suj'
+ },
+ {
+ name: 'Sukuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suk',
+ iso6392B: 'suk',
+ iso6392T: 'suk'
+ },
+ {
+ name: 'Sundanese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sun',
+ iso6392B: 'sun',
+ iso6392T: 'sun',
+ iso6391: 'su'
+ },
+ {
+ name: 'Suri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suq'
+ },
+ {
+ name: 'Mwaghavul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sur'
+ },
+ {
+ name: 'Susu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sus',
+ iso6392B: 'sus',
+ iso6392T: 'sus'
+ },
+ {
+ name: 'Subtiaba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sut'
+ },
+ {
+ name: 'Puroik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suv'
+ },
+ {
+ name: 'Sumbwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suw'
+ },
+ {
+ name: 'Sumerian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'sux',
+ iso6392B: 'sux',
+ iso6392T: 'sux'
+ },
+ {
+ name: 'Suyá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suy'
+ },
+ {
+ name: 'Sunwar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'suz'
+ },
+ {
+ name: 'Svan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sva'
+ },
+ {
+ name: 'Ulau-Suain',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'svb'
+ },
+ {
+ name: 'Vincentian Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'svc'
+ },
+ {
+ name: 'Serili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sve'
+ },
+ {
+ name: 'Slovakian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'svk'
+ },
+ {
+ name: 'Slavomolisano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'svm'
+ },
+ {
+ name: 'Savosavo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'svs'
+ },
+ {
+ name: 'Skalvian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'svx'
+ },
+ {
+ name: 'Swahili (macrolanguage)',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'swa',
+ iso6392B: 'swa',
+ iso6392T: 'swa',
+ iso6391: 'sw'
+ },
+ {
+ name: 'Maore Comorian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swb'
+ },
+ {
+ name: 'Congo Swahili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swc'
+ },
+ {
+ name: 'Swedish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swe',
+ iso6392B: 'swe',
+ iso6392T: 'swe',
+ iso6391: 'sv'
+ },
+ {
+ name: 'Sere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swf'
+ },
+ {
+ name: 'Swabian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swg'
+ },
+ {
+ name: 'Swahili (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swh'
+ },
+ {
+ name: 'Sui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swi'
+ },
+ {
+ name: 'Sira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swj'
+ },
+ {
+ name: 'Malawi Sena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swk'
+ },
+ {
+ name: 'Swedish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swl'
+ },
+ {
+ name: 'Samosa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swm'
+ },
+ {
+ name: 'Sawknah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swn'
+ },
+ {
+ name: 'Shanenawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swo'
+ },
+ {
+ name: 'Suau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swp'
+ },
+ {
+ name: 'Sharwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swq'
+ },
+ {
+ name: 'Saweru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swr'
+ },
+ {
+ name: 'Seluwasan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sws'
+ },
+ {
+ name: 'Sawila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swt'
+ },
+ {
+ name: 'Suwawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swu'
+ },
+ {
+ name: 'Shekhawati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swv'
+ },
+ {
+ name: 'Sowa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sww'
+ },
+ {
+ name: 'Suruahá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swx'
+ },
+ {
+ name: 'Sarua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'swy'
+ },
+ {
+ name: 'Suba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxb'
+ },
+ {
+ name: 'Sicanian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'sxc'
+ },
+ {
+ name: 'Sighu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxe'
+ },
+ {
+ name: 'Shuhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxg'
+ },
+ {
+ name: 'Southern Kalapuya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sxk'
+ },
+ {
+ name: 'Selian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'sxl'
+ },
+ {
+ name: 'Samre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxm'
+ },
+ {
+ name: 'Sangir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxn'
+ },
+ {
+ name: 'Sorothaptic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'sxo'
+ },
+ {
+ name: 'Saaroa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxr'
+ },
+ {
+ name: 'Sasaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxs'
+ },
+ {
+ name: 'Upper Saxon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxu'
+ },
+ {
+ name: 'Saxwe Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sxw'
+ },
+ {
+ name: 'Siang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sya'
+ },
+ {
+ name: 'Central Subanen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syb'
+ },
+ {
+ name: 'Classical Syriac',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'syc',
+ iso6392B: 'syc',
+ iso6392T: 'syc'
+ },
+ {
+ name: 'Seki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syi'
+ },
+ {
+ name: 'Sukur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syk'
+ },
+ {
+ name: 'Sylheti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syl'
+ },
+ {
+ name: 'Maya Samo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sym'
+ },
+ {
+ name: 'Senaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syn'
+ },
+ {
+ name: 'Suoy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syo'
+ },
+ {
+ name: 'Syriac',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'syr',
+ iso6392B: 'syr',
+ iso6392T: 'syr'
+ },
+ {
+ name: 'Sinyar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sys'
+ },
+ {
+ name: 'Kagate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syw'
+ },
+ {
+ name: 'Samay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syx'
+ },
+ {
+ name: 'Al-Sayyid Bedouin Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'syy'
+ },
+ {
+ name: 'Semelai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sza'
+ },
+ {
+ name: 'Ngalum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szb'
+ },
+ {
+ name: 'Semaq Beri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szc'
+ },
+ {
+ name: 'Seru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'szd'
+ },
+ {
+ name: 'Seze',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'sze'
+ },
+ {
+ name: 'Sengele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szg'
+ },
+ {
+ name: 'Silesian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szl'
+ },
+ {
+ name: 'Sula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szn'
+ },
+ {
+ name: 'Suabo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szp'
+ },
+ {
+ name: 'Solomon Islands Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szs'
+ },
+ {
+ name: 'Isu (Fako Division)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szv'
+ },
+ {
+ name: 'Sawai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szw'
+ },
+ {
+ name: 'Sakizaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'szy'
+ },
+ {
+ name: 'Lower Tanana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taa'
+ },
+ {
+ name: 'Tabassaran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tab'
+ },
+ {
+ name: 'Lowland Tarahumara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tac'
+ },
+ {
+ name: 'Tause',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tad'
+ },
+ {
+ name: 'Tariana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tae'
+ },
+ {
+ name: 'Tapirapé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taf'
+ },
+ {
+ name: 'Tagoi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tag'
+ },
+ {
+ name: 'Tahitian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tah',
+ iso6392B: 'tah',
+ iso6392T: 'tah',
+ iso6391: 'ty'
+ },
+ {
+ name: 'Eastern Tamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taj'
+ },
+ {
+ name: 'Tala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tak'
+ },
+ {
+ name: 'Tal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tal'
+ },
+ {
+ name: 'Tamil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tam',
+ iso6392B: 'tam',
+ iso6392T: 'tam',
+ iso6391: 'ta'
+ },
+ {
+ name: 'Tangale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tan'
+ },
+ {
+ name: 'Yami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tao'
+ },
+ {
+ name: 'Taabwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tap'
+ },
+ {
+ name: 'Tamasheq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taq'
+ },
+ {
+ name: 'Central Tarahumara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tar'
+ },
+ {
+ name: 'Tay Boi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tas'
+ },
+ {
+ name: 'Tatar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tat',
+ iso6392B: 'tat',
+ iso6392T: 'tat',
+ iso6391: 'tt'
+ },
+ {
+ name: 'Upper Tanana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tau'
+ },
+ {
+ name: 'Tatuyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tav'
+ },
+ {
+ name: 'Tai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taw'
+ },
+ {
+ name: 'Tamki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tax'
+ },
+ {
+ name: 'Atayal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tay'
+ },
+ {
+ name: 'Tocho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'taz'
+ },
+ {
+ name: 'Aikanã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tba'
+ },
+ {
+ name: 'Takia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbc'
+ },
+ {
+ name: 'Kaki Ae',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbd'
+ },
+ {
+ name: 'Tanimbili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbe'
+ },
+ {
+ name: 'Mandara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbf'
+ },
+ {
+ name: 'North Tairora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbg'
+ },
+ {
+ name: 'Dharawal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tbh'
+ },
+ {
+ name: 'Gaam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbi'
+ },
+ {
+ name: 'Tiang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbj'
+ },
+ {
+ name: 'Calamian Tagbanwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbk'
+ },
+ {
+ name: 'Tboli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbl'
+ },
+ {
+ name: 'Tagbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbm'
+ },
+ {
+ name: 'Barro Negro Tunebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbn'
+ },
+ {
+ name: 'Tawala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbo'
+ },
+ {
+ name: 'Taworta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbp'
+ },
+ {
+ name: 'Tumtum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbr'
+ },
+ {
+ name: 'Tanguat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbs'
+ },
+ {
+ name: 'Tembo (Kitembo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbt'
+ },
+ {
+ name: 'Tubar',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tbu'
+ },
+ {
+ name: 'Tobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbv'
+ },
+ {
+ name: 'Tagbanwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbw'
+ },
+ {
+ name: 'Kapin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbx'
+ },
+ {
+ name: 'Tabaru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tby'
+ },
+ {
+ name: 'Ditammari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tbz'
+ },
+ {
+ name: 'Ticuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tca'
+ },
+ {
+ name: 'Tanacross',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcb'
+ },
+ {
+ name: 'Datooga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcc'
+ },
+ {
+ name: 'Tafi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcd'
+ },
+ {
+ name: 'Southern Tutchone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tce'
+ },
+ {
+ name: "Malinaltepec Me'phaa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcf'
+ },
+ {
+ name: 'Tamagario',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcg'
+ },
+ {
+ name: 'Turks And Caicos Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tch'
+ },
+ {
+ name: 'Wára',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tci'
+ },
+ {
+ name: 'Tchitchege',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tck'
+ },
+ {
+ name: 'Taman (Myanmar)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tcl'
+ },
+ {
+ name: 'Tanahmerah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcm'
+ },
+ {
+ name: 'Tichurong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcn'
+ },
+ {
+ name: 'Taungyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tco'
+ },
+ {
+ name: 'Tawr Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcp'
+ },
+ {
+ name: 'Kaiy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcq'
+ },
+ {
+ name: 'Torres Strait Creole',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcs'
+ },
+ {
+ name: "T'en",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tct'
+ },
+ {
+ name: 'Southeastern Tarahumara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcu'
+ },
+ {
+ name: 'Tecpatlán Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcw'
+ },
+ {
+ name: 'Toda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcx'
+ },
+ {
+ name: 'Tulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcy'
+ },
+ {
+ name: 'Thado Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tcz'
+ },
+ {
+ name: 'Tagdal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tda'
+ },
+ {
+ name: 'Panchpargania',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdb'
+ },
+ {
+ name: 'Emberá-Tadó',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdc'
+ },
+ {
+ name: 'Tai Nüa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdd'
+ },
+ {
+ name: 'Tiranige Diga Dogon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tde'
+ },
+ {
+ name: 'Talieng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdf'
+ },
+ {
+ name: 'Western Tamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdg'
+ },
+ {
+ name: 'Thulung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdh'
+ },
+ {
+ name: 'Tomadino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdi'
+ },
+ {
+ name: 'Tajio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdj'
+ },
+ {
+ name: 'Tambas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdk'
+ },
+ {
+ name: 'Sur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdl'
+ },
+ {
+ name: 'Taruma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdm'
+ },
+ {
+ name: 'Tondano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdn'
+ },
+ {
+ name: 'Teme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdo'
+ },
+ {
+ name: 'Tita',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdq'
+ },
+ {
+ name: 'Todrah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdr'
+ },
+ {
+ name: 'Doutai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tds'
+ },
+ {
+ name: 'Tetun Dili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdt'
+ },
+ {
+ name: 'Toro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdv'
+ },
+ {
+ name: 'Tandroy-Mahafaly Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdx'
+ },
+ {
+ name: 'Tadyawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tdy'
+ },
+ {
+ name: 'Temiar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tea'
+ },
+ {
+ name: 'Tetete',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'teb'
+ },
+ {
+ name: 'Terik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tec'
+ },
+ {
+ name: 'Tepo Krumen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ted'
+ },
+ {
+ name: 'Huehuetla Tepehua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tee'
+ },
+ {
+ name: 'Teressa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tef'
+ },
+ {
+ name: 'Teke-Tege',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'teg'
+ },
+ {
+ name: 'Tehuelche',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'teh'
+ },
+ {
+ name: 'Torricelli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tei'
+ },
+ {
+ name: 'Ibali Teke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tek'
+ },
+ {
+ name: 'Telugu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tel',
+ iso6392B: 'tel',
+ iso6392T: 'tel',
+ iso6391: 'te'
+ },
+ {
+ name: 'Timne',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tem',
+ iso6392B: 'tem',
+ iso6392T: 'tem'
+ },
+ {
+ name: 'Tama (Colombia)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ten'
+ },
+ {
+ name: 'Teso',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'teo'
+ },
+ {
+ name: 'Tepecano',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tep'
+ },
+ {
+ name: 'Temein',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'teq'
+ },
+ {
+ name: 'Tereno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ter',
+ iso6392B: 'ter',
+ iso6392T: 'ter'
+ },
+ {
+ name: 'Tengger',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tes'
+ },
+ {
+ name: 'Tetum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tet',
+ iso6392B: 'tet',
+ iso6392T: 'tet'
+ },
+ {
+ name: 'Soo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'teu'
+ },
+ {
+ name: 'Teor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tev'
+ },
+ {
+ name: 'Tewa (USA)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tew'
+ },
+ {
+ name: 'Tennet',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tex'
+ },
+ {
+ name: 'Tulishi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tey'
+ },
+ {
+ name: 'Tetserret',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tez'
+ },
+ {
+ name: 'Tofin Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tfi'
+ },
+ {
+ name: 'Tanaina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tfn'
+ },
+ {
+ name: 'Tefaro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tfo'
+ },
+ {
+ name: 'Teribe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tfr'
+ },
+ {
+ name: 'Ternate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tft'
+ },
+ {
+ name: 'Sagalla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tga'
+ },
+ {
+ name: 'Tobilung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgb'
+ },
+ {
+ name: 'Tigak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgc'
+ },
+ {
+ name: 'Ciwogai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgd'
+ },
+ {
+ name: 'Eastern Gorkha Tamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tge'
+ },
+ {
+ name: 'Chalikha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgf'
+ },
+ {
+ name: 'Tobagonian Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgh'
+ },
+ {
+ name: 'Lawunuia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgi'
+ },
+ {
+ name: 'Tagin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgj'
+ },
+ {
+ name: 'Tajik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgk',
+ iso6392B: 'tgk',
+ iso6392T: 'tgk',
+ iso6391: 'tg'
+ },
+ {
+ name: 'Tagalog',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgl',
+ iso6392B: 'tgl',
+ iso6392T: 'tgl',
+ iso6391: 'tl'
+ },
+ {
+ name: 'Tandaganon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgn'
+ },
+ {
+ name: 'Sudest',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgo'
+ },
+ {
+ name: 'Tangoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgp'
+ },
+ {
+ name: 'Tring',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgq'
+ },
+ {
+ name: 'Tareng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgr'
+ },
+ {
+ name: 'Nume',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgs'
+ },
+ {
+ name: 'Central Tagbanwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgt'
+ },
+ {
+ name: 'Tanggu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgu'
+ },
+ {
+ name: 'Tingui-Boto',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tgv'
+ },
+ {
+ name: 'Tagwana Senoufo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgw'
+ },
+ {
+ name: 'Tagish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tgx'
+ },
+ {
+ name: 'Togoyo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tgy'
+ },
+ {
+ name: 'Tagalaka',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tgz'
+ },
+ {
+ name: 'Thai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tha',
+ iso6392B: 'tha',
+ iso6392T: 'tha',
+ iso6391: 'th'
+ },
+ {
+ name: 'Kuuk Thaayorre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thd'
+ },
+ {
+ name: 'Chitwania Tharu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'the'
+ },
+ {
+ name: 'Thangmi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thf'
+ },
+ {
+ name: 'Northern Tarahumara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thh'
+ },
+ {
+ name: 'Tai Long',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thi'
+ },
+ {
+ name: 'Tharaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thk'
+ },
+ {
+ name: 'Dangaura Tharu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thl'
+ },
+ {
+ name: 'Aheu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thm'
+ },
+ {
+ name: 'Thachanadan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thn'
+ },
+ {
+ name: 'Thompson',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thp'
+ },
+ {
+ name: 'Kochila Tharu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thq'
+ },
+ {
+ name: 'Rana Tharu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thr'
+ },
+ {
+ name: 'Thakali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ths'
+ },
+ {
+ name: 'Tahltan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tht'
+ },
+ {
+ name: 'Thuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thu'
+ },
+ {
+ name: 'Tahaggart Tamahaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thv'
+ },
+ {
+ name: 'Tha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thy'
+ },
+ {
+ name: 'Tayart Tamajeq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'thz'
+ },
+ {
+ name: 'Tidikelt Tamazight',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tia'
+ },
+ {
+ name: 'Tira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tic'
+ },
+ {
+ name: 'Tifal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tif'
+ },
+ {
+ name: 'Tigre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tig',
+ iso6392B: 'tig',
+ iso6392T: 'tig'
+ },
+ {
+ name: 'Timugon Murut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tih'
+ },
+ {
+ name: 'Tiene',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tii'
+ },
+ {
+ name: 'Tilung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tij'
+ },
+ {
+ name: 'Tikar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tik'
+ },
+ {
+ name: 'Tillamook',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'til'
+ },
+ {
+ name: 'Timbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tim'
+ },
+ {
+ name: 'Tindi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tin'
+ },
+ {
+ name: 'Teop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tio'
+ },
+ {
+ name: 'Trimuris',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tip'
+ },
+ {
+ name: 'Tiéfo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiq'
+ },
+ {
+ name: 'Tigrinya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tir',
+ iso6392B: 'tir',
+ iso6392T: 'tir',
+ iso6391: 'ti'
+ },
+ {
+ name: 'Masadiit Itneg',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tis'
+ },
+ {
+ name: 'Tinigua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tit'
+ },
+ {
+ name: 'Adasen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiu'
+ },
+ {
+ name: 'Tiv',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiv',
+ iso6392B: 'tiv',
+ iso6392T: 'tiv'
+ },
+ {
+ name: 'Tiwi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiw'
+ },
+ {
+ name: 'Southern Tiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tix'
+ },
+ {
+ name: 'Tiruray',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiy'
+ },
+ {
+ name: 'Tai Hongjin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tiz'
+ },
+ {
+ name: 'Tajuasohn',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tja'
+ },
+ {
+ name: 'Tunjung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjg'
+ },
+ {
+ name: 'Northern Tujia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tji'
+ },
+ {
+ name: 'Tjungundji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjj'
+ },
+ {
+ name: 'Tai Laing',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjl'
+ },
+ {
+ name: 'Timucua',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tjm'
+ },
+ {
+ name: 'Tonjon',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tjn'
+ },
+ {
+ name: 'Temacine Tamazight',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjo'
+ },
+ {
+ name: 'Tjupany',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjp'
+ },
+ {
+ name: 'Southern Tujia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjs'
+ },
+ {
+ name: 'Tjurruru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tju'
+ },
+ {
+ name: 'Djabwurrung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tjw'
+ },
+ {
+ name: 'Truká',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tka'
+ },
+ {
+ name: 'Buksa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkb'
+ },
+ {
+ name: 'Tukudede',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkd'
+ },
+ {
+ name: 'Takwane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tke'
+ },
+ {
+ name: 'Tukumanféd',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tkf'
+ },
+ {
+ name: 'Tesaka Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkg'
+ },
+ {
+ name: 'Tokelau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkl',
+ iso6392B: 'tkl',
+ iso6392T: 'tkl'
+ },
+ {
+ name: 'Takelma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tkm'
+ },
+ {
+ name: 'Toku-No-Shima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkn'
+ },
+ {
+ name: 'Tikopia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkp'
+ },
+ {
+ name: 'Tee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkq'
+ },
+ {
+ name: 'Tsakhur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkr'
+ },
+ {
+ name: 'Takestani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tks'
+ },
+ {
+ name: 'Kathoriya Tharu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkt'
+ },
+ {
+ name: 'Upper Necaxa Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tku'
+ },
+ {
+ name: 'Mur Pano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkv'
+ },
+ {
+ name: 'Teanu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkw'
+ },
+ {
+ name: 'Tangko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkx'
+ },
+ {
+ name: 'Takua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tkz'
+ },
+ {
+ name: 'Southwestern Tepehuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tla'
+ },
+ {
+ name: 'Tobelo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlb'
+ },
+ {
+ name: 'Yecuatla Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlc'
+ },
+ {
+ name: 'Talaud',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tld'
+ },
+ {
+ name: 'Telefol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlf'
+ },
+ {
+ name: 'Tofanma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlg'
+ },
+ {
+ name: 'Klingon',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'tlh',
+ iso6392B: 'tlh',
+ iso6392T: 'tlh'
+ },
+ {
+ name: 'Tlingit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tli',
+ iso6392B: 'tli',
+ iso6392T: 'tli'
+ },
+ {
+ name: 'Talinga-Bwisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlj'
+ },
+ {
+ name: 'Taloki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlk'
+ },
+ {
+ name: 'Tetela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tll'
+ },
+ {
+ name: 'Tolomako',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlm'
+ },
+ {
+ name: "Talondo'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tln'
+ },
+ {
+ name: 'Talodi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlo'
+ },
+ {
+ name: 'Filomena Mata-Coahuitlán Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlp'
+ },
+ {
+ name: 'Tai Loi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlq'
+ },
+ {
+ name: 'Talise',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlr'
+ },
+ {
+ name: 'Tambotalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tls'
+ },
+ {
+ name: 'Sou Nama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlt'
+ },
+ {
+ name: 'Tulehu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlu'
+ },
+ {
+ name: 'Taliabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlv'
+ },
+ {
+ name: 'Khehek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tlx'
+ },
+ {
+ name: 'Talysh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tly'
+ },
+ {
+ name: 'Tama (Chad)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tma'
+ },
+ {
+ name: 'Katbol',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmb'
+ },
+ {
+ name: 'Tumak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmc'
+ },
+ {
+ name: 'Haruai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmd'
+ },
+ {
+ name: 'Tremembé',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tme'
+ },
+ {
+ name: 'Toba-Maskoy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmf'
+ },
+ {
+ name: 'Ternateño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tmg'
+ },
+ {
+ name: 'Tamashek',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'tmh',
+ iso6392B: 'tmh',
+ iso6392T: 'tmh'
+ },
+ {
+ name: 'Tutuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmi'
+ },
+ {
+ name: 'Samarokena',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmj'
+ },
+ {
+ name: 'Northwestern Tamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmk'
+ },
+ {
+ name: 'Tamnim Citak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tml'
+ },
+ {
+ name: 'Tai Thanh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmm'
+ },
+ {
+ name: 'Taman (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmn'
+ },
+ {
+ name: 'Temoq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmo'
+ },
+ {
+ name: 'Tumleo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmq'
+ },
+ {
+ name: 'Jewish Babylonian Aramaic (ca. 200-1200 CE)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tmr'
+ },
+ {
+ name: 'Tima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tms'
+ },
+ {
+ name: 'Tasmate',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmt'
+ },
+ {
+ name: 'Iau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmu'
+ },
+ {
+ name: 'Tembo (Motembo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmv'
+ },
+ {
+ name: 'Temuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmw'
+ },
+ {
+ name: 'Tami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tmy'
+ },
+ {
+ name: 'Tamanaku',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tmz'
+ },
+ {
+ name: 'Tacana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tna'
+ },
+ {
+ name: 'Western Tunebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnb'
+ },
+ {
+ name: 'Tanimuca-Retuarã',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnc'
+ },
+ {
+ name: 'Angosturas Tunebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnd'
+ },
+ {
+ name: 'Tobanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tng'
+ },
+ {
+ name: 'Maiani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnh'
+ },
+ {
+ name: 'Tandia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tni'
+ },
+ {
+ name: 'Kwamera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnk'
+ },
+ {
+ name: 'Lenakel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnl'
+ },
+ {
+ name: 'Tabla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnm'
+ },
+ {
+ name: 'North Tanna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnn'
+ },
+ {
+ name: 'Toromono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tno'
+ },
+ {
+ name: 'Whitesands',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnp'
+ },
+ {
+ name: 'Taino',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tnq'
+ },
+ {
+ name: 'Ménik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnr'
+ },
+ {
+ name: 'Tenis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tns'
+ },
+ {
+ name: 'Tontemboan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnt'
+ },
+ {
+ name: 'Tay Khang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnu'
+ },
+ {
+ name: 'Tangchangya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnv'
+ },
+ {
+ name: 'Tonsawang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnw'
+ },
+ {
+ name: 'Tanema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnx'
+ },
+ {
+ name: 'Tongwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tny'
+ },
+ {
+ name: "Ten'edn",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tnz'
+ },
+ {
+ name: 'Toba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tob'
+ },
+ {
+ name: 'Coyutla Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toc'
+ },
+ {
+ name: 'Toma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tod'
+ },
+ {
+ name: 'Gizrra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tof'
+ },
+ {
+ name: 'Tonga (Nyasa)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tog',
+ iso6392B: 'tog',
+ iso6392T: 'tog'
+ },
+ {
+ name: 'Gitonga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toh'
+ },
+ {
+ name: 'Tonga (Zambia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toi'
+ },
+ {
+ name: 'Tojolabal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toj'
+ },
+ {
+ name: 'Tolowa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tol'
+ },
+ {
+ name: 'Tombulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tom'
+ },
+ {
+ name: 'Tonga (Tonga Islands)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ton',
+ iso6392B: 'ton',
+ iso6392T: 'ton',
+ iso6391: 'to'
+ },
+ {
+ name: 'Xicotepec De Juárez Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'too'
+ },
+ {
+ name: 'Papantla Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'top'
+ },
+ {
+ name: 'Toposa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toq'
+ },
+ {
+ name: 'Togbo-Vara Banda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tor'
+ },
+ {
+ name: 'Highland Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tos'
+ },
+ {
+ name: 'Tho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tou'
+ },
+ {
+ name: 'Upper Taromi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tov'
+ },
+ {
+ name: 'Jemez',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tow'
+ },
+ {
+ name: 'Tobian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tox'
+ },
+ {
+ name: 'Topoiyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toy'
+ },
+ {
+ name: 'To',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'toz'
+ },
+ {
+ name: 'Taupota',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpa'
+ },
+ {
+ name: "Azoyú Me'phaa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpc'
+ },
+ {
+ name: 'Tippera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpe'
+ },
+ {
+ name: 'Tarpia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpf'
+ },
+ {
+ name: 'Kula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpg'
+ },
+ {
+ name: 'Tok Pisin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpi',
+ iso6392B: 'tpi',
+ iso6392T: 'tpi'
+ },
+ {
+ name: 'Tapieté',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpj'
+ },
+ {
+ name: 'Tupinikin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tpk'
+ },
+ {
+ name: "Tlacoapa Me'phaa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpl'
+ },
+ {
+ name: 'Tampulma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpm'
+ },
+ {
+ name: 'Tupinambá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tpn'
+ },
+ {
+ name: 'Tai Pao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpo'
+ },
+ {
+ name: 'Pisaflores Tepehua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpp'
+ },
+ {
+ name: 'Tukpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpq'
+ },
+ {
+ name: 'Tuparí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpr'
+ },
+ {
+ name: 'Tlachichilco Tepehua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpt'
+ },
+ {
+ name: 'Tampuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpu'
+ },
+ {
+ name: 'Tanapag',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpv'
+ },
+ {
+ name: 'Tupí',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tpw'
+ },
+ {
+ name: "Acatepec Me'phaa",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpx'
+ },
+ {
+ name: 'Trumai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpy'
+ },
+ {
+ name: 'Tinputz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tpz'
+ },
+ {
+ name: 'Tembé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqb'
+ },
+ {
+ name: 'Lehali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tql'
+ },
+ {
+ name: 'Turumsa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqm'
+ },
+ {
+ name: 'Tenino',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqn'
+ },
+ {
+ name: 'Toaripi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqo'
+ },
+ {
+ name: 'Tomoip',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqp'
+ },
+ {
+ name: 'Tunni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqq'
+ },
+ {
+ name: 'Torona',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tqr'
+ },
+ {
+ name: 'Western Totonac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqt'
+ },
+ {
+ name: 'Touo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tqu'
+ },
+ {
+ name: 'Tonkawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tqw'
+ },
+ {
+ name: 'Tirahi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tra'
+ },
+ {
+ name: 'Terebu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trb'
+ },
+ {
+ name: 'Copala Triqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trc'
+ },
+ {
+ name: 'Turi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trd'
+ },
+ {
+ name: 'East Tarangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tre'
+ },
+ {
+ name: 'Trinidadian Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trf'
+ },
+ {
+ name: 'Lishán Didán',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trg'
+ },
+ {
+ name: 'Turaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trh'
+ },
+ {
+ name: 'Trió',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tri'
+ },
+ {
+ name: 'Toram',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trj'
+ },
+ {
+ name: 'Traveller Scottish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trl'
+ },
+ {
+ name: 'Tregami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trm'
+ },
+ {
+ name: 'Trinitario',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trn'
+ },
+ {
+ name: 'Tarao Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tro'
+ },
+ {
+ name: 'Kok Borok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trp'
+ },
+ {
+ name: 'San Martín Itunyoso Triqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trq'
+ },
+ {
+ name: 'Taushiro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trr'
+ },
+ {
+ name: 'Chicahuaxtla Triqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trs'
+ },
+ {
+ name: 'Tunggare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trt'
+ },
+ {
+ name: 'Turoyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tru'
+ },
+ {
+ name: 'Taroko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trv'
+ },
+ {
+ name: 'Torwali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trw'
+ },
+ {
+ name: 'Tringgus-Sembaan Bidayuh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'trx'
+ },
+ {
+ name: 'Turung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'try'
+ },
+ {
+ name: 'Torá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'trz'
+ },
+ {
+ name: 'Tsaangi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsa'
+ },
+ {
+ name: 'Tsamai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsb'
+ },
+ {
+ name: 'Tswa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsc'
+ },
+ {
+ name: 'Tsakonian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsd'
+ },
+ {
+ name: 'Tunisian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tse'
+ },
+ {
+ name: 'Tausug',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsg'
+ },
+ {
+ name: 'Tsuvan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsh'
+ },
+ {
+ name: 'Tsimshian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsi',
+ iso6392B: 'tsi',
+ iso6392T: 'tsi'
+ },
+ {
+ name: 'Tshangla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsj'
+ },
+ {
+ name: 'Tseku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsk'
+ },
+ {
+ name: "Ts'ün-Lao",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsl'
+ },
+ {
+ name: 'Turkish Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsm'
+ },
+ {
+ name: 'Tswana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsn',
+ iso6392B: 'tsn',
+ iso6392T: 'tsn',
+ iso6391: 'tn'
+ },
+ {
+ name: 'Tsonga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tso',
+ iso6392B: 'tso',
+ iso6392T: 'tso',
+ iso6391: 'ts'
+ },
+ {
+ name: 'Northern Toussian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsp'
+ },
+ {
+ name: 'Thai Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsq'
+ },
+ {
+ name: 'Akei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsr'
+ },
+ {
+ name: 'Taiwan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tss'
+ },
+ {
+ name: 'Tondi Songway Kiini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tst'
+ },
+ {
+ name: 'Tsou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsu'
+ },
+ {
+ name: 'Tsogo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsv'
+ },
+ {
+ name: 'Tsishingini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsw'
+ },
+ {
+ name: 'Mubami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsx'
+ },
+ {
+ name: 'Tebul Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsy'
+ },
+ {
+ name: 'Purepecha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tsz'
+ },
+ {
+ name: 'Tutelo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tta'
+ },
+ {
+ name: 'Gaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttb'
+ },
+ {
+ name: 'Tektiteko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttc'
+ },
+ {
+ name: 'Tauade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttd'
+ },
+ {
+ name: 'Bwanabwana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tte'
+ },
+ {
+ name: 'Tuotomb',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttf'
+ },
+ {
+ name: 'Tutong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttg'
+ },
+ {
+ name: "Upper Ta'oih",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tth'
+ },
+ {
+ name: 'Tobati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tti'
+ },
+ {
+ name: 'Tooro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttj'
+ },
+ {
+ name: 'Totoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttk'
+ },
+ {
+ name: 'Totela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttl'
+ },
+ {
+ name: 'Northern Tutchone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttm'
+ },
+ {
+ name: 'Towei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttn'
+ },
+ {
+ name: "Lower Ta'oih",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tto'
+ },
+ {
+ name: 'Tombelala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttp'
+ },
+ {
+ name: 'Tawallammat Tamajaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttq'
+ },
+ {
+ name: 'Tera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttr'
+ },
+ {
+ name: 'Northeastern Thai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tts'
+ },
+ {
+ name: 'Muslim Tat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttt'
+ },
+ {
+ name: 'Torau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttu'
+ },
+ {
+ name: 'Titan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttv'
+ },
+ {
+ name: 'Long Wat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttw'
+ },
+ {
+ name: 'Sikaritai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tty'
+ },
+ {
+ name: 'Tsum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ttz'
+ },
+ {
+ name: 'Wiarumus',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tua'
+ },
+ {
+ name: 'Tübatulabal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tub'
+ },
+ {
+ name: 'Mutu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuc'
+ },
+ {
+ name: 'Tuxá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tud'
+ },
+ {
+ name: 'Tuyuca',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tue'
+ },
+ {
+ name: 'Central Tunebo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuf'
+ },
+ {
+ name: 'Tunia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tug'
+ },
+ {
+ name: 'Taulil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuh'
+ },
+ {
+ name: 'Tupuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tui'
+ },
+ {
+ name: 'Tugutil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuj'
+ },
+ {
+ name: 'Turkmen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuk',
+ iso6392B: 'tuk',
+ iso6392T: 'tuk',
+ iso6391: 'tk'
+ },
+ {
+ name: 'Tula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tul'
+ },
+ {
+ name: 'Tumbuka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tum',
+ iso6392B: 'tum',
+ iso6392T: 'tum'
+ },
+ {
+ name: 'Tunica',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tun'
+ },
+ {
+ name: 'Tucano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuo'
+ },
+ {
+ name: 'Tedaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuq'
+ },
+ {
+ name: 'Turkish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tur',
+ iso6392B: 'tur',
+ iso6392T: 'tur',
+ iso6391: 'tr'
+ },
+ {
+ name: 'Tuscarora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tus'
+ },
+ {
+ name: 'Tututni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuu'
+ },
+ {
+ name: 'Turkana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuv'
+ },
+ {
+ name: 'Tuxináwa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tux'
+ },
+ {
+ name: 'Tugen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuy'
+ },
+ {
+ name: 'Turka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tuz'
+ },
+ {
+ name: 'Vaghua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tva'
+ },
+ {
+ name: 'Tsuvadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvd'
+ },
+ {
+ name: "Te'un",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tve'
+ },
+ {
+ name: 'Southeast Ambrym',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvk'
+ },
+ {
+ name: 'Tuvalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvl',
+ iso6392B: 'tvl',
+ iso6392T: 'tvl'
+ },
+ {
+ name: 'Tela-Masbuar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvm'
+ },
+ {
+ name: 'Tavoyan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvn'
+ },
+ {
+ name: 'Tidore',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvo'
+ },
+ {
+ name: 'Taveta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvs'
+ },
+ {
+ name: 'Tutsa Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvt'
+ },
+ {
+ name: 'Tunen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvu'
+ },
+ {
+ name: 'Sedoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tvw'
+ },
+ {
+ name: 'Taivoan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tvx'
+ },
+ {
+ name: 'Timor Pidgin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'tvy'
+ },
+ {
+ name: 'Twana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'twa'
+ },
+ {
+ name: 'Western Tawbuid',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twb'
+ },
+ {
+ name: 'Teshenawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'twc'
+ },
+ {
+ name: 'Twents',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twd'
+ },
+ {
+ name: 'Tewa (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twe'
+ },
+ {
+ name: 'Northern Tiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twf'
+ },
+ {
+ name: 'Tereweng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twg'
+ },
+ {
+ name: 'Tai Dón',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twh'
+ },
+ {
+ name: 'Twi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twi',
+ iso6392B: 'twi',
+ iso6392T: 'twi',
+ iso6391: 'tw'
+ },
+ {
+ name: 'Tawara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twl'
+ },
+ {
+ name: 'Tawang Monpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twm'
+ },
+ {
+ name: 'Twendi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twn'
+ },
+ {
+ name: 'Tswapong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'two'
+ },
+ {
+ name: 'Ere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twp'
+ },
+ {
+ name: 'Tasawaq',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twq'
+ },
+ {
+ name: 'Southwestern Tarahumara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twr'
+ },
+ {
+ name: 'Turiwára',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'twt'
+ },
+ {
+ name: 'Termanu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twu'
+ },
+ {
+ name: 'Tuwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tww'
+ },
+ {
+ name: 'Tewe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twx'
+ },
+ {
+ name: 'Tawoyan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'twy'
+ },
+ {
+ name: 'Tombonuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txa'
+ },
+ {
+ name: 'Tokharian B',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'txb'
+ },
+ {
+ name: 'Tsetsaut',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'txc'
+ },
+ {
+ name: 'Totoli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txe'
+ },
+ {
+ name: 'Tangut',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'txg'
+ },
+ {
+ name: 'Thracian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'txh'
+ },
+ {
+ name: 'Ikpeng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txi'
+ },
+ {
+ name: 'Tarjumo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txj'
+ },
+ {
+ name: 'Tomini',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txm'
+ },
+ {
+ name: 'West Tarangan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txn'
+ },
+ {
+ name: 'Toto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txo'
+ },
+ {
+ name: 'Tii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txq'
+ },
+ {
+ name: 'Tartessian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'txr'
+ },
+ {
+ name: 'Tonsea',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txs'
+ },
+ {
+ name: 'Citak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txt'
+ },
+ {
+ name: 'Kayapó',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txu'
+ },
+ {
+ name: 'Tatana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txx'
+ },
+ {
+ name: 'Tanosy Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'txy'
+ },
+ {
+ name: 'Tauya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tya'
+ },
+ {
+ name: 'Kyanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tye'
+ },
+ {
+ name: "O'du",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyh'
+ },
+ {
+ name: 'Teke-Tsaayi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyi'
+ },
+ {
+ name: 'Tai Do',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyj'
+ },
+ {
+ name: 'Thu Lao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyl'
+ },
+ {
+ name: 'Kombai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyn'
+ },
+ {
+ name: 'Thaypan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'typ'
+ },
+ {
+ name: 'Tai Daeng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyr'
+ },
+ {
+ name: 'Tày Sa Pa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tys'
+ },
+ {
+ name: 'Tày Tac',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyt'
+ },
+ {
+ name: 'Kua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyu'
+ },
+ {
+ name: 'Tuvinian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyv',
+ iso6392B: 'tyv',
+ iso6392T: 'tyv'
+ },
+ {
+ name: 'Teke-Tyee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyx'
+ },
+ {
+ name: 'Tày',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tyz'
+ },
+ {
+ name: 'Tanzanian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tza'
+ },
+ {
+ name: 'Tzeltal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzh'
+ },
+ {
+ name: "Tz'utujil",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzj'
+ },
+ {
+ name: 'Talossan',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'tzl'
+ },
+ {
+ name: 'Central Atlas Tamazight',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzm'
+ },
+ {
+ name: 'Tugun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzn'
+ },
+ {
+ name: 'Tzotzil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzo'
+ },
+ {
+ name: 'Tabriak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'tzx'
+ },
+ {
+ name: 'Uamué',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'uam'
+ },
+ {
+ name: 'Kuan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uan'
+ },
+ {
+ name: 'Tairuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uar'
+ },
+ {
+ name: 'Ubang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uba'
+ },
+ {
+ name: 'Ubi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ubi'
+ },
+ {
+ name: "Buhi'non Bikol",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ubl'
+ },
+ {
+ name: 'Ubir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ubr'
+ },
+ {
+ name: 'Umbu-Ungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ubu'
+ },
+ {
+ name: 'Ubykh',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'uby'
+ },
+ {
+ name: 'Uda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uda'
+ },
+ {
+ name: 'Udihe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ude'
+ },
+ {
+ name: 'Muduga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udg'
+ },
+ {
+ name: 'Udi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udi'
+ },
+ {
+ name: 'Ujir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udj'
+ },
+ {
+ name: 'Wuzlam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udl'
+ },
+ {
+ name: 'Udmurt',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udm',
+ iso6392B: 'udm',
+ iso6392T: 'udm'
+ },
+ {
+ name: 'Uduk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'udu'
+ },
+ {
+ name: 'Kioko',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ues'
+ },
+ {
+ name: 'Ufim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ufi'
+ },
+ {
+ name: 'Ugaritic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'uga',
+ iso6392B: 'uga',
+ iso6392T: 'uga'
+ },
+ {
+ name: 'Kuku-Ugbanh',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ugb'
+ },
+ {
+ name: 'Ughele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uge'
+ },
+ {
+ name: 'Ugandan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ugn'
+ },
+ {
+ name: 'Ugong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ugo'
+ },
+ {
+ name: 'Uruguayan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ugy'
+ },
+ {
+ name: 'Uhami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uha'
+ },
+ {
+ name: 'Damal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uhn'
+ },
+ {
+ name: 'Uighur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uig',
+ iso6392B: 'uig',
+ iso6392T: 'uig',
+ iso6391: 'ug'
+ },
+ {
+ name: 'Uisai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uis'
+ },
+ {
+ name: 'Iyive',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uiv'
+ },
+ {
+ name: 'Tanjijili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uji'
+ },
+ {
+ name: 'Kaburi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uka'
+ },
+ {
+ name: 'Ukuriguma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukg'
+ },
+ {
+ name: 'Ukhwejo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukh'
+ },
+ {
+ name: 'Kui (India)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uki'
+ },
+ {
+ name: 'Muak Sa-aak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukk'
+ },
+ {
+ name: 'Ukrainian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukl'
+ },
+ {
+ name: 'Ukpe-Bayobiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukp'
+ },
+ {
+ name: 'Ukwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukq'
+ },
+ {
+ name: 'Ukrainian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukr',
+ iso6392B: 'ukr',
+ iso6392T: 'ukr',
+ iso6391: 'uk'
+ },
+ {
+ name: 'Urubú-Kaapor Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uks'
+ },
+ {
+ name: 'Ukue',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uku'
+ },
+ {
+ name: 'Kuku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukv'
+ },
+ {
+ name: 'Ukwuani-Aboh-Ndoni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ukw'
+ },
+ {
+ name: 'Kuuk-Yak',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'uky'
+ },
+ {
+ name: 'Fungwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ula'
+ },
+ {
+ name: 'Ulukwumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulb'
+ },
+ {
+ name: 'Ulch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulc'
+ },
+ {
+ name: 'Lule',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ule'
+ },
+ {
+ name: 'Usku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulf'
+ },
+ {
+ name: 'Ulithian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uli'
+ },
+ {
+ name: 'Meriam Mir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulk'
+ },
+ {
+ name: 'Ullatan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ull'
+ },
+ {
+ name: "Ulumanda'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulm'
+ },
+ {
+ name: 'Unserdeutsch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uln'
+ },
+ {
+ name: "Uma' Lung",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulu'
+ },
+ {
+ name: 'Ulwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ulw'
+ },
+ {
+ name: 'Umatilla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uma'
+ },
+ {
+ name: 'Umbundu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'umb',
+ iso6392B: 'umb',
+ iso6392T: 'umb'
+ },
+ {
+ name: 'Marrucinian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'umc'
+ },
+ {
+ name: 'Umbindhamu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'umd'
+ },
+ {
+ name: 'Morrobalama',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'umg'
+ },
+ {
+ name: 'Ukit',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'umi'
+ },
+ {
+ name: 'Umon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'umm'
+ },
+ {
+ name: 'Makyan Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'umn'
+ },
+ {
+ name: 'Umotína',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'umo'
+ },
+ {
+ name: 'Umpila',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ump'
+ },
+ {
+ name: 'Umbugarla',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'umr'
+ },
+ {
+ name: 'Pendau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ums'
+ },
+ {
+ name: 'Munsee',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'umu'
+ },
+ {
+ name: 'North Watut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'una'
+ },
+ {
+ name: 'Undetermined',
+ type: 'special',
+ scope: 'special',
+ iso6393: 'und',
+ iso6392B: 'und',
+ iso6392T: 'und'
+ },
+ {
+ name: 'Uneme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'une'
+ },
+ {
+ name: 'Ngarinyin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ung'
+ },
+ {
+ name: 'Enawené-Nawé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unk'
+ },
+ {
+ name: 'Unami',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'unm'
+ },
+ {
+ name: 'Kurnai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unn'
+ },
+ {
+ name: 'Mundari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unr'
+ },
+ {
+ name: 'Unubahe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unu'
+ },
+ {
+ name: 'Munda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unx'
+ },
+ {
+ name: 'Unde Kaili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'unz'
+ },
+ {
+ name: 'Umeda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'upi'
+ },
+ {
+ name: 'Uripiv-Wala-Rano-Atchin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'upv'
+ },
+ {
+ name: 'Urarina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ura'
+ },
+ {
+ name: 'Urubú-Kaapor',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urb'
+ },
+ {
+ name: 'Urningangg',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'urc'
+ },
+ {
+ name: 'Urdu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urd',
+ iso6392B: 'urd',
+ iso6392T: 'urd',
+ iso6391: 'ur'
+ },
+ {
+ name: 'Uru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ure'
+ },
+ {
+ name: 'Uradhi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'urf'
+ },
+ {
+ name: 'Urigina',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urg'
+ },
+ {
+ name: 'Urhobo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urh'
+ },
+ {
+ name: 'Urim',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uri'
+ },
+ {
+ name: "Urak Lawoi'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urk'
+ },
+ {
+ name: 'Urali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'url'
+ },
+ {
+ name: 'Urapmin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urm'
+ },
+ {
+ name: 'Uruangnirin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urn'
+ },
+ {
+ name: 'Ura (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uro'
+ },
+ {
+ name: 'Uru-Pa-In',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urp'
+ },
+ {
+ name: 'Lehalurup',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urr'
+ },
+ {
+ name: 'Urat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urt'
+ },
+ {
+ name: 'Urumi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'uru'
+ },
+ {
+ name: 'Uruava',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'urv'
+ },
+ {
+ name: 'Sop',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urw'
+ },
+ {
+ name: 'Urimo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urx'
+ },
+ {
+ name: 'Orya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ury'
+ },
+ {
+ name: 'Uru-Eu-Wau-Wau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'urz'
+ },
+ {
+ name: 'Usarufa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'usa'
+ },
+ {
+ name: 'Ushojo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ush'
+ },
+ {
+ name: 'Usui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'usi'
+ },
+ {
+ name: 'Usaghade',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'usk'
+ },
+ {
+ name: 'Uspanteco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'usp'
+ },
+ {
+ name: 'us-Saare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uss'
+ },
+ {
+ name: 'Uya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'usu'
+ },
+ {
+ name: 'Otank',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uta'
+ },
+ {
+ name: 'Ute-Southern Paiute',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ute'
+ },
+ {
+ name: 'ut-Hun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uth'
+ },
+ {
+ name: 'Amba (Solomon Islands)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'utp'
+ },
+ {
+ name: 'Etulo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'utr'
+ },
+ {
+ name: 'Utu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'utu'
+ },
+ {
+ name: 'Urum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uum'
+ },
+ {
+ name: 'Kulon-Pazeh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uun'
+ },
+ {
+ name: 'Ura (Vanuatu)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uur'
+ },
+ {
+ name: 'U',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uuu'
+ },
+ {
+ name: 'West Uvean',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uve'
+ },
+ {
+ name: 'Uri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uvh'
+ },
+ {
+ name: 'Lote',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uvl'
+ },
+ {
+ name: 'Kuku-Uwanh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uwa'
+ },
+ {
+ name: 'Doko-Uyanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uya'
+ },
+ {
+ name: 'Uzbek',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'uzb',
+ iso6392B: 'uzb',
+ iso6392T: 'uzb',
+ iso6391: 'uz'
+ },
+ {
+ name: 'Northern Uzbek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uzn'
+ },
+ {
+ name: 'Southern Uzbek',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'uzs'
+ },
+ {
+ name: 'Vaagri Booli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vaa'
+ },
+ {
+ name: 'Vale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vae'
+ },
+ {
+ name: 'Vafsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vaf'
+ },
+ {
+ name: 'Vagla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vag'
+ },
+ {
+ name: 'Varhadi-Nagpuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vah'
+ },
+ {
+ name: 'Vai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vai',
+ iso6392B: 'vai',
+ iso6392T: 'vai'
+ },
+ {
+ name: 'Sekele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vaj'
+ },
+ {
+ name: 'Vehes',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'val'
+ },
+ {
+ name: 'Vanimo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vam'
+ },
+ {
+ name: 'Valman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'van'
+ },
+ {
+ name: 'Vao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vao'
+ },
+ {
+ name: 'Vaiphei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vap'
+ },
+ {
+ name: 'Huarijio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'var'
+ },
+ {
+ name: 'Vasavi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vas'
+ },
+ {
+ name: 'Vanuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vau'
+ },
+ {
+ name: 'Varli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vav'
+ },
+ {
+ name: 'Wayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vay'
+ },
+ {
+ name: 'Southeast Babar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vbb'
+ },
+ {
+ name: 'Southwestern Bontok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vbk'
+ },
+ {
+ name: 'Venetian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vec'
+ },
+ {
+ name: 'Veddah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ved'
+ },
+ {
+ name: 'Veluws',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vel'
+ },
+ {
+ name: 'Vemgo-Mabas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vem'
+ },
+ {
+ name: 'Venda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ven',
+ iso6392B: 'ven',
+ iso6392T: 'ven',
+ iso6391: 've'
+ },
+ {
+ name: 'Ventureño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'veo'
+ },
+ {
+ name: 'Veps',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vep'
+ },
+ {
+ name: 'Mom Jango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ver'
+ },
+ {
+ name: 'Vaghri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vgr'
+ },
+ {
+ name: 'Vlaamse Gebarentaal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vgt'
+ },
+ {
+ name: 'Virgin Islands Creole English',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vic'
+ },
+ {
+ name: 'Vidunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vid'
+ },
+ {
+ name: 'Vietnamese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vie',
+ iso6392B: 'vie',
+ iso6392T: 'vie',
+ iso6391: 'vi'
+ },
+ {
+ name: 'Vili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vif'
+ },
+ {
+ name: 'Viemo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vig'
+ },
+ {
+ name: 'Vilela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vil'
+ },
+ {
+ name: 'Vinza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vin'
+ },
+ {
+ name: 'Vishavan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vis'
+ },
+ {
+ name: 'Viti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vit'
+ },
+ {
+ name: 'Iduna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'viv'
+ },
+ {
+ name: 'Kariyarra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vka'
+ },
+ {
+ name: 'Ija-Zuba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vki'
+ },
+ {
+ name: 'Kujarge',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vkj'
+ },
+ {
+ name: 'Kaur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vkk'
+ },
+ {
+ name: 'Kulisusu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vkl'
+ },
+ {
+ name: 'Kamakan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vkm'
+ },
+ {
+ name: 'Kodeoha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vko'
+ },
+ {
+ name: 'Korlai Creole Portuguese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vkp'
+ },
+ {
+ name: 'Tenggarong Kutai Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vkt'
+ },
+ {
+ name: 'Kurrama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vku'
+ },
+ {
+ name: 'Valpei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vlp'
+ },
+ {
+ name: 'Vlaams',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vls'
+ },
+ {
+ name: 'Martuyhunira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vma'
+ },
+ {
+ name: 'Barbaram',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vmb'
+ },
+ {
+ name: 'Juxtlahuaca Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmc'
+ },
+ {
+ name: 'Mudu Koraga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmd'
+ },
+ {
+ name: 'East Masela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vme'
+ },
+ {
+ name: 'Mainfränkisch',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmf'
+ },
+ {
+ name: 'Lungalunga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmg'
+ },
+ {
+ name: 'Maraghei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmh'
+ },
+ {
+ name: 'Miwa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vmi'
+ },
+ {
+ name: 'Ixtayutla Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmj'
+ },
+ {
+ name: 'Makhuwa-Shirima',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmk'
+ },
+ {
+ name: 'Malgana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vml'
+ },
+ {
+ name: 'Mitlatongo Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmm'
+ },
+ {
+ name: 'Soyaltepec Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmp'
+ },
+ {
+ name: 'Soyaltepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmq'
+ },
+ {
+ name: 'Marenje',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmr'
+ },
+ {
+ name: 'Moksela',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vms'
+ },
+ {
+ name: 'Muluridyi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vmu'
+ },
+ {
+ name: 'Valley Maidu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'vmv'
+ },
+ {
+ name: 'Makhuwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmw'
+ },
+ {
+ name: 'Tamazola Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmx'
+ },
+ {
+ name: 'Ayautla Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmy'
+ },
+ {
+ name: 'Mazatlán Mazatec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vmz'
+ },
+ {
+ name: 'Vano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vnk'
+ },
+ {
+ name: 'Vinmavis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vnm'
+ },
+ {
+ name: 'Vunapu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vnp'
+ },
+ {
+ name: 'Volapük',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'vol',
+ iso6392B: 'vol',
+ iso6392T: 'vol',
+ iso6391: 'vo'
+ },
+ {
+ name: 'Voro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vor'
+ },
+ {
+ name: 'Votic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vot',
+ iso6392B: 'vot',
+ iso6392T: 'vot'
+ },
+ {
+ name: "Vera'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vra'
+ },
+ {
+ name: 'Võro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vro'
+ },
+ {
+ name: 'Varisi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vrs'
+ },
+ {
+ name: 'Burmbar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vrt'
+ },
+ {
+ name: 'Moldova Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vsi'
+ },
+ {
+ name: 'Venezuelan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vsl'
+ },
+ {
+ name: 'Valencian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vsv'
+ },
+ {
+ name: 'Vitou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vto'
+ },
+ {
+ name: 'Vumbu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vum'
+ },
+ {
+ name: 'Vunjo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vun'
+ },
+ {
+ name: 'Vute',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vut'
+ },
+ {
+ name: 'Awa (China)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'vwa'
+ },
+ {
+ name: 'Walla Walla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'waa'
+ },
+ {
+ name: 'Wab',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wab'
+ },
+ {
+ name: 'Wasco-Wishram',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wac'
+ },
+ {
+ name: 'Wandamen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wad'
+ },
+ {
+ name: 'Walser',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wae'
+ },
+ {
+ name: 'Wakoná',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'waf'
+ },
+ {
+ name: "Wa'ema",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wag'
+ },
+ {
+ name: 'Watubela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wah'
+ },
+ {
+ name: 'Wares',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wai'
+ },
+ {
+ name: 'Waffa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'waj'
+ },
+ {
+ name: 'Wolaytta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wal',
+ iso6392B: 'wal',
+ iso6392T: 'wal'
+ },
+ {
+ name: 'Wampanoag',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wam'
+ },
+ {
+ name: 'Wan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wan'
+ },
+ {
+ name: 'Wappo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wao'
+ },
+ {
+ name: 'Wapishana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wap'
+ },
+ {
+ name: 'Wagiman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'waq'
+ },
+ {
+ name: 'Waray (Philippines)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'war',
+ iso6392B: 'war',
+ iso6392T: 'war'
+ },
+ {
+ name: 'Washo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'was',
+ iso6392B: 'was',
+ iso6392T: 'was'
+ },
+ {
+ name: 'Kaninuwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wat'
+ },
+ {
+ name: 'Waurá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wau'
+ },
+ {
+ name: 'Waka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wav'
+ },
+ {
+ name: 'Waiwai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'waw'
+ },
+ {
+ name: 'Watam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wax'
+ },
+ {
+ name: 'Wayana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'way'
+ },
+ {
+ name: 'Wampur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'waz'
+ },
+ {
+ name: 'Warao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wba'
+ },
+ {
+ name: 'Wabo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbb'
+ },
+ {
+ name: 'Waritai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbe'
+ },
+ {
+ name: 'Wara',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbf'
+ },
+ {
+ name: 'Wanda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbh'
+ },
+ {
+ name: 'Vwanji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbi'
+ },
+ {
+ name: 'Alagwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbj'
+ },
+ {
+ name: 'Waigali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbk'
+ },
+ {
+ name: 'Wakhi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbl'
+ },
+ {
+ name: 'Wa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbm'
+ },
+ {
+ name: 'Warlpiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbp'
+ },
+ {
+ name: 'Waddar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbq'
+ },
+ {
+ name: 'Wagdi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbr'
+ },
+ {
+ name: 'West Bengal Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbs'
+ },
+ {
+ name: 'Warnman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbt'
+ },
+ {
+ name: 'Wajarri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbv'
+ },
+ {
+ name: 'Woi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wbw'
+ },
+ {
+ name: 'Yanomámi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wca'
+ },
+ {
+ name: 'Waci Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wci'
+ },
+ {
+ name: 'Wandji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wdd'
+ },
+ {
+ name: 'Wadaginam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wdg'
+ },
+ {
+ name: 'Wadjiginy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wdj'
+ },
+ {
+ name: 'Wadikali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wdk'
+ },
+ {
+ name: 'Wadjigu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wdu'
+ },
+ {
+ name: 'Wadjabangayi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wdy'
+ },
+ {
+ name: 'Wewaw',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wea'
+ },
+ {
+ name: 'Wè Western',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wec'
+ },
+ {
+ name: 'Wedau',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wed'
+ },
+ {
+ name: 'Wergaia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'weg'
+ },
+ {
+ name: 'Weh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'weh'
+ },
+ {
+ name: 'Kiunum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wei'
+ },
+ {
+ name: 'Weme Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wem'
+ },
+ {
+ name: 'Wemale',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'weo'
+ },
+ {
+ name: 'Westphalien',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wep'
+ },
+ {
+ name: 'Weri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wer'
+ },
+ {
+ name: 'Cameroon Pidgin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wes'
+ },
+ {
+ name: 'Perai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wet'
+ },
+ {
+ name: 'Rawngtu Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'weu'
+ },
+ {
+ name: 'Wejewa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wew'
+ },
+ {
+ name: 'Yafi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wfg'
+ },
+ {
+ name: 'Wagaya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wga'
+ },
+ {
+ name: 'Wagawaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wgb'
+ },
+ {
+ name: 'Wangkangurru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wgg'
+ },
+ {
+ name: 'Wahgi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wgi'
+ },
+ {
+ name: 'Waigeo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wgo'
+ },
+ {
+ name: 'Wirangu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wgu'
+ },
+ {
+ name: 'Warrgamay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wgy'
+ },
+ {
+ name: 'Sou Upaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wha'
+ },
+ {
+ name: 'North Wahgi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'whg'
+ },
+ {
+ name: 'Wahau Kenyah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'whk'
+ },
+ {
+ name: 'Wahau Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'whu'
+ },
+ {
+ name: 'Southern Toussian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wib'
+ },
+ {
+ name: 'Wichita',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wic'
+ },
+ {
+ name: 'Wik-Epa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wie'
+ },
+ {
+ name: 'Wik-Keyangan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wif'
+ },
+ {
+ name: 'Wik Ngathan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wig'
+ },
+ {
+ name: "Wik-Me'anha",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wih'
+ },
+ {
+ name: 'Minidien',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wii'
+ },
+ {
+ name: 'Wik-Iiyanh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wij'
+ },
+ {
+ name: 'Wikalkan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wik'
+ },
+ {
+ name: 'Wilawila',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wil'
+ },
+ {
+ name: 'Wik-Mungkan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wim'
+ },
+ {
+ name: 'Ho-Chunk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'win'
+ },
+ {
+ name: 'Wiraféd',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wir'
+ },
+ {
+ name: 'Wiru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wiu'
+ },
+ {
+ name: 'Vitu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wiv'
+ },
+ {
+ name: 'Wiyot',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wiy'
+ },
+ {
+ name: 'Waja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wja'
+ },
+ {
+ name: 'Warji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wji'
+ },
+ {
+ name: "Kw'adza",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wka'
+ },
+ {
+ name: 'Kumbaran',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wkb'
+ },
+ {
+ name: 'Wakde',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wkd'
+ },
+ {
+ name: 'Kalanadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wkl'
+ },
+ {
+ name: 'Keerray-Woorroong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wkr'
+ },
+ {
+ name: 'Kunduvadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wku'
+ },
+ {
+ name: 'Wakawaka',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wkw'
+ },
+ {
+ name: 'Wangkayutyuru',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wky'
+ },
+ {
+ name: 'Walio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wla'
+ },
+ {
+ name: 'Mwali Comorian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlc'
+ },
+ {
+ name: 'Wolane',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wle'
+ },
+ {
+ name: 'Kunbarlang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlg'
+ },
+ {
+ name: 'Welaun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlh'
+ },
+ {
+ name: 'Waioli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wli'
+ },
+ {
+ name: 'Wailaki',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wlk'
+ },
+ {
+ name: 'Wali (Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wll'
+ },
+ {
+ name: 'Middle Welsh',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'wlm'
+ },
+ {
+ name: 'Walloon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wln',
+ iso6392B: 'wln',
+ iso6392T: 'wln',
+ iso6391: 'wa'
+ },
+ {
+ name: 'Wolio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlo'
+ },
+ {
+ name: 'Wailapa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlr'
+ },
+ {
+ name: 'Wallisian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wls'
+ },
+ {
+ name: 'Wuliwuli',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wlu'
+ },
+ {
+ name: 'Wichí Lhamtés Vejoz',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlv'
+ },
+ {
+ name: 'Walak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlw'
+ },
+ {
+ name: 'Wali (Ghana)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wlx'
+ },
+ {
+ name: 'Waling',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wly'
+ },
+ {
+ name: 'Mawa (Nigeria)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wma'
+ },
+ {
+ name: 'Wambaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmb'
+ },
+ {
+ name: 'Wamas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmc'
+ },
+ {
+ name: 'Mamaindé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmd'
+ },
+ {
+ name: 'Wambule',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wme'
+ },
+ {
+ name: "Waima'a",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmh'
+ },
+ {
+ name: 'Wamin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wmi'
+ },
+ {
+ name: 'Maiwa (Indonesia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmm'
+ },
+ {
+ name: 'Waamwang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wmn'
+ },
+ {
+ name: 'Wom (Papua New Guinea)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmo'
+ },
+ {
+ name: 'Wambon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wms'
+ },
+ {
+ name: 'Walmajarri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmt'
+ },
+ {
+ name: 'Mwani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmw'
+ },
+ {
+ name: 'Womo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wmx'
+ },
+ {
+ name: 'Wanambre',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnb'
+ },
+ {
+ name: 'Wantoat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnc'
+ },
+ {
+ name: 'Wandarang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wnd'
+ },
+ {
+ name: 'Waneci',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wne'
+ },
+ {
+ name: 'Wanggom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wng'
+ },
+ {
+ name: 'Ndzwani Comorian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wni'
+ },
+ {
+ name: 'Wanukaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnk'
+ },
+ {
+ name: 'Wanggamala',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wnm'
+ },
+ {
+ name: 'Wunumara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wnn'
+ },
+ {
+ name: 'Wano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wno'
+ },
+ {
+ name: 'Wanap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnp'
+ },
+ {
+ name: 'Usan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnu'
+ },
+ {
+ name: 'Wintu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wnw'
+ },
+ {
+ name: 'Wanyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wny'
+ },
+ {
+ name: 'Kuwema',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'woa'
+ },
+ {
+ name: 'Wè Northern',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wob'
+ },
+ {
+ name: 'Wogeo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'woc'
+ },
+ {
+ name: 'Wolani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wod'
+ },
+ {
+ name: 'Woleaian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'woe'
+ },
+ {
+ name: 'Gambian Wolof',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wof'
+ },
+ {
+ name: 'Wogamusin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wog'
+ },
+ {
+ name: 'Kamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'woi'
+ },
+ {
+ name: 'Longto',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wok'
+ },
+ {
+ name: 'Wolof',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wol',
+ iso6392B: 'wol',
+ iso6392T: 'wol',
+ iso6391: 'wo'
+ },
+ {
+ name: 'Wom (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wom'
+ },
+ {
+ name: 'Wongo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'won'
+ },
+ {
+ name: 'Manombai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'woo'
+ },
+ {
+ name: 'Woria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wor'
+ },
+ {
+ name: 'Hanga Hundi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wos'
+ },
+ {
+ name: 'Wawonii',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wow'
+ },
+ {
+ name: 'Weyto',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'woy'
+ },
+ {
+ name: 'Maco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wpc'
+ },
+ {
+ name: 'Warapu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wra'
+ },
+ {
+ name: 'Waluwarra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wrb'
+ },
+ {
+ name: 'Warduji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrd'
+ },
+ {
+ name: 'Warungu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wrg'
+ },
+ {
+ name: 'Wiradjuri',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wrh'
+ },
+ {
+ name: 'Wariyangga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wri'
+ },
+ {
+ name: 'Garrwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrk'
+ },
+ {
+ name: 'Warlmanpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrl'
+ },
+ {
+ name: 'Warumungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrm'
+ },
+ {
+ name: 'Warnang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrn'
+ },
+ {
+ name: 'Worrorra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wro'
+ },
+ {
+ name: 'Waropen',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrp'
+ },
+ {
+ name: 'Wardaman',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrr'
+ },
+ {
+ name: 'Waris',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrs'
+ },
+ {
+ name: 'Waru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wru'
+ },
+ {
+ name: 'Waruna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrv'
+ },
+ {
+ name: 'Gugu Warra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wrw'
+ },
+ {
+ name: 'Wae Rana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wrx'
+ },
+ {
+ name: 'Merwari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wry'
+ },
+ {
+ name: 'Waray (Australia)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wrz'
+ },
+ {
+ name: 'Warembori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wsa'
+ },
+ {
+ name: 'Adilabad Gondi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wsg'
+ },
+ {
+ name: 'Wusi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wsi'
+ },
+ {
+ name: 'Waskia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wsk'
+ },
+ {
+ name: 'Owenia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wsr'
+ },
+ {
+ name: 'Wasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wss'
+ },
+ {
+ name: 'Wasu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wsu'
+ },
+ {
+ name: 'Wotapuri-Katarqalai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wsv'
+ },
+ {
+ name: 'Watiwa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wtf'
+ },
+ {
+ name: 'Wathawurrung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wth'
+ },
+ {
+ name: 'Berta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wti'
+ },
+ {
+ name: 'Watakataui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wtk'
+ },
+ {
+ name: 'Mewati',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wtm'
+ },
+ {
+ name: 'Wotu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wtw'
+ },
+ {
+ name: 'Wikngenchera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wua'
+ },
+ {
+ name: 'Wunambal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wub'
+ },
+ {
+ name: 'Wudu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wud'
+ },
+ {
+ name: 'Wutunhua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wuh'
+ },
+ {
+ name: 'Silimo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wul'
+ },
+ {
+ name: 'Wumbvu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wum'
+ },
+ {
+ name: 'Bungu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wun'
+ },
+ {
+ name: 'Wurrugu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wur'
+ },
+ {
+ name: 'Wutung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wut'
+ },
+ {
+ name: 'Wu Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wuu'
+ },
+ {
+ name: 'Wuvulu-Aua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wuv'
+ },
+ {
+ name: 'Wulna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wux'
+ },
+ {
+ name: 'Wauyai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wuy'
+ },
+ {
+ name: 'Waama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wwa'
+ },
+ {
+ name: 'Wakabunga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wwb'
+ },
+ {
+ name: 'Wetamut',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wwo'
+ },
+ {
+ name: 'Warrwa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wwr'
+ },
+ {
+ name: 'Wawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'www'
+ },
+ {
+ name: 'Waxianghua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wxa'
+ },
+ {
+ name: 'Wardandi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wxw'
+ },
+ {
+ name: 'Wyandot',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wya'
+ },
+ {
+ name: 'Wangaaybuwan-Ngiyambaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wyb'
+ },
+ {
+ name: 'Woiwurrung',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'wyi'
+ },
+ {
+ name: 'Wymysorys',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wym'
+ },
+ {
+ name: 'Wayoró',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wyr'
+ },
+ {
+ name: 'Western Fijian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'wyy'
+ },
+ {
+ name: 'Andalusian Arabic',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xaa'
+ },
+ {
+ name: 'Sambe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xab'
+ },
+ {
+ name: 'Kachari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xac'
+ },
+ {
+ name: 'Adai',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xad'
+ },
+ {
+ name: 'Aequian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xae'
+ },
+ {
+ name: 'Aghwan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xag'
+ },
+ {
+ name: 'Kaimbé',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xai'
+ },
+ {
+ name: 'Ararandewára',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xaj'
+ },
+ {
+ name: 'Máku',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xak'
+ },
+ {
+ name: 'Kalmyk',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xal',
+ iso6392B: 'xal',
+ iso6392T: 'xal'
+ },
+ {
+ name: 'ǀXam',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xam'
+ },
+ {
+ name: 'Xamtanga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xan'
+ },
+ {
+ name: 'Khao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xao'
+ },
+ {
+ name: 'Apalachee',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xap'
+ },
+ {
+ name: 'Aquitanian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xaq'
+ },
+ {
+ name: 'Karami',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xar'
+ },
+ {
+ name: 'Kamas',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xas'
+ },
+ {
+ name: 'Katawixi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xat'
+ },
+ {
+ name: 'Kauwera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xau'
+ },
+ {
+ name: 'Xavánte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xav'
+ },
+ {
+ name: 'Kawaiisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xaw'
+ },
+ {
+ name: 'Kayan Mahakam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xay'
+ },
+ {
+ name: 'Lower Burdekin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbb'
+ },
+ {
+ name: 'Bactrian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xbc'
+ },
+ {
+ name: 'Bindal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbd'
+ },
+ {
+ name: 'Bigambal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbe'
+ },
+ {
+ name: 'Bunganditj',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbg'
+ },
+ {
+ name: 'Kombio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xbi'
+ },
+ {
+ name: 'Birrpayi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbj'
+ },
+ {
+ name: 'Middle Breton',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xbm'
+ },
+ {
+ name: 'Kenaboi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbn'
+ },
+ {
+ name: 'Bolgarian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xbo'
+ },
+ {
+ name: 'Bibbulman',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbp'
+ },
+ {
+ name: 'Kambera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xbr'
+ },
+ {
+ name: 'Kambiwá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xbw'
+ },
+ {
+ name: 'Batjala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xby'
+ },
+ {
+ name: 'Cumbric',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xcb'
+ },
+ {
+ name: 'Camunic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xcc'
+ },
+ {
+ name: 'Celtiberian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xce'
+ },
+ {
+ name: 'Cisalpine Gaulish',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xcg'
+ },
+ {
+ name: 'Chemakum',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xch'
+ },
+ {
+ name: 'Classical Armenian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xcl'
+ },
+ {
+ name: 'Comecrudo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xcm'
+ },
+ {
+ name: 'Cotoname',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xcn'
+ },
+ {
+ name: 'Chorasmian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xco'
+ },
+ {
+ name: 'Carian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xcr'
+ },
+ {
+ name: 'Classical Tibetan',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xct'
+ },
+ {
+ name: 'Curonian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xcu'
+ },
+ {
+ name: 'Chuvantsy',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xcv'
+ },
+ {
+ name: 'Coahuilteco',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xcw'
+ },
+ {
+ name: 'Cayuse',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xcy'
+ },
+ {
+ name: 'Darkinyung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xda'
+ },
+ {
+ name: 'Dacian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xdc'
+ },
+ {
+ name: 'Dharuk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xdk'
+ },
+ {
+ name: 'Edomite',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xdm'
+ },
+ {
+ name: 'Kwandu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xdo'
+ },
+ {
+ name: 'Malayic Dayak',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xdy'
+ },
+ {
+ name: 'Eblan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xeb'
+ },
+ {
+ name: 'Hdi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xed'
+ },
+ {
+ name: 'ǁXegwi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xeg'
+ },
+ {
+ name: 'Kelo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xel'
+ },
+ {
+ name: 'Kembayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xem'
+ },
+ {
+ name: 'Epi-Olmec',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xep'
+ },
+ {
+ name: 'Xerénte',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xer'
+ },
+ {
+ name: 'Kesawai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xes'
+ },
+ {
+ name: 'Xetá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xet'
+ },
+ {
+ name: 'Keoru-Ahia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xeu'
+ },
+ {
+ name: 'Faliscan',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xfa'
+ },
+ {
+ name: 'Galatian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xga'
+ },
+ {
+ name: 'Gbin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgb'
+ },
+ {
+ name: 'Gudang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgd'
+ },
+ {
+ name: 'Gabrielino-Fernandeño',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgf'
+ },
+ {
+ name: 'Goreng',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgg'
+ },
+ {
+ name: 'Garingbal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgi'
+ },
+ {
+ name: 'Galindan',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xgl'
+ },
+ {
+ name: 'Dharumbal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgm'
+ },
+ {
+ name: 'Garza',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgr'
+ },
+ {
+ name: 'Unggumi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xgu'
+ },
+ {
+ name: 'Guwa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xgw'
+ },
+ {
+ name: 'Harami',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xha'
+ },
+ {
+ name: 'Hunnic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xhc'
+ },
+ {
+ name: 'Hadrami',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xhd'
+ },
+ {
+ name: 'Khetrani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xhe'
+ },
+ {
+ name: 'Xhosa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xho',
+ iso6392B: 'xho',
+ iso6392T: 'xho',
+ iso6391: 'xh'
+ },
+ {
+ name: 'Hernican',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xhr'
+ },
+ {
+ name: 'Hattic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xht'
+ },
+ {
+ name: 'Hurrian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xhu'
+ },
+ {
+ name: 'Khua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xhv'
+ },
+ {
+ name: 'Iberian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xib'
+ },
+ {
+ name: 'Xiri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xii'
+ },
+ {
+ name: 'Illyrian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xil'
+ },
+ {
+ name: 'Xinca',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xin'
+ },
+ {
+ name: 'Xiriâna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xir'
+ },
+ {
+ name: 'Kisan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xis'
+ },
+ {
+ name: 'Indus Valley Language',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xiv'
+ },
+ {
+ name: 'Xipaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xiy'
+ },
+ {
+ name: 'Minjungbal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xjb'
+ },
+ {
+ name: 'Jaitmatang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xjt'
+ },
+ {
+ name: 'Kalkoti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xka'
+ },
+ {
+ name: 'Northern Nago',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkb'
+ },
+ {
+ name: "Kho'ini",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkc'
+ },
+ {
+ name: 'Mendalam Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkd'
+ },
+ {
+ name: 'Kereho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xke'
+ },
+ {
+ name: 'Khengkha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkf'
+ },
+ {
+ name: 'Kagoro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkg'
+ },
+ {
+ name: 'Kenyan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xki'
+ },
+ {
+ name: 'Kajali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkj'
+ },
+ {
+ name: "Kaco'",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkk'
+ },
+ {
+ name: 'Mainstream Kenyah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkl'
+ },
+ {
+ name: 'Kayan River Kayan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkn'
+ },
+ {
+ name: 'Kiorr',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xko'
+ },
+ {
+ name: 'Kabatei',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkp'
+ },
+ {
+ name: 'Koroni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkq'
+ },
+ {
+ name: 'Xakriabá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xkr'
+ },
+ {
+ name: 'Kumbewaha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xks'
+ },
+ {
+ name: 'Kantosi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkt'
+ },
+ {
+ name: 'Kaamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xku'
+ },
+ {
+ name: 'Kgalagadi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkv'
+ },
+ {
+ name: 'Kembra',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkw'
+ },
+ {
+ name: 'Karore',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkx'
+ },
+ {
+ name: "Uma' Lasan",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xky'
+ },
+ {
+ name: 'Kurtokha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xkz'
+ },
+ {
+ name: 'Kamula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xla'
+ },
+ {
+ name: 'Loup B',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xlb'
+ },
+ {
+ name: 'Lycian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xlc'
+ },
+ {
+ name: 'Lydian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xld'
+ },
+ {
+ name: 'Lemnian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xle'
+ },
+ {
+ name: 'Ligurian (Ancient)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xlg'
+ },
+ {
+ name: 'Liburnian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xli'
+ },
+ {
+ name: 'Alanic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xln'
+ },
+ {
+ name: 'Loup A',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xlo'
+ },
+ {
+ name: 'Lepontic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xlp'
+ },
+ {
+ name: 'Lusitanian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xls'
+ },
+ {
+ name: 'Cuneiform Luwian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xlu'
+ },
+ {
+ name: 'Elymian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xly'
+ },
+ {
+ name: 'Mushungulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xma'
+ },
+ {
+ name: 'Mbonga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmb'
+ },
+ {
+ name: 'Makhuwa-Marrevone',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmc'
+ },
+ {
+ name: 'Mbudum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmd'
+ },
+ {
+ name: 'Median',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xme'
+ },
+ {
+ name: 'Mingrelian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmf'
+ },
+ {
+ name: 'Mengaka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmg'
+ },
+ {
+ name: 'Kugu-Muminh',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmh'
+ },
+ {
+ name: 'Majera',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmj'
+ },
+ {
+ name: 'Ancient Macedonian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xmk'
+ },
+ {
+ name: 'Malaysian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xml'
+ },
+ {
+ name: 'Manado Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmm'
+ },
+ {
+ name: 'Manichaean Middle Persian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xmn'
+ },
+ {
+ name: 'Morerebi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmo'
+ },
+ {
+ name: "Kuku-Mu'inh",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xmp'
+ },
+ {
+ name: 'Kuku-Mangk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xmq'
+ },
+ {
+ name: 'Meroitic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xmr'
+ },
+ {
+ name: 'Moroccan Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xms'
+ },
+ {
+ name: 'Matbat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmt'
+ },
+ {
+ name: 'Kamu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xmu'
+ },
+ {
+ name: 'Antankarana Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmv'
+ },
+ {
+ name: 'Tsimihety Malagasy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmw'
+ },
+ {
+ name: 'Maden',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmx'
+ },
+ {
+ name: 'Mayaguduna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmy'
+ },
+ {
+ name: 'Mori Bawah',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xmz'
+ },
+ {
+ name: 'Ancient North Arabian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xna'
+ },
+ {
+ name: 'Kanakanabu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xnb'
+ },
+ {
+ name: 'Middle Mongolian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xng'
+ },
+ {
+ name: 'Kuanhua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xnh'
+ },
+ {
+ name: 'Ngarigu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xni'
+ },
+ {
+ name: 'Nganakarti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xnk'
+ },
+ {
+ name: 'Ngumbarl',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xnm'
+ },
+ {
+ name: 'Northern Kankanay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xnn'
+ },
+ {
+ name: 'Anglo-Norman',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xno'
+ },
+ {
+ name: 'Kangri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xnr'
+ },
+ {
+ name: 'Kanashi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xns'
+ },
+ {
+ name: 'Narragansett',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xnt'
+ },
+ {
+ name: 'Nukunul',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xnu'
+ },
+ {
+ name: 'Nyiyaparli',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xny'
+ },
+ {
+ name: 'Kenzi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xnz'
+ },
+ {
+ name: "O'chi'chi'",
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xoc'
+ },
+ {
+ name: 'Kokoda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xod'
+ },
+ {
+ name: 'Soga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xog'
+ },
+ {
+ name: 'Kominimung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xoi'
+ },
+ {
+ name: 'Xokleng',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xok'
+ },
+ {
+ name: 'Komo (Sudan)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xom'
+ },
+ {
+ name: 'Konkomba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xon'
+ },
+ {
+ name: 'Xukurú',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xoo'
+ },
+ {
+ name: 'Kopar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xop'
+ },
+ {
+ name: 'Korubo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xor'
+ },
+ {
+ name: 'Kowaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xow'
+ },
+ {
+ name: 'Pirriya',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpa'
+ },
+ {
+ name: 'Northeastern Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpb'
+ },
+ {
+ name: 'Pecheneg',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xpc'
+ },
+ {
+ name: 'Oyster Bay Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpd'
+ },
+ {
+ name: 'Liberia Kpelle',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xpe'
+ },
+ {
+ name: 'Southeast Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpf'
+ },
+ {
+ name: 'Phrygian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xpg'
+ },
+ {
+ name: 'North Midlands Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xph'
+ },
+ {
+ name: 'Pictish',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xpi'
+ },
+ {
+ name: 'Mpalitjanh',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpj'
+ },
+ {
+ name: 'Kulina Pano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xpk'
+ },
+ {
+ name: 'Port Sorell Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpl'
+ },
+ {
+ name: 'Pumpokol',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpm'
+ },
+ {
+ name: 'Kapinawá',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpn'
+ },
+ {
+ name: 'Pochutec',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpo'
+ },
+ {
+ name: 'Puyo-Paekche',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xpp'
+ },
+ {
+ name: 'Mohegan-Pequot',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpq'
+ },
+ {
+ name: 'Parthian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xpr'
+ },
+ {
+ name: 'Pisidian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xps'
+ },
+ {
+ name: 'Punthamara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpt'
+ },
+ {
+ name: 'Punic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xpu'
+ },
+ {
+ name: 'Northern Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpv'
+ },
+ {
+ name: 'Northwestern Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpw'
+ },
+ {
+ name: 'Southwestern Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpx'
+ },
+ {
+ name: 'Puyo',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xpy'
+ },
+ {
+ name: 'Bruny Island Tasmanian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xpz'
+ },
+ {
+ name: 'Karakhanid',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xqa'
+ },
+ {
+ name: 'Qatabanian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xqt'
+ },
+ {
+ name: 'Krahô',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xra'
+ },
+ {
+ name: 'Eastern Karaboro',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xrb'
+ },
+ {
+ name: 'Gundungurra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xrd'
+ },
+ {
+ name: 'Kreye',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xre'
+ },
+ {
+ name: 'Minang',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xrg'
+ },
+ {
+ name: 'Krikati-Timbira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xri'
+ },
+ {
+ name: 'Armazic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xrm'
+ },
+ {
+ name: 'Arin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xrn'
+ },
+ {
+ name: 'Raetic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xrr'
+ },
+ {
+ name: 'Aranama-Tamique',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xrt'
+ },
+ {
+ name: 'Marriammu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xru'
+ },
+ {
+ name: 'Karawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xrw'
+ },
+ {
+ name: 'Sabaean',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xsa'
+ },
+ {
+ name: 'Sambal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsb'
+ },
+ {
+ name: 'Scythian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xsc'
+ },
+ {
+ name: 'Sidetic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xsd'
+ },
+ {
+ name: 'Sempan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xse'
+ },
+ {
+ name: 'Shamang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsh'
+ },
+ {
+ name: 'Sio',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsi'
+ },
+ {
+ name: 'Subi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsj'
+ },
+ {
+ name: 'South Slavey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsl'
+ },
+ {
+ name: 'Kasem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsm'
+ },
+ {
+ name: 'Sanga (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsn'
+ },
+ {
+ name: 'Solano',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xso'
+ },
+ {
+ name: 'Silopi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsp'
+ },
+ {
+ name: 'Makhuwa-Saka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsq'
+ },
+ {
+ name: 'Sherpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsr'
+ },
+ {
+ name: 'Assan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xss'
+ },
+ {
+ name: 'Sanumá',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsu'
+ },
+ {
+ name: 'Sudovian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xsv'
+ },
+ {
+ name: 'Saisiyat',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xsy'
+ },
+ {
+ name: 'Alcozauca Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xta'
+ },
+ {
+ name: 'Chazumba Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtb'
+ },
+ {
+ name: 'Katcha-Kadugli-Miri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtc'
+ },
+ {
+ name: 'Diuxi-Tilantongo Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtd'
+ },
+ {
+ name: 'Ketengban',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xte'
+ },
+ {
+ name: 'Transalpine Gaulish',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xtg'
+ },
+ {
+ name: 'Yitha Yitha',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xth'
+ },
+ {
+ name: 'Sinicahua Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xti'
+ },
+ {
+ name: 'San Juan Teita Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtj'
+ },
+ {
+ name: 'Tijaltepec Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtl'
+ },
+ {
+ name: 'Magdalena Peñasco Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtm'
+ },
+ {
+ name: 'Northern Tlaxiaco Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtn'
+ },
+ {
+ name: 'Tokharian A',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xto'
+ },
+ {
+ name: 'San Miguel Piedras Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtp'
+ },
+ {
+ name: 'Tumshuqese',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xtq'
+ },
+ {
+ name: 'Early Tripuri',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xtr'
+ },
+ {
+ name: 'Sindihui Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xts'
+ },
+ {
+ name: 'Tacahua Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtt'
+ },
+ {
+ name: 'Cuyamecalco Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtu'
+ },
+ {
+ name: 'Thawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xtv'
+ },
+ {
+ name: 'Tawandê',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xtw'
+ },
+ {
+ name: 'Yoloxochitl Mixtec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xty'
+ },
+ {
+ name: 'Alu Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xua'
+ },
+ {
+ name: 'Betta Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xub'
+ },
+ {
+ name: 'Umiida',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xud'
+ },
+ {
+ name: 'Kunigami',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xug'
+ },
+ {
+ name: 'Jennu Kurumba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xuj'
+ },
+ {
+ name: 'Ngunawal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xul'
+ },
+ {
+ name: 'Umbrian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xum'
+ },
+ {
+ name: 'Unggaranggu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xun'
+ },
+ {
+ name: 'Kuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xuo'
+ },
+ {
+ name: 'Upper Umpqua',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xup'
+ },
+ {
+ name: 'Urartian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xur'
+ },
+ {
+ name: 'Kuthant',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xut'
+ },
+ {
+ name: 'Kxoe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xuu'
+ },
+ {
+ name: 'Venetic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xve'
+ },
+ {
+ name: 'Kamviri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xvi'
+ },
+ {
+ name: 'Vandalic',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xvn'
+ },
+ {
+ name: 'Volscian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xvo'
+ },
+ {
+ name: 'Vestinian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xvs'
+ },
+ {
+ name: 'Kwaza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xwa'
+ },
+ {
+ name: 'Woccon',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwc'
+ },
+ {
+ name: 'Wadi Wadi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwd'
+ },
+ {
+ name: 'Xwela Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xwe'
+ },
+ {
+ name: 'Kwegu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xwg'
+ },
+ {
+ name: 'Wajuk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwj'
+ },
+ {
+ name: 'Wangkumara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwk'
+ },
+ {
+ name: 'Western Xwla Gbe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xwl'
+ },
+ {
+ name: 'Written Oirat',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwo'
+ },
+ {
+ name: 'Kwerba Mamberamo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xwr'
+ },
+ {
+ name: 'Wotjobaluk',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xwt'
+ },
+ {
+ name: 'Wemba Wemba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xww'
+ },
+ {
+ name: 'Boro (Ghana)',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xxb'
+ },
+ {
+ name: "Ke'o",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xxk'
+ },
+ {
+ name: 'Minkin',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xxm'
+ },
+ {
+ name: 'Koropó',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xxr'
+ },
+ {
+ name: 'Tambora',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xxt'
+ },
+ {
+ name: 'Yaygir',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xya'
+ },
+ {
+ name: 'Yandjibara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xyb'
+ },
+ {
+ name: 'Mayi-Yapi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xyj'
+ },
+ {
+ name: 'Mayi-Kulan',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xyk'
+ },
+ {
+ name: 'Yalakalore',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xyl'
+ },
+ {
+ name: 'Mayi-Thakurti',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xyt'
+ },
+ {
+ name: 'Yorta Yorta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'xyy'
+ },
+ {
+ name: 'Zhang-Zhung',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'xzh'
+ },
+ {
+ name: 'Zemgalian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'xzm'
+ },
+ {
+ name: 'Ancient Zapotec',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'xzp'
+ },
+ {
+ name: 'Yaminahua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaa'
+ },
+ {
+ name: 'Yuhup',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yab'
+ },
+ {
+ name: 'Pass Valley Yali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yac'
+ },
+ {
+ name: 'Yagua',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yad'
+ },
+ {
+ name: 'Pumé',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yae'
+ },
+ {
+ name: 'Yaka (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaf'
+ },
+ {
+ name: 'Yámana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yag'
+ },
+ {
+ name: 'Yazgulyam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yah'
+ },
+ {
+ name: 'Yagnobi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yai'
+ },
+ {
+ name: 'Banda-Yangere',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaj'
+ },
+ {
+ name: 'Yakama',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yak'
+ },
+ {
+ name: 'Yalunka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yal'
+ },
+ {
+ name: 'Yamba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yam'
+ },
+ {
+ name: 'Mayangna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yan'
+ },
+ {
+ name: 'Yao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yao',
+ iso6392B: 'yao',
+ iso6392T: 'yao'
+ },
+ {
+ name: 'Yapese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yap',
+ iso6392B: 'yap',
+ iso6392T: 'yap'
+ },
+ {
+ name: 'Yaqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaq'
+ },
+ {
+ name: 'Yabarana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yar'
+ },
+ {
+ name: 'Nugunu (Cameroon)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yas'
+ },
+ {
+ name: 'Yambeta',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yat'
+ },
+ {
+ name: 'Yuwana',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yau'
+ },
+ {
+ name: 'Yangben',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yav'
+ },
+ {
+ name: 'Yawalapití',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaw'
+ },
+ {
+ name: 'Yauma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yax'
+ },
+ {
+ name: 'Agwagwune',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yay'
+ },
+ {
+ name: 'Lokaa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yaz'
+ },
+ {
+ name: 'Yala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yba'
+ },
+ {
+ name: 'Yemba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybb'
+ },
+ {
+ name: 'West Yugur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybe'
+ },
+ {
+ name: 'Yakha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybh'
+ },
+ {
+ name: 'Yamphu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybi'
+ },
+ {
+ name: 'Hasha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybj'
+ },
+ {
+ name: 'Bokha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybk'
+ },
+ {
+ name: 'Yukuben',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybl'
+ },
+ {
+ name: 'Yaben',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybm'
+ },
+ {
+ name: 'Yabaâna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ybn'
+ },
+ {
+ name: 'Yabong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybo'
+ },
+ {
+ name: 'Yawiyo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ybx'
+ },
+ {
+ name: 'Yaweyuha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yby'
+ },
+ {
+ name: 'Chesu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ych'
+ },
+ {
+ name: 'Lolopo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ycl'
+ },
+ {
+ name: 'Yucuna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ycn'
+ },
+ {
+ name: 'Chepya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ycp'
+ },
+ {
+ name: 'Yanda',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yda'
+ },
+ {
+ name: 'Eastern Yiddish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ydd'
+ },
+ {
+ name: 'Yangum Dey',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yde'
+ },
+ {
+ name: 'Yidgha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ydg'
+ },
+ {
+ name: 'Yoidik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ydk'
+ },
+ {
+ name: 'Ravula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yea'
+ },
+ {
+ name: 'Yeniche',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yec'
+ },
+ {
+ name: 'Yimas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yee'
+ },
+ {
+ name: 'Yeni',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yei'
+ },
+ {
+ name: 'Yevanic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yej'
+ },
+ {
+ name: 'Yela',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yel'
+ },
+ {
+ name: 'Tarok',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yer'
+ },
+ {
+ name: 'Nyankpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yes'
+ },
+ {
+ name: 'Yetfa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yet'
+ },
+ {
+ name: 'Yerukula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yeu'
+ },
+ {
+ name: 'Yapunda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yev'
+ },
+ {
+ name: 'Yeyi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yey'
+ },
+ {
+ name: 'Malyangapa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yga'
+ },
+ {
+ name: 'Yiningayi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ygi'
+ },
+ {
+ name: 'Yangum Gel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygl'
+ },
+ {
+ name: 'Yagomi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygm'
+ },
+ {
+ name: 'Gepo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygp'
+ },
+ {
+ name: 'Yagaria',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygr'
+ },
+ {
+ name: 'Yolŋu Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygs'
+ },
+ {
+ name: 'Yugul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygu'
+ },
+ {
+ name: 'Yagwoia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ygw'
+ },
+ {
+ name: 'Baha Buyang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yha'
+ },
+ {
+ name: 'Judeo-Iraqi Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yhd'
+ },
+ {
+ name: 'Hlepho Phowa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yhl'
+ },
+ {
+ name: 'Yan-nhaŋu Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yhs'
+ },
+ {
+ name: 'Yinggarda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yia'
+ },
+ {
+ name: 'Yiddish',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'yid',
+ iso6392B: 'yid',
+ iso6392T: 'yid',
+ iso6391: 'yi'
+ },
+ {
+ name: 'Ache',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yif'
+ },
+ {
+ name: 'Wusa Nasu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yig'
+ },
+ {
+ name: 'Western Yiddish',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yih'
+ },
+ {
+ name: 'Yidiny',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yii'
+ },
+ {
+ name: 'Yindjibarndi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yij'
+ },
+ {
+ name: 'Dongshanba Lalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yik'
+ },
+ {
+ name: 'Yindjilandji',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yil'
+ },
+ {
+ name: 'Yimchungru Naga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yim'
+ },
+ {
+ name: 'Riang Lai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yin'
+ },
+ {
+ name: 'Pholo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yip'
+ },
+ {
+ name: 'Miqie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yiq'
+ },
+ {
+ name: 'North Awyu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yir'
+ },
+ {
+ name: 'Yis',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yis'
+ },
+ {
+ name: 'Eastern Lalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yit'
+ },
+ {
+ name: 'Awu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yiu'
+ },
+ {
+ name: 'Northern Nisu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yiv'
+ },
+ {
+ name: 'Axi Yi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yix'
+ },
+ {
+ name: 'Azhe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yiz'
+ },
+ {
+ name: 'Yakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yka'
+ },
+ {
+ name: 'Northern Yukaghir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykg'
+ },
+ {
+ name: 'Yoke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yki'
+ },
+ {
+ name: 'Yakaikeke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykk'
+ },
+ {
+ name: 'Khlula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykl'
+ },
+ {
+ name: 'Kap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykm'
+ },
+ {
+ name: 'Kua-nsi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykn'
+ },
+ {
+ name: 'Yasa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yko'
+ },
+ {
+ name: 'Yekora',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykr'
+ },
+ {
+ name: 'Kathu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ykt'
+ },
+ {
+ name: 'Kuamasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yku'
+ },
+ {
+ name: 'Yakoma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yky'
+ },
+ {
+ name: 'Yaul',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yla'
+ },
+ {
+ name: 'Yaleba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ylb'
+ },
+ {
+ name: 'Yele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yle'
+ },
+ {
+ name: 'Yelogu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ylg'
+ },
+ {
+ name: 'Angguruk Yali',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yli'
+ },
+ {
+ name: 'Yil',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yll'
+ },
+ {
+ name: 'Limi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ylm'
+ },
+ {
+ name: 'Langnian Buyang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yln'
+ },
+ {
+ name: 'Naluo Yi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ylo'
+ },
+ {
+ name: 'Yalarnnga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ylr'
+ },
+ {
+ name: 'Aribwaung',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ylu'
+ },
+ {
+ name: 'Nyâlayu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yly'
+ },
+ {
+ name: 'Yambes',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymb'
+ },
+ {
+ name: 'Southern Muji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymc'
+ },
+ {
+ name: 'Muda',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymd'
+ },
+ {
+ name: 'Yameo',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yme'
+ },
+ {
+ name: 'Yamongeri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymg'
+ },
+ {
+ name: 'Mili',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymh'
+ },
+ {
+ name: 'Moji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymi'
+ },
+ {
+ name: 'Makwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymk'
+ },
+ {
+ name: 'Iamalele',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yml'
+ },
+ {
+ name: 'Maay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymm'
+ },
+ {
+ name: 'Yamna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymn'
+ },
+ {
+ name: 'Yangum Mon',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymo'
+ },
+ {
+ name: 'Yamap',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymp'
+ },
+ {
+ name: 'Qila Muji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymq'
+ },
+ {
+ name: 'Malasar',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymr'
+ },
+ {
+ name: 'Mysian',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'yms'
+ },
+ {
+ name: 'Northern Muji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymx'
+ },
+ {
+ name: 'Muzi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ymz'
+ },
+ {
+ name: 'Aluo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yna'
+ },
+ {
+ name: 'Yandruwandha',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ynd'
+ },
+ {
+ name: "Lang'e",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yne'
+ },
+ {
+ name: 'Yango',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yng'
+ },
+ {
+ name: 'Naukan Yupik',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ynk'
+ },
+ {
+ name: 'Yangulam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ynl'
+ },
+ {
+ name: 'Yana',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ynn'
+ },
+ {
+ name: 'Yong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yno'
+ },
+ {
+ name: 'Yendang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ynq'
+ },
+ {
+ name: 'Yansi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yns'
+ },
+ {
+ name: 'Yahuna',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ynu'
+ },
+ {
+ name: 'Yoba',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yob'
+ },
+ {
+ name: 'Yogad',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yog'
+ },
+ {
+ name: 'Yonaguni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yoi'
+ },
+ {
+ name: 'Yokuts',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yok'
+ },
+ {
+ name: 'Yola',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yol'
+ },
+ {
+ name: 'Yombe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yom'
+ },
+ {
+ name: 'Yongkom',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yon'
+ },
+ {
+ name: 'Yoruba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yor',
+ iso6392B: 'yor',
+ iso6392T: 'yor',
+ iso6391: 'yo'
+ },
+ {
+ name: 'Yotti',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yot'
+ },
+ {
+ name: 'Yoron',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yox'
+ },
+ {
+ name: 'Yoy',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yoy'
+ },
+ {
+ name: 'Phala',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypa'
+ },
+ {
+ name: 'Labo Phowa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypb'
+ },
+ {
+ name: 'Phola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypg'
+ },
+ {
+ name: 'Phupha',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yph'
+ },
+ {
+ name: 'Phuma',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypm'
+ },
+ {
+ name: 'Ani Phowa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypn'
+ },
+ {
+ name: 'Alo Phola',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypo'
+ },
+ {
+ name: 'Phupa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypp'
+ },
+ {
+ name: 'Phuza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ypz'
+ },
+ {
+ name: 'Yerakai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yra'
+ },
+ {
+ name: 'Yareba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrb'
+ },
+ {
+ name: 'Yaouré',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yre'
+ },
+ {
+ name: 'Nenets',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrk'
+ },
+ {
+ name: 'Nhengatu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrl'
+ },
+ {
+ name: 'Yirrk-Mel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrm'
+ },
+ {
+ name: 'Yerong',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrn'
+ },
+ {
+ name: 'Yaroamë',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yro'
+ },
+ {
+ name: 'Yarsun',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrs'
+ },
+ {
+ name: 'Yarawata',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yrw'
+ },
+ {
+ name: 'Yarluyandi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yry'
+ },
+ {
+ name: 'Yassic',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ysc'
+ },
+ {
+ name: 'Samatao',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysd'
+ },
+ {
+ name: 'Sonaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysg'
+ },
+ {
+ name: 'Yugoslavian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysl'
+ },
+ {
+ name: 'Sani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysn'
+ },
+ {
+ name: 'Nisi (China)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yso'
+ },
+ {
+ name: 'Southern Lolopo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysp'
+ },
+ {
+ name: 'Sirenik Yupik',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'ysr'
+ },
+ {
+ name: 'Yessan-Mayo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yss'
+ },
+ {
+ name: 'Sanie',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ysy'
+ },
+ {
+ name: 'Talu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yta'
+ },
+ {
+ name: 'Tanglang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ytl'
+ },
+ {
+ name: 'Thopho',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ytp'
+ },
+ {
+ name: 'Yout Wam',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ytw'
+ },
+ {
+ name: 'Yatay',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yty'
+ },
+ {
+ name: 'Yucateco',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yua'
+ },
+ {
+ name: 'Yugambal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yub'
+ },
+ {
+ name: 'Yuchi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuc'
+ },
+ {
+ name: 'Judeo-Tripolitanian Arabic',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yud'
+ },
+ {
+ name: 'Yue Chinese',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yue'
+ },
+ {
+ name: 'Havasupai-Walapai-Yavapai',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuf'
+ },
+ {
+ name: 'Yug',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yug'
+ },
+ {
+ name: 'Yurutí',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yui'
+ },
+ {
+ name: 'Karkar-Yuri',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuj'
+ },
+ {
+ name: 'Yuki',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yuk'
+ },
+ {
+ name: 'Yulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yul'
+ },
+ {
+ name: 'Quechan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yum'
+ },
+ {
+ name: 'Bena (Nigeria)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yun'
+ },
+ {
+ name: 'Yukpa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yup'
+ },
+ {
+ name: 'Yuqui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuq'
+ },
+ {
+ name: 'Yurok',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yur'
+ },
+ {
+ name: 'Yopno',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yut'
+ },
+ {
+ name: 'Yau (Morobe Province)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuw'
+ },
+ {
+ name: 'Southern Yukaghir',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yux'
+ },
+ {
+ name: 'East Yugur',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuy'
+ },
+ {
+ name: 'Yuracare',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yuz'
+ },
+ {
+ name: 'Yawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yva'
+ },
+ {
+ name: 'Yavitero',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yvt'
+ },
+ {
+ name: 'Kalou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywa'
+ },
+ {
+ name: 'Yinhawangka',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywg'
+ },
+ {
+ name: 'Western Lalu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywl'
+ },
+ {
+ name: 'Yawanawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywn'
+ },
+ {
+ name: 'Wuding-Luquan Yi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywq'
+ },
+ {
+ name: 'Yawuru',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywr'
+ },
+ {
+ name: 'Xishanba Lalo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywt'
+ },
+ {
+ name: 'Wumeng Nasu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ywu'
+ },
+ {
+ name: 'Yawarawarga',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yww'
+ },
+ {
+ name: 'Mayawali',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxa'
+ },
+ {
+ name: 'Yagara',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxg'
+ },
+ {
+ name: 'Yardliyawarra',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxl'
+ },
+ {
+ name: 'Yinwum',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxm'
+ },
+ {
+ name: 'Yuyu',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxu'
+ },
+ {
+ name: 'Yabula Yabula',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yxy'
+ },
+ {
+ name: 'Yir Yoront',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'yyr'
+ },
+ {
+ name: 'Yau (Sandaun Province)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yyu'
+ },
+ {
+ name: 'Ayizi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yyz'
+ },
+ {
+ name: "E'ma Buyang",
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yzg'
+ },
+ {
+ name: 'Zokhuo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'yzk'
+ },
+ {
+ name: 'Sierra de Juárez Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaa'
+ },
+ {
+ name: 'Western Tlacolula Valley Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zab'
+ },
+ {
+ name: 'Ocotlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zac'
+ },
+ {
+ name: 'Cajonos Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zad'
+ },
+ {
+ name: 'Yareni Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zae'
+ },
+ {
+ name: 'Ayoquesco Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaf'
+ },
+ {
+ name: 'Zaghawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zag'
+ },
+ {
+ name: 'Zangwal',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zah'
+ },
+ {
+ name: 'Isthmus Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zai'
+ },
+ {
+ name: 'Zaramo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaj'
+ },
+ {
+ name: 'Zanaki',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zak'
+ },
+ {
+ name: 'Zauzou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zal'
+ },
+ {
+ name: 'Miahuatlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zam'
+ },
+ {
+ name: 'Ozolotepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zao'
+ },
+ {
+ name: 'Zapotec',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'zap',
+ iso6392B: 'zap',
+ iso6392T: 'zap'
+ },
+ {
+ name: 'Aloápam Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaq'
+ },
+ {
+ name: 'Rincón Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zar'
+ },
+ {
+ name: 'Santo Domingo Albarradas Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zas'
+ },
+ {
+ name: 'Tabaa Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zat'
+ },
+ {
+ name: 'Zangskari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zau'
+ },
+ {
+ name: 'Yatzachi Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zav'
+ },
+ {
+ name: 'Mitla Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaw'
+ },
+ {
+ name: 'Xadani Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zax'
+ },
+ {
+ name: 'Zayse-Zergulla',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zay'
+ },
+ {
+ name: 'Zari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zaz'
+ },
+ {
+ name: 'Balaibalan',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'zba'
+ },
+ {
+ name: 'Central Berawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zbc'
+ },
+ {
+ name: 'East Berawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zbe'
+ },
+ {
+ name: 'Blissymbols',
+ type: 'constructed',
+ scope: 'individual',
+ iso6393: 'zbl',
+ iso6392B: 'zbl',
+ iso6392T: 'zbl'
+ },
+ {
+ name: 'Batui',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zbt'
+ },
+ {
+ name: 'West Berawan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zbw'
+ },
+ {
+ name: 'Coatecas Altas Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zca'
+ },
+ {
+ name: 'Central Hongshuihe Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zch'
+ },
+ {
+ name: 'Ngazidja Comorian',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zdj'
+ },
+ {
+ name: 'Zeeuws',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zea'
+ },
+ {
+ name: 'Zenag',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zeg'
+ },
+ {
+ name: 'Eastern Hongshuihe Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zeh'
+ },
+ {
+ name: 'Zenaga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zen',
+ iso6392B: 'zen',
+ iso6392T: 'zen'
+ },
+ {
+ name: 'Kinga',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zga'
+ },
+ {
+ name: 'Guibei Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zgb'
+ },
+ {
+ name: 'Standard Moroccan Tamazight',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zgh',
+ iso6392B: 'zgh',
+ iso6392T: 'zgh'
+ },
+ {
+ name: 'Minz Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zgm'
+ },
+ {
+ name: 'Guibian Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zgn'
+ },
+ {
+ name: 'Magori',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zgr'
+ },
+ {
+ name: 'Zhuang',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'zha',
+ iso6392B: 'zha',
+ iso6392T: 'zha',
+ iso6391: 'za'
+ },
+ {
+ name: 'Zhaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zhb'
+ },
+ {
+ name: 'Dai Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zhd'
+ },
+ {
+ name: 'Zhire',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zhi'
+ },
+ {
+ name: 'Nong Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zhn'
+ },
+ {
+ name: 'Chinese',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'zho',
+ iso6392B: 'chi',
+ iso6392T: 'zho',
+ iso6391: 'zh'
+ },
+ {
+ name: 'Zhoa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zhw'
+ },
+ {
+ name: 'Zia',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zia'
+ },
+ {
+ name: 'Zimbabwe Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zib'
+ },
+ {
+ name: 'Zimakani',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zik'
+ },
+ {
+ name: 'Zialo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zil'
+ },
+ {
+ name: 'Mesme',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zim'
+ },
+ {
+ name: 'Zinza',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zin'
+ },
+ {
+ name: 'Zigula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ziw'
+ },
+ {
+ name: 'Zizilivakan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ziz'
+ },
+ {
+ name: 'Kaimbulawa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zka'
+ },
+ {
+ name: 'Koibal',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zkb'
+ },
+ {
+ name: 'Kadu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zkd'
+ },
+ {
+ name: 'Koguryo',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'zkg'
+ },
+ {
+ name: 'Khorezmian',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'zkh'
+ },
+ {
+ name: 'Karankawa',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zkk'
+ },
+ {
+ name: 'Kanan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zkn'
+ },
+ {
+ name: 'Kott',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zko'
+ },
+ {
+ name: 'São Paulo Kaingáng',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zkp'
+ },
+ {
+ name: 'Zakhring',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zkr'
+ },
+ {
+ name: 'Kitan',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'zkt'
+ },
+ {
+ name: 'Kaurna',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zku'
+ },
+ {
+ name: 'Krevinian',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zkv'
+ },
+ {
+ name: 'Khazar',
+ type: 'historical',
+ scope: 'individual',
+ iso6393: 'zkz'
+ },
+ {
+ name: 'Liujiang Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zlj'
+ },
+ {
+ name: 'Malay (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zlm'
+ },
+ {
+ name: 'Lianshan Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zln'
+ },
+ {
+ name: 'Liuqian Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zlq'
+ },
+ {
+ name: 'Manda (Australia)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zma'
+ },
+ {
+ name: 'Zimba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmb'
+ },
+ {
+ name: 'Margany',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zmc'
+ },
+ {
+ name: 'Maridan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmd'
+ },
+ {
+ name: 'Mangerr',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zme'
+ },
+ {
+ name: 'Mfinu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmf'
+ },
+ {
+ name: 'Marti Ke',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmg'
+ },
+ {
+ name: 'Makolkol',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zmh'
+ },
+ {
+ name: 'Negeri Sembilan Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmi'
+ },
+ {
+ name: 'Maridjabin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmj'
+ },
+ {
+ name: 'Mandandanyi',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zmk'
+ },
+ {
+ name: 'Matngala',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zml'
+ },
+ {
+ name: 'Marimanindji',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmm'
+ },
+ {
+ name: 'Mbangwe',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmn'
+ },
+ {
+ name: 'Molo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmo'
+ },
+ {
+ name: 'Mpuono',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmp'
+ },
+ {
+ name: 'Mituku',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmq'
+ },
+ {
+ name: 'Maranunggu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmr'
+ },
+ {
+ name: 'Mbesa',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zms'
+ },
+ {
+ name: 'Maringarr',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmt'
+ },
+ {
+ name: 'Muruwari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zmu'
+ },
+ {
+ name: 'Mbariman-Gudhinma',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zmv'
+ },
+ {
+ name: 'Mbo (Democratic Republic of Congo)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmw'
+ },
+ {
+ name: 'Bomitaba',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmx'
+ },
+ {
+ name: 'Mariyedi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmy'
+ },
+ {
+ name: 'Mbandja',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zmz'
+ },
+ {
+ name: 'Zan Gula',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zna'
+ },
+ {
+ name: 'Zande (individual language)',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zne'
+ },
+ {
+ name: 'Mang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zng'
+ },
+ {
+ name: 'Manangkari',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'znk'
+ },
+ {
+ name: 'Mangas',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zns'
+ },
+ {
+ name: 'Copainalá Zoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zoc'
+ },
+ {
+ name: 'Chimalapa Zoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zoh'
+ },
+ {
+ name: 'Zou',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zom'
+ },
+ {
+ name: 'Asunción Mixtepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zoo'
+ },
+ {
+ name: 'Tabasco Zoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zoq'
+ },
+ {
+ name: 'Rayón Zoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zor'
+ },
+ {
+ name: 'Francisco León Zoque',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zos'
+ },
+ {
+ name: 'Lachiguiri Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpa'
+ },
+ {
+ name: 'Yautepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpb'
+ },
+ {
+ name: 'Choapan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpc'
+ },
+ {
+ name: 'Southeastern Ixtlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpd'
+ },
+ {
+ name: 'Petapa Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpe'
+ },
+ {
+ name: 'San Pedro Quiatoni Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpf'
+ },
+ {
+ name: 'Guevea De Humboldt Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpg'
+ },
+ {
+ name: 'Totomachapan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zph'
+ },
+ {
+ name: 'Santa María Quiegolani Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpi'
+ },
+ {
+ name: 'Quiavicuzas Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpj'
+ },
+ {
+ name: 'Tlacolulita Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpk'
+ },
+ {
+ name: 'Lachixío Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpl'
+ },
+ {
+ name: 'Mixtepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpm'
+ },
+ {
+ name: 'Santa Inés Yatzechi Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpn'
+ },
+ {
+ name: 'Amatlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpo'
+ },
+ {
+ name: 'El Alto Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpp'
+ },
+ {
+ name: 'Zoogocho Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpq'
+ },
+ {
+ name: 'Santiago Xanica Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpr'
+ },
+ {
+ name: 'Coatlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zps'
+ },
+ {
+ name: 'San Vicente Coatlán Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpt'
+ },
+ {
+ name: 'Yalálag Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpu'
+ },
+ {
+ name: 'Chichicapan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpv'
+ },
+ {
+ name: 'Zaniza Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpw'
+ },
+ {
+ name: 'San Baltazar Loxicha Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpx'
+ },
+ {
+ name: 'Mazaltepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpy'
+ },
+ {
+ name: 'Texmelucan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zpz'
+ },
+ {
+ name: 'Qiubei Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zqe'
+ },
+ {
+ name: 'Kara (Korea)',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'zra'
+ },
+ {
+ name: 'Mirgan',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zrg'
+ },
+ {
+ name: 'Zerenkel',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zrn'
+ },
+ {
+ name: 'Záparo',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zro'
+ },
+ {
+ name: 'Zarphatic',
+ type: 'extinct',
+ scope: 'individual',
+ iso6393: 'zrp'
+ },
+ {
+ name: 'Mairasi',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zrs'
+ },
+ {
+ name: 'Sarasira',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zsa'
+ },
+ {
+ name: 'Kaskean',
+ type: 'ancient',
+ scope: 'individual',
+ iso6393: 'zsk'
+ },
+ {
+ name: 'Zambian Sign Language',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zsl'
+ },
+ {
+ name: 'Standard Malay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zsm'
+ },
+ {
+ name: 'Southern Rincon Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zsr'
+ },
+ {
+ name: 'Sukurum',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zsu'
+ },
+ {
+ name: 'Elotepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zte'
+ },
+ {
+ name: 'Xanaguía Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztg'
+ },
+ {
+ name: 'Lapaguía-Guivini Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztl'
+ },
+ {
+ name: 'San Agustín Mixtepec Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztm'
+ },
+ {
+ name: 'Santa Catarina Albarradas Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztn'
+ },
+ {
+ name: 'Loxicha Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztp'
+ },
+ {
+ name: 'Quioquitani-Quierí Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztq'
+ },
+ {
+ name: 'Tilquiapan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zts'
+ },
+ {
+ name: 'Tejalapan Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztt'
+ },
+ {
+ name: 'Güilá Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztu'
+ },
+ {
+ name: 'Zaachila Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'ztx'
+ },
+ {
+ name: 'Yatee Zapotec',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zty'
+ },
+ {
+ name: 'Zeem',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zua'
+ },
+ {
+ name: 'Tokano',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zuh'
+ },
+ {
+ name: 'Zulu',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zul',
+ iso6392B: 'zul',
+ iso6392T: 'zul',
+ iso6391: 'zu'
+ },
+ {
+ name: 'Kumzari',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zum'
+ },
+ {
+ name: 'Zuni',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zun',
+ iso6392B: 'zun',
+ iso6392T: 'zun'
+ },
+ {
+ name: 'Zumaya',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zuy'
+ },
+ {
+ name: 'Zay',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zwa'
+ },
+ {
+ name: 'No linguistic content',
+ type: 'special',
+ scope: 'special',
+ iso6393: 'zxx',
+ iso6392B: 'zxx',
+ iso6392T: 'zxx'
+ },
+ {
+ name: 'Yongbei Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zyb'
+ },
+ {
+ name: 'Yang Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zyg'
+ },
+ {
+ name: 'Youjiang Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zyj'
+ },
+ {
+ name: 'Yongnan Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zyn'
+ },
+ {
+ name: 'Zyphe Chin',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zyp'
+ },
+ {
+ name: 'Zaza',
+ type: 'living',
+ scope: 'macrolanguage',
+ iso6393: 'zza',
+ iso6392B: 'zza',
+ iso6392T: 'zza'
+ },
+ {
+ name: 'Zuojiang Zhuang',
+ type: 'living',
+ scope: 'individual',
+ iso6393: 'zzj'
+ }
+]
diff --git a/packages/lang/src/lang/index.ts b/packages/lang/src/lang/index.ts
new file mode 100644
index 0000000..f566672
--- /dev/null
+++ b/packages/lang/src/lang/index.ts
@@ -0,0 +1,11 @@
+import { franc, francAll as _ } from "franc-all";
+import { iso6393To1 } from "../iso/iso6393-to-1";
+// TODO add https://github.com/wooorm/iso-3166?
+async function detectLang(text: string) {
+ // FrancAll return [langCode, confidence float]
+ const iso3 = franc(text);
+ const iso1 = iso6393To1[iso3];
+ return iso1 ? iso1 : iso3;
+}
+
+export { detectLang };
diff --git a/packages/lang/src/types.ts b/packages/lang/src/types.ts
new file mode 100644
index 0000000..57e46b4
--- /dev/null
+++ b/packages/lang/src/types.ts
@@ -0,0 +1,50 @@
+// * Category of a language:
+// *
+// * * `'living'`
+// * — currently spoken language
+// * (example: `nhi` for `Zacatlán-Ahuacatlán-Tepetzintla Nahuatl`)
+// * * `'historical'`
+// * — extinct language distinct from modern languages that descended from it
+// * (example: `ofs` for `Old Frisian`)
+// * * `'extinct'`
+// * — language that went extinct recently
+// * (example: `rbp` for `Barababaraba`)
+// * * `'ancient'`
+// * — language that went extinct long ago
+// * (example: `got` for `Gothic`)
+// * * `'constructed'`
+// * — artificial languages, excluding programming languages
+// * (example: `epo` for `Esperanto`)
+// * * `'special'`
+// * — non-language codes
+// * (example: `und` for `Undetermined`)
+// *
+// * Scope of a language:
+// *
+// * * `'individual'`
+// * — normal, single language
+// * (example: `eng` for `English`)
+// * * `'macrolanguage'`
+// * — one-to-many grouping of languages, because older ISO 639s included them
+// * (example: `ara` for `Arabic`)
+// * * `'special'`
+// * — non-language codes
+// * (example: `und` for `Undetermined`)
+// *
+
+type ISO_6393_3_TYPE = string;
+export type ISO_6393_3 = {
+ // Script name.
+ name: string;
+ // Four character ISO 15924 code.
+ type: ISO_6393_3_TYPE;
+ // // * Three character ISO 15924 code.
+ scope: "individual" | "macrolanguage" | "special";
+ // * Property value alias.
+ iso6393: string;
+ // * ISO 639-2 (bibliographic) code (example: `'eng'`).
+ iso6392B?: string;
+ // * ISO 639-2 (terminologic) code (example: `'eng'`).
+ iso6392T?: string;
+ iso6391?: string;
+};
diff --git a/packages/lang/src/unicode/index.ts b/packages/lang/src/unicode/index.ts
new file mode 100644
index 0000000..bf8821d
--- /dev/null
+++ b/packages/lang/src/unicode/index.ts
@@ -0,0 +1,1424 @@
+// Author: Amir Hossein Kargaran (TypeScript port)
+// Date: March, 2025
+
+import { ISO_15924_CODE } from "../iso";
+
+// Description: This code detects/separates the script(s) (writing system(s)) of the given text.
+// TypeScript port of the original Python implementation.
+
+// Types
+type ScriptRange = [number, number];
+type ScriptRanges = Record<string, ScriptRange[]>;
+
+type ScoredScript = [ISO_15924_CODE | null, number, ScriptDetails];
+
+interface ScriptDetails {
+ details: Record<ISO_15924_CODE, number> | null;
+ tie: boolean | null;
+ interval: number | null;
+}
+
+// The SCRIPT_RANGES object contains Unicode ranges for different scripts
+export const SCRIPT_RANGES: ScriptRanges = {
+ Latn: [
+ [65, 90],
+ [97, 122],
+ [170, 170],
+ [186, 186],
+ [192, 214],
+ [216, 246],
+ [248, 696],
+ [736, 740],
+ [7424, 7461],
+ [7468, 7516],
+ [7522, 7525],
+ [7531, 7543],
+ [7545, 7614],
+ [7680, 7935],
+ [8305, 8305],
+ [8319, 8319],
+ [8336, 8348],
+ [8490, 8491],
+ [8498, 8498],
+ [8526, 8526],
+ [8544, 8584],
+ [11360, 11391],
+ [42786, 42887],
+ [42891, 42954],
+ [42960, 42961],
+ [42963, 42963],
+ [42965, 42969],
+ [42994, 43007],
+ [43824, 43866],
+ [43868, 43876],
+ [43878, 43881],
+ [64256, 64262],
+ [65313, 65338],
+ [65345, 65370],
+ [67456, 67461],
+ [67463, 67504],
+ [67506, 67514],
+ [122624, 122654],
+ [122661, 122666],
+ ], // Latin
+ Bopo: [
+ [746, 747],
+ [12549, 12591],
+ [12704, 12735],
+ ], // Bopomofo
+ Zinh: [
+ [768, 879],
+ [1157, 1158],
+ [2385, 2388],
+ [6832, 6862],
+ [7376, 7378],
+ [7380, 7392],
+ [7394, 7400],
+ [7405, 7405],
+ [7412, 7412],
+ [7416, 7417],
+ [7616, 7679],
+ [8204, 8205],
+ [8400, 8432],
+ [12330, 12333],
+ [12441, 12442],
+ [65024, 65039],
+ [65056, 65069],
+ [66045, 66045],
+ [66272, 66272],
+ [70459, 70459],
+ [118528, 118573],
+ [118576, 118598],
+ [119143, 119145],
+ [119163, 119170],
+ [119173, 119179],
+ [119210, 119213],
+ [917760, 917999],
+ ], // Inherited
+ Grek: [
+ [880, 883],
+ [885, 887],
+ [890, 893],
+ [895, 895],
+ [900, 900],
+ [902, 902],
+ [904, 906],
+ [908, 908],
+ [910, 929],
+ [931, 993],
+ [1008, 1023],
+ [7462, 7466],
+ [7517, 7521],
+ [7526, 7530],
+ [7615, 7615],
+ [7936, 7957],
+ [7960, 7965],
+ [7968, 8005],
+ [8008, 8013],
+ [8016, 8023],
+ [8025, 8025],
+ [8027, 8027],
+ [8029, 8029],
+ [8031, 8061],
+ [8064, 8116],
+ [8118, 8132],
+ [8134, 8147],
+ [8150, 8155],
+ [8157, 8175],
+ [8178, 8180],
+ [8182, 8190],
+ [8486, 8486],
+ [43877, 43877],
+ [65856, 65934],
+ [65952, 65952],
+ [119296, 119365],
+ ], // Greek
+ Copt: [
+ [994, 1007],
+ [11392, 11507],
+ [11513, 11519],
+ ], // Coptic
+ Cyrl: [
+ [1024, 1156],
+ [1159, 1327],
+ [7296, 7304],
+ [7467, 7467],
+ [7544, 7544],
+ [11744, 11775],
+ [42560, 42655],
+ [65070, 65071],
+ [122928, 122989],
+ [123023, 123023],
+ ], // Cyrillic
+ Armn: [
+ [1329, 1366],
+ [1369, 1418],
+ [1421, 1423],
+ [64275, 64279],
+ ], // Armenian
+ Hebr: [
+ [1425, 1479],
+ [1488, 1514],
+ [1519, 1524],
+ [64285, 64310],
+ [64312, 64316],
+ [64318, 64318],
+ [64320, 64321],
+ [64323, 64324],
+ [64326, 64335],
+ ], // Hebrew
+ Arab: [
+ [1536, 1540],
+ [1542, 1547],
+ [1549, 1562],
+ [1564, 1566],
+ [1568, 1599],
+ [1601, 1648],
+ [1649, 1756],
+ [1758, 1791],
+ [1872, 1919],
+ [2160, 2190],
+ [2192, 2193],
+ [2200, 2273],
+ [2275, 2303],
+ [64336, 64450],
+ [64467, 64829],
+ [64832, 64911],
+ [64914, 64967],
+ [64975, 64975],
+ [65008, 65023],
+ [65136, 65140],
+ [65142, 65276],
+ [69216, 69246],
+ [69373, 69375],
+ [126464, 126467],
+ [126469, 126495],
+ [126497, 126498],
+ [126500, 126500],
+ [126503, 126503],
+ [126505, 126514],
+ [126516, 126519],
+ [126521, 126521],
+ [126523, 126523],
+ [126530, 126530],
+ [126535, 126535],
+ [126537, 126537],
+ [126539, 126539],
+ [126541, 126543],
+ [126545, 126546],
+ [126548, 126548],
+ [126551, 126551],
+ [126553, 126553],
+ [126555, 126555],
+ [126557, 126557],
+ [126559, 126559],
+ [126561, 126562],
+ [126564, 126564],
+ [126567, 126570],
+ [126572, 126578],
+ [126580, 126583],
+ [126585, 126588],
+ [126590, 126590],
+ [126592, 126601],
+ [126603, 126619],
+ [126625, 126627],
+ [126629, 126633],
+ [126635, 126651],
+ [126704, 126705],
+ ], // Arabic
+ Syrc: [
+ [1792, 1805],
+ [1807, 1866],
+ [1869, 1871],
+ [2144, 2154],
+ ], // Syriac
+ Thaa: [[1920, 1969]], // Thaana
+ Nkoo: [
+ [1984, 2042],
+ [2045, 2047],
+ ], // Nko
+ Samr: [
+ [2048, 2093],
+ [2096, 2110],
+ ], // Samaritan
+ Mand: [
+ [2112, 2139],
+ [2142, 2142],
+ ], // Mandaic
+ Deva: [
+ [2304, 2384],
+ [2389, 2403],
+ [2406, 2431],
+ [43232, 43263],
+ [72448, 72457],
+ ], // Devanagari
+ Beng: [
+ [2432, 2435],
+ [2437, 2444],
+ [2447, 2448],
+ [2451, 2472],
+ [2474, 2480],
+ [2482, 2482],
+ [2486, 2489],
+ [2492, 2500],
+ [2503, 2504],
+ [2507, 2510],
+ [2519, 2519],
+ [2524, 2525],
+ [2527, 2531],
+ [2534, 2558],
+ ], // Bengali
+ Guru: [
+ [2561, 2563],
+ [2565, 2570],
+ [2575, 2576],
+ [2579, 2600],
+ [2602, 2608],
+ [2610, 2611],
+ [2613, 2614],
+ [2616, 2617],
+ [2620, 2620],
+ [2622, 2626],
+ [2631, 2632],
+ [2635, 2637],
+ [2641, 2641],
+ [2649, 2652],
+ [2654, 2654],
+ [2662, 2678],
+ ], // Gurmukhi
+ Gujr: [
+ [2689, 2691],
+ [2693, 2701],
+ [2703, 2705],
+ [2707, 2728],
+ [2730, 2736],
+ [2738, 2739],
+ [2741, 2745],
+ [2748, 2757],
+ [2759, 2761],
+ [2763, 2765],
+ [2768, 2768],
+ [2784, 2787],
+ [2790, 2801],
+ [2809, 2815],
+ ], // Gujarati
+ Orya: [
+ [2817, 2819],
+ [2821, 2828],
+ [2831, 2832],
+ [2835, 2856],
+ [2858, 2864],
+ [2866, 2867],
+ [2869, 2873],
+ [2876, 2884],
+ [2887, 2888],
+ [2891, 2893],
+ [2901, 2903],
+ [2908, 2909],
+ [2911, 2915],
+ [2918, 2935],
+ ], // Oriya
+ Taml: [
+ [2946, 2947],
+ [2949, 2954],
+ [2958, 2960],
+ [2962, 2965],
+ [2969, 2970],
+ [2972, 2972],
+ [2974, 2975],
+ [2979, 2980],
+ [2984, 2986],
+ [2990, 3001],
+ [3006, 3010],
+ [3014, 3016],
+ [3018, 3021],
+ [3024, 3024],
+ [3031, 3031],
+ [3046, 3066],
+ [73664, 73713],
+ [73727, 73727],
+ ], // Tamil
+ Telu: [
+ [3072, 3084],
+ [3086, 3088],
+ [3090, 3112],
+ [3114, 3129],
+ [3132, 3140],
+ [3142, 3144],
+ [3146, 3149],
+ [3157, 3158],
+ [3160, 3162],
+ [3165, 3165],
+ [3168, 3171],
+ [3174, 3183],
+ [3191, 3199],
+ ], // Telugu
+ Knda: [
+ [3200, 3212],
+ [3214, 3216],
+ [3218, 3240],
+ [3242, 3251],
+ [3253, 3257],
+ [3260, 3268],
+ [3270, 3272],
+ [3274, 3277],
+ [3285, 3286],
+ [3293, 3294],
+ [3296, 3299],
+ [3302, 3311],
+ [3313, 3315],
+ ], // Kannada
+ Mlym: [
+ [3328, 3340],
+ [3342, 3344],
+ [3346, 3396],
+ [3398, 3400],
+ [3402, 3407],
+ [3412, 3427],
+ [3430, 3455],
+ ], // Malayalam
+ Sinh: [
+ [3457, 3459],
+ [3461, 3478],
+ [3482, 3505],
+ [3507, 3515],
+ [3517, 3517],
+ [3520, 3526],
+ [3530, 3530],
+ [3535, 3540],
+ [3542, 3542],
+ [3544, 3551],
+ [3558, 3567],
+ [3570, 3572],
+ [70113, 70132],
+ ], // Sinhala
+ Thai: [
+ [3585, 3642],
+ [3648, 3675],
+ ], // Thai
+ Laoo: [
+ [3713, 3714],
+ [3716, 3716],
+ [3718, 3722],
+ [3724, 3747],
+ [3749, 3749],
+ [3751, 3773],
+ [3776, 3780],
+ [3782, 3782],
+ [3784, 3790],
+ [3792, 3801],
+ [3804, 3807],
+ ], // Lao
+ Tibt: [
+ [3840, 3911],
+ [3913, 3948],
+ [3953, 3991],
+ [3993, 4028],
+ [4030, 4044],
+ [4046, 4052],
+ [4057, 4058],
+ ], // Tibetan
+ Mymr: [
+ [4096, 4255],
+ [43488, 43518],
+ [43616, 43647],
+ ], // Myanmar
+ Geor: [
+ [4256, 4293],
+ [4295, 4295],
+ [4301, 4301],
+ [4304, 4346],
+ [4348, 4351],
+ [7312, 7354],
+ [7357, 7359],
+ [11520, 11557],
+ [11559, 11559],
+ [11565, 11565],
+ ], // Georgian
+ Hang: [
+ [4352, 4607],
+ [12334, 12335],
+ [12593, 12686],
+ [12800, 12830],
+ [12896, 12926],
+ [43360, 43388],
+ [44032, 55203],
+ [55216, 55238],
+ [55243, 55291],
+ [65440, 65470],
+ [65474, 65479],
+ [65482, 65487],
+ [65490, 65495],
+ [65498, 65500],
+ ], // Hangul
+ Ethi: [
+ [4608, 4680],
+ [4682, 4685],
+ [4688, 4694],
+ [4696, 4696],
+ [4698, 4701],
+ [4704, 4744],
+ [4746, 4749],
+ [4752, 4784],
+ [4786, 4789],
+ [4792, 4798],
+ [4800, 4800],
+ [4802, 4805],
+ [4808, 4822],
+ [4824, 4880],
+ [4882, 4885],
+ [4888, 4954],
+ [4957, 4988],
+ [4992, 5017],
+ [11648, 11670],
+ [11680, 11686],
+ [11688, 11694],
+ [11696, 11702],
+ [11704, 11710],
+ [11712, 11718],
+ [11720, 11726],
+ [11728, 11734],
+ [11736, 11742],
+ [43777, 43782],
+ [43785, 43790],
+ [43793, 43798],
+ [43808, 43814],
+ [43816, 43822],
+ [124896, 124902],
+ [124904, 124907],
+ [124909, 124910],
+ [124912, 124926],
+ ], // Ethiopic
+ Cher: [
+ [5024, 5109],
+ [5112, 5117],
+ [43888, 43967],
+ ], // Cherokee
+ Cans: [
+ [5120, 5759],
+ [6320, 6389],
+ [72368, 72383],
+ ], // Canadian_Aboriginal
+ Ogam: [[5760, 5788]], // Ogham
+ Runr: [
+ [5792, 5866],
+ [5870, 5880],
+ ], // Runic
+ Tglg: [
+ [5888, 5909],
+ [5919, 5919],
+ ], // Tagalog
+ Hano: [[5920, 5940]], // Hanunoo
+ Buhd: [[5952, 5971]], // Buhid
+ Tagb: [
+ [5984, 5996],
+ [5998, 6000],
+ [6002, 6003],
+ ], // Tagbanwa
+ Khmr: [
+ [6016, 6109],
+ [6112, 6121],
+ [6128, 6137],
+ [6624, 6655],
+ ], // Khmer
+ Mong: [
+ [6144, 6145],
+ [6148, 6148],
+ [6150, 6169],
+ [6176, 6264],
+ [6272, 6314],
+ [71264, 71276],
+ ], // Mongolian
+ Limb: [
+ [6400, 6430],
+ [6432, 6443],
+ [6448, 6459],
+ [6464, 6464],
+ [6468, 6479],
+ ], // Limbu
+ Tale: [
+ [6480, 6509],
+ [6512, 6516],
+ ], // Tai_Le
+ Talu: [
+ [6528, 6571],
+ [6576, 6601],
+ [6608, 6618],
+ [6622, 6623],
+ ], // New_Tai_Lue
+ Bugi: [
+ [6656, 6683],
+ [6686, 6687],
+ ], // Buginese
+ Lana: [
+ [6688, 6750],
+ [6752, 6780],
+ [6783, 6793],
+ [6800, 6809],
+ [6816, 6829],
+ ], // Tai_Tham
+ Bali: [
+ [6912, 6988],
+ [6992, 7038],
+ ], // Balinese
+ Sund: [
+ [7040, 7103],
+ [7360, 7367],
+ ], // Sundanese
+ Batk: [
+ [7104, 7155],
+ [7164, 7167],
+ ], // Batak
+ Lepc: [
+ [7168, 7223],
+ [7227, 7241],
+ [7245, 7247],
+ ], // Lepcha
+ Olck: [[7248, 7295]], // Ol_Chiki
+ Brai: [[10240, 10495]], // Braille
+ Glag: [
+ [11264, 11359],
+ [122880, 122886],
+ [122888, 122904],
+ [122907, 122913],
+ [122915, 122916],
+ [122918, 122922],
+ ], // Glagolitic
+ Tfng: [
+ [11568, 11623],
+ [11631, 11632],
+ [11647, 11647],
+ ], // Tifinagh
+ Hani: [
+ [11904, 11929],
+ [11931, 12019],
+ [12032, 12245],
+ [12293, 12293],
+ [12295, 12295],
+ [12321, 12329],
+ [12344, 12347],
+ [13312, 19903],
+ [19968, 40959],
+ [63744, 64109],
+ [64112, 64217],
+ [94178, 94179],
+ [94192, 94193],
+ [131072, 173791],
+ [173824, 177977],
+ [177984, 178205],
+ [178208, 183969],
+ [183984, 191456],
+ [194560, 195101],
+ [196608, 201546],
+ [201552, 205743],
+ ], // Han
+ Hira: [
+ [12353, 12438],
+ [12445, 12447],
+ [110593, 110879],
+ [110898, 110898],
+ [110928, 110930],
+ [127488, 127488],
+ ], // Hiragana
+ Kana: [
+ [12449, 12538],
+ [12541, 12543],
+ [12784, 12799],
+ [13008, 13054],
+ [13056, 13143],
+ [65382, 65391],
+ [65393, 65437],
+ [110576, 110579],
+ [110581, 110587],
+ [110589, 110590],
+ [110592, 110592],
+ [110880, 110882],
+ [110933, 110933],
+ [110948, 110951],
+ ], // Katakana
+ Yiii: [
+ [40960, 42124],
+ [42128, 42182],
+ ], // Yi
+ Lisu: [
+ [42192, 42239],
+ [73648, 73648],
+ ], // Lisu
+ Vaii: [[42240, 42539]], // Vai
+ Bamu: [
+ [42656, 42743],
+ [92160, 92728],
+ ], // Bamum
+ Sylo: [[43008, 43052]], // Syloti_Nagri
+ Phag: [[43072, 43127]], // Phags_Pa
+ Saur: [
+ [43136, 43205],
+ [43214, 43225],
+ ], // Saurashtra
+ Kali: [
+ [43264, 43309],
+ [43311, 43311],
+ ], // Kayah_Li
+ Rjng: [
+ [43312, 43347],
+ [43359, 43359],
+ ], // Rejang
+ Java: [
+ [43392, 43469],
+ [43472, 43481],
+ [43486, 43487],
+ ], // Javanese
+ Cham: [
+ [43520, 43574],
+ [43584, 43597],
+ [43600, 43609],
+ [43612, 43615],
+ ], // Cham
+ Tavt: [
+ [43648, 43714],
+ [43739, 43743],
+ ], // Tai_Viet
+ Mtei: [
+ [43744, 43766],
+ [43968, 44013],
+ [44016, 44025],
+ ], // Meetei_Mayek
+ Linb: [
+ [65536, 65547],
+ [65549, 65574],
+ [65576, 65594],
+ [65596, 65597],
+ [65599, 65613],
+ [65616, 65629],
+ [65664, 65786],
+ ], // Linear_B
+ Lyci: [[66176, 66204]], // Lycian
+ Cari: [[66208, 66256]], // Carian
+ Ital: [
+ [66304, 66339],
+ [66349, 66351],
+ ], // Old_Italic
+ Goth: [[66352, 66378]], // Gothic
+ Perm: [[66384, 66426]], // Old_Permic
+ Ugar: [
+ [66432, 66461],
+ [66463, 66463],
+ ], // Ugaritic
+ Xpeo: [
+ [66464, 66499],
+ [66504, 66517],
+ ], // Old_Persian
+ Dsrt: [[66560, 66639]], // Deseret
+ Shaw: [[66640, 66687]], // Shavian
+ Osma: [
+ [66688, 66717],
+ [66720, 66729],
+ ], // Osmanya
+ Osge: [
+ [66736, 66771],
+ [66776, 66811],
+ ], // Osage
+ Elba: [[66816, 66855]], // Elbasan
+ Aghb: [
+ [66864, 66915],
+ [66927, 66927],
+ ], // Caucasian_Albanian
+ Vith: [
+ [66928, 66938],
+ [66940, 66954],
+ [66956, 66962],
+ [66964, 66965],
+ [66967, 66977],
+ [66979, 66993],
+ [66995, 67001],
+ [67003, 67004],
+ ], // Vithkuqi
+ Lina: [
+ [67072, 67382],
+ [67392, 67413],
+ [67424, 67431],
+ ], // Linear_A
+ Cprt: [
+ [67584, 67589],
+ [67592, 67592],
+ [67594, 67637],
+ [67639, 67640],
+ [67644, 67644],
+ [67647, 67647],
+ ], // Cypriot
+ Armi: [
+ [67648, 67669],
+ [67671, 67679],
+ ], // Imperial_Aramaic
+ Palm: [[67680, 67711]], // Palmyrene
+ Nbat: [
+ [67712, 67742],
+ [67751, 67759],
+ ], // Nabataean
+ Hatr: [
+ [67808, 67826],
+ [67828, 67829],
+ [67835, 67839],
+ ], // Hatran
+ Phnx: [
+ [67840, 67867],
+ [67871, 67871],
+ ], // Phoenician
+ Lydi: [
+ [67872, 67897],
+ [67903, 67903],
+ ], // Lydian
+ Mero: [[67968, 67999]], // Meroitic_Hieroglyphs
+ Merc: [
+ [68000, 68023],
+ [68028, 68047],
+ [68050, 68095],
+ ], // Meroitic_Cursive
+ Khar: [
+ [68096, 68099],
+ [68101, 68102],
+ [68108, 68115],
+ [68117, 68119],
+ [68121, 68149],
+ [68152, 68154],
+ [68159, 68168],
+ [68176, 68184],
+ ], // Kharoshthi
+ Sarb: [[68192, 68223]], // Old_South_Arabian
+ Narb: [[68224, 68255]], // Old_North_Arabian
+ Mani: [
+ [68288, 68326],
+ [68331, 68342],
+ ], // Manichaean
+ Avst: [
+ [68352, 68405],
+ [68409, 68415],
+ ], // Avestan
+ Prti: [
+ [68416, 68437],
+ [68440, 68447],
+ ], // Inscriptional_Parthian
+ Phli: [
+ [68448, 68466],
+ [68472, 68479],
+ ], // Inscriptional_Pahlavi
+ Phlp: [
+ [68480, 68497],
+ [68505, 68508],
+ [68521, 68527],
+ ], // Psalter_Pahlavi
+ Orkh: [[68608, 68680]], // Old_Turkic
+ Hung: [
+ [68736, 68786],
+ [68800, 68850],
+ [68858, 68863],
+ ], // Old_Hungarian
+ Rohg: [
+ [68864, 68903],
+ [68912, 68921],
+ ], // Hanifi_Rohingya
+ Yezi: [
+ [69248, 69289],
+ [69291, 69293],
+ [69296, 69297],
+ ], // Yezidi
+ Sogo: [[69376, 69415]], // Old_Sogdian
+ Sogd: [[69424, 69465]], // Sogdian
+ Ougr: [[69488, 69513]], // Old_Uyghur
+ Chrs: [[69552, 69579]], // Chorasmian
+ Elym: [[69600, 69622]], // Elymaic
+ Brah: [
+ [69632, 69709],
+ [69714, 69749],
+ [69759, 69759],
+ ], // Brahmi
+ Kthi: [
+ [69760, 69826],
+ [69837, 69837],
+ ], // Kaithi
+ Sora: [
+ [69840, 69864],
+ [69872, 69881],
+ ], // Sora_Sompeng
+ Cakm: [
+ [69888, 69940],
+ [69942, 69959],
+ ], // Chakma
+ Mahj: [[69968, 70006]], // Mahajani
+ Shrd: [[70016, 70111]], // Sharada
+ Khoj: [
+ [70144, 70161],
+ [70163, 70209],
+ ], // Khojki
+ Mult: [
+ [70272, 70278],
+ [70280, 70280],
+ [70282, 70285],
+ [70287, 70301],
+ [70303, 70313],
+ ], // Multani
+ Sind: [
+ [70320, 70378],
+ [70384, 70393],
+ ], // Khudawadi
+ Gran: [
+ [70400, 70403],
+ [70405, 70412],
+ [70415, 70416],
+ [70419, 70440],
+ [70442, 70448],
+ [70450, 70451],
+ [70453, 70457],
+ [70460, 70468],
+ [70471, 70472],
+ [70475, 70477],
+ [70480, 70480],
+ [70487, 70487],
+ [70493, 70499],
+ [70502, 70508],
+ [70512, 70516],
+ ], // Grantha
+ Newa: [
+ [70656, 70747],
+ [70749, 70753],
+ ], // Newa
+ Tirh: [
+ [70784, 70855],
+ [70864, 70873],
+ ], // Tirhuta
+ Sidd: [
+ [71040, 71093],
+ [71096, 71133],
+ ], // Siddham
+ Modi: [
+ [71168, 71236],
+ [71248, 71257],
+ ], // Modi
+ Takr: [
+ [71296, 71353],
+ [71360, 71369],
+ ], // Takri
+ Ahom: [
+ [71424, 71450],
+ [71453, 71467],
+ [71472, 71494],
+ ], // Ahom
+ Dogr: [[71680, 71739]], // Dogra
+ Wara: [
+ [71840, 71922],
+ [71935, 71935],
+ ], // Warang_Citi
+ Diak: [
+ [71936, 71942],
+ [71945, 71945],
+ [71948, 71955],
+ [71957, 71958],
+ [71960, 71989],
+ [71991, 71992],
+ [71995, 72006],
+ [72016, 72025],
+ ], // Dives_Akuru
+ Nand: [
+ [72096, 72103],
+ [72106, 72151],
+ [72154, 72164],
+ ], // Nandinagari
+ Zanb: [[72192, 72263]], // Zanabazar_Square
+ Soyo: [[72272, 72354]], // Soyombo
+ Pauc: [[72384, 72440]], // Pau_Cin_Hau
+ Bhks: [
+ [72704, 72712],
+ [72714, 72758],
+ [72760, 72773],
+ [72784, 72812],
+ ], // Bhaiksuki
+ Marc: [
+ [72816, 72847],
+ [72850, 72871],
+ [72873, 72886],
+ ], // Marchen
+ Gonm: [
+ [72960, 72966],
+ [72968, 72969],
+ [72971, 73014],
+ [73018, 73018],
+ [73020, 73021],
+ [73023, 73031],
+ [73040, 73049],
+ ], // Masaram_Gondi
+ Gong: [
+ [73056, 73061],
+ [73063, 73064],
+ [73066, 73102],
+ [73104, 73105],
+ [73107, 73112],
+ [73120, 73129],
+ ], // Gunjala_Gondi
+ Maka: [[73440, 73464]], // Makasar
+ Kawi: [
+ [73472, 73488],
+ [73490, 73530],
+ [73534, 73561],
+ ], // Kawi
+ Xsux: [
+ [73728, 74649],
+ [74752, 74862],
+ [74864, 74868],
+ [74880, 75075],
+ ], // Cuneiform
+ Cpmn: [[77712, 77810]], // Cypro_Minoan
+ Egyp: [[77824, 78933]], // Egyptian_Hieroglyphs
+ Hluw: [[82944, 83526]], // Anatolian_Hieroglyphs
+ Mroo: [
+ [92736, 92766],
+ [92768, 92777],
+ [92782, 92783],
+ ], // Mro
+ Tnsa: [
+ [92784, 92862],
+ [92864, 92873],
+ ], // Tangsa
+ Bass: [
+ [92880, 92909],
+ [92912, 92917],
+ ], // Bassa_Vah
+ Hmng: [
+ [92928, 92997],
+ [93008, 93017],
+ [93019, 93025],
+ [93027, 93047],
+ [93053, 93071],
+ ], // Pahawh_Hmong
+ Medf: [[93760, 93850]], // Medefaidrin
+ Plrd: [
+ [93952, 94026],
+ [94031, 94087],
+ [94095, 94111],
+ ], // Miao
+ Tang: [
+ [94176, 94176],
+ [94208, 100343],
+ [100352, 101119],
+ [101632, 101640],
+ ], // Tangut
+ Nshu: [
+ [94177, 94177],
+ [110960, 111355],
+ ], // Nushu
+ Kits: [
+ [94180, 94180],
+ [101120, 101589],
+ ], // Khitan_Small_Script
+ Dupl: [
+ [113664, 113770],
+ [113776, 113788],
+ [113792, 113800],
+ [113808, 113817],
+ [113820, 113823],
+ ], // Duployan
+ Sgnw: [
+ [120832, 121483],
+ [121499, 121503],
+ [121505, 121519],
+ ], // SignWriting
+ Hmnp: [
+ [123136, 123180],
+ [123184, 123197],
+ [123200, 123209],
+ [123214, 123215],
+ ], // Nyiakeng_Puachue_Hmong
+ Toto: [[123536, 123566]], // Toto
+ Wcho: [
+ [123584, 123641],
+ [123647, 123647],
+ ], // Wancho
+ Nagm: [[124112, 124153]], // Nag_Mundari
+ Mend: [
+ [124928, 125124],
+ [125127, 125142],
+ ], // Mende_Kikakui
+ Adlm: [
+ [125184, 125259],
+ [125264, 125273],
+ [125278, 125279],
+ ], // Adlam
+ Zyyy: [
+ [0, 64],
+ [91, 96],
+ [123, 169],
+ [171, 185],
+ [187, 191],
+ [215, 215],
+ [247, 247],
+ [697, 735],
+ [741, 745],
+ [748, 767],
+ [884, 884],
+ [894, 894],
+ [901, 901],
+ [903, 903],
+ [1541, 1541],
+ [1548, 1548],
+ [1563, 1563],
+ [1567, 1567],
+ [1600, 1600],
+ [1757, 1757],
+ [2274, 2274],
+ [2404, 2405],
+ [3647, 3647],
+ [4053, 4056],
+ [4347, 4347],
+ [5867, 5869],
+ [5941, 5942],
+ [6146, 6147],
+ [6149, 6149],
+ [7379, 7379],
+ [7393, 7393],
+ [7401, 7404],
+ [7406, 7411],
+ [7413, 7415],
+ [7418, 7418],
+ [8192, 8203],
+ [8206, 8292],
+ [8294, 8304],
+ [8308, 8318],
+ [8320, 8334],
+ [8352, 8384],
+ [8448, 8485],
+ [8487, 8489],
+ [8492, 8497],
+ [8499, 8525],
+ [8527, 8543],
+ [8585, 8587],
+ [8592, 9254],
+ [9280, 9290],
+ [9312, 10239],
+ [10496, 11123],
+ [11126, 11157],
+ [11159, 11263],
+ [11776, 11869],
+ [12272, 12283],
+ [12288, 12292],
+ [12294, 12294],
+ [12296, 12320],
+ [12336, 12343],
+ [12348, 12351],
+ [12443, 12444],
+ [12448, 12448],
+ [12539, 12540],
+ [12688, 12703],
+ [12736, 12771],
+ [12832, 12895],
+ [12927, 13007],
+ [13055, 13055],
+ [13144, 13311],
+ [19904, 19967],
+ [42752, 42785],
+ [42888, 42890],
+ [43056, 43065],
+ [43310, 43310],
+ [43471, 43471],
+ [43867, 43867],
+ [43882, 43883],
+ [64830, 64831],
+ [65040, 65049],
+ [65072, 65106],
+ [65108, 65126],
+ [65128, 65131],
+ [65279, 65279],
+ [65281, 65312],
+ [65339, 65344],
+ [65371, 65381],
+ [65392, 65392],
+ [65438, 65439],
+ [65504, 65510],
+ [65512, 65518],
+ [65529, 65532],
+ [65792, 65794],
+ [65799, 65843],
+ [65847, 65855],
+ [65936, 65948],
+ [66000, 66044],
+ [66273, 66299],
+ [113824, 113827],
+ [118608, 118723],
+ [118784, 119029],
+ [119040, 119078],
+ [119081, 119142],
+ [119146, 119162],
+ [119171, 119172],
+ [119180, 119209],
+ [119214, 119274],
+ [119488, 119507],
+ [119520, 119539],
+ [119552, 119638],
+ [119648, 119672],
+ [119808, 119892],
+ [119894, 119964],
+ [119966, 119967],
+ [119970, 119970],
+ [119973, 119974],
+ [119977, 119980],
+ [119982, 119993],
+ [119995, 119995],
+ [119997, 120003],
+ [120005, 120069],
+ [120071, 120074],
+ [120077, 120084],
+ [120086, 120092],
+ [120094, 120121],
+ [120123, 120126],
+ [120128, 120132],
+ [120134, 120134],
+ [120138, 120144],
+ [120146, 120485],
+ [120488, 120779],
+ [120782, 120831],
+ [126065, 126132],
+ [126209, 126269],
+ [126976, 127019],
+ [127024, 127123],
+ [127136, 127150],
+ [127153, 127167],
+ [127169, 127183],
+ [127185, 127221],
+ [127232, 127405],
+ [127462, 127487],
+ [127489, 127490],
+ [127504, 127547],
+ [127552, 127560],
+ [127568, 127569],
+ [127584, 127589],
+ [127744, 128727],
+ [128732, 128748],
+ [128752, 128764],
+ [128768, 128886],
+ [128891, 128985],
+ [128992, 129003],
+ [129008, 129008],
+ [129024, 129035],
+ [129040, 129095],
+ [129104, 129113],
+ [129120, 129159],
+ [129168, 129197],
+ [129200, 129201],
+ [129280, 129619],
+ [129632, 129645],
+ [129648, 129660],
+ [129664, 129672],
+ [129680, 129725],
+ [129727, 129733],
+ [129742, 129755],
+ [129760, 129768],
+ [129776, 129784],
+ [129792, 129938],
+ [129940, 129994],
+ [130032, 130041],
+ [917505, 917505],
+ [917536, 917631],
+ ], // Common
+ Zzzz: [[65533, 65533]],
+};
+
+export function getScriptPredictor(
+ replaceWhitespace: boolean = true,
+ replacePunctuation: boolean = true,
+ replaceDigits: boolean = true,
+): (sent: string) => ScoredScript {
+ // Create a map of code points to script names
+ const histMap: Map<number, Set<string>> = new Map();
+
+ for (const [key, ranges] of Object.entries(SCRIPT_RANGES)) {
+ for (const [start, end] of ranges) {
+ for (let ordinal = start; ordinal <= end; ordinal++) {
+ if (!histMap.has(ordinal)) {
+ histMap.set(ordinal, new Set());
+ }
+ histMap.get(ordinal)!.add(key);
+ }
+ }
+ }
+
+ // Helper function to check if a character is whitespace
+ const isWhitespace = (char: string): boolean => /\s/.test(char);
+
+ // Helper function to check if a character is punctuation
+ const isPunctuation = (char: string): boolean =>
+ /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/.test(char);
+
+ // Helper function to check if a character is a digit
+ const isDigit = (char: string): boolean => /\d/.test(char);
+
+ return (sent: string): ScoredScript => {
+ // Filter out characters based on settings
+ const filteredText = sent
+ .split("")
+ .filter((char) => {
+ if (replaceWhitespace && isWhitespace(char)) return false;
+ if (replacePunctuation && isPunctuation(char)) return false;
+ if (replaceDigits && isDigit(char)) return false;
+ return true;
+ })
+ .join("");
+
+ if (filteredText.length === 0) {
+ return [null, 0, { details: null, tie: null, interval: null }];
+ }
+
+ // Count characters by script
+ const scriptCount: Map<string, number> = new Map();
+
+ for (const char of filteredText) {
+ const ordinal = char.codePointAt(0)!;
+ const scripts = histMap.get(ordinal) || new Set(["Zzzz"]);
+
+ for (const script of scripts) {
+ scriptCount.set(script, (scriptCount.get(script) || 0) + 1);
+ }
+ }
+
+ // Convert to sorted object for details
+ const sortedScores: Record<string, number> = {};
+ for (const [script, count] of scriptCount.entries()) {
+ sortedScores[script] = count / filteredText.length;
+ }
+
+ // Find the script with maximum score
+ let maxScore = 0;
+ let maxScript: string | null = null;
+
+ for (const [script, count] of scriptCount.entries()) {
+ const score = count / filteredText.length;
+ if (score > maxScore) {
+ maxScore = score;
+ maxScript = script;
+ }
+ }
+
+ // Sort scores for details
+ const sortedEntries = Object.entries(sortedScores).sort(
+ (a, b) => b[1] - a[1],
+ );
+ const sortedDetails: Record<string, number> =
+ Object.fromEntries(sortedEntries);
+
+ // Calculate interval and check for ties
+ if (sortedEntries.length > 1) {
+ const secondScore = sortedEntries[1][1];
+ const interval = maxScore - secondScore;
+ return [
+ maxScript,
+ maxScore,
+ {
+ details: sortedDetails,
+ tie: interval === 0,
+ interval: interval,
+ },
+ ];
+ }
+
+ return [
+ maxScript,
+ maxScore,
+ {
+ details: sortedDetails,
+ tie: false,
+ interval: 1,
+ },
+ ];
+ };
+}
+
+export function separateScript(sent: string): Record<string, string> {
+ const result: Record<string, string[]> = {};
+
+ for (const char of sent) {
+ const codePoint = char.codePointAt(0)!;
+
+ for (const [script, ranges] of Object.entries(SCRIPT_RANGES)) {
+ for (const [start, end] of ranges) {
+ if ((start <= codePoint && codePoint <= end) || char === " ") {
+ if (!result[script]) {
+ result[script] = [];
+ }
+ result[script].push(char);
+ break;
+ }
+ }
+ }
+ }
+
+ // Filter out empty values and spaces, convert arrays to strings
+ const filtered: Record<string, string> = {};
+ for (const [key, value] of Object.entries(result)) {
+ const joined = value.join("");
+ if (joined && joined.trim()) {
+ filtered[key] = joined;
+ }
+ }
+
+ return filtered;
+}
+
+// Test functions
+export function testPredictScript(): void {
+ const predictor = getScriptPredictor();
+
+ const tests: [string, [string | null, number]][] = [
+ ["this is a latin script.", ["Latn", 1.0]],
+ ["isso é escrita latina 1234", ["Latn", 1.0]],
+ ["এটি বাংলা লিপি", ["Beng", 1.0]],
+ ["นี่คืออักษรไทย", ["Thai", 1.0]],
+ [
+ "자미로콰이 Jamiroquai는 영국의 애시드 재즈 밴드이다 자미로콰이는 년대 초반 런던에서 활발하게 일어난 애시드 재즈",
+ ["Hang", 0.8148148148148148],
+ ],
+ ["이어지는기사 에서그점 에관해알려줄것 입니다", ["Hang", 1.0]],
+ ["12345", [null, 0]],
+ [" ", [null, 0]],
+ ["", [null, 0]],
+ ];
+
+ for (const [input, expected] of tests) {
+ const result = predictor(input);
+ console.assert(
+ result[0] === expected[0] && Math.abs(result[1] - expected[1]) < 0.0001,
+ `Test failed for "${input}"\nExpected: ${expected}\nGot: [${result[0]}, ${result[1]}]`,
+ );
+ }
+}
+
+export function testSeparateScript(): void {
+ const sent = "Hello Salut سلام 你好 こんにちは שלום مرحبا";
+ const detected = separateScript(sent);
+
+ const groundTruth: Record<string, string> = {
+ Latn: "Hello Salut ",
+ Hebr: " שלום ",
+ Arab: " سلام مرحبا",
+ Hani: " 你好 ",
+ Hira: " こんにちは ",
+ };
+
+ for (const key of Object.keys(groundTruth)) {
+ console.assert(
+ key in detected,
+ `Error: '${key}' script not found in detected scripts.`,
+ );
+
+ const detectedTokens = detected[key]
+ .split(" ")
+ .map((x) => x.trim())
+ .filter((x) => x.length > 0);
+ const groundTruthTokens = groundTruth[key]
+ .split(" ")
+ .map((x) => x.trim())
+ .filter((x) => x.length > 0);
+
+ console.assert(
+ JSON.stringify(detectedTokens.sort()) ===
+ JSON.stringify(groundTruthTokens.sort()),
+ `Error: Tokens for key '${key}' do not match.`,
+ );
+ }
+}
diff --git a/packages/lang/test.ts b/packages/lang/test.ts
new file mode 100644
index 0000000..d1763ca
--- /dev/null
+++ b/packages/lang/test.ts
@@ -0,0 +1,24 @@
+import { franc, francAll } from "franc-all";
+import { getScriptPredictor } from "./src";
+
+const a = "My name is John.";
+const a2 = "My name is John";
+const b = "ของไทย";
+const b2 = "ของไทย";
+const c = "こんにちは";
+const c2 = "こんにちは";
+//
+// This isn't working must be using some browser APIs to do the work I guess
+const fa = franc("My name is John.");
+const fa2 = francAll("My name is John");
+const fb = franc("ของไทย");
+const fb2 = francAll("ของไทย");
+const fc = franc("こんにちは");
+const fc2 = franc("こんにちは");
+
+console.log({ fa, fa2, fb, fb2, fc, fc2 });
+
+const Predictor = getScriptPredictor();
+const ua = Predictor(a);
+
+console.dir({ ua }, { depth: null });
diff --git a/packages/lang/tsconfig.json b/packages/lang/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/packages/lang/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/packages/prosody-ui/.gitignore b/packages/prosody-ui/.gitignore
new file mode 100644
index 0000000..9b1ee42
--- /dev/null
+++ b/packages/prosody-ui/.gitignore
@@ -0,0 +1,175 @@
+# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
+
+# Logs
+
+logs
+_.log
+npm-debug.log_
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Caches
+
+.cache
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# Runtime data
+
+pids
+_.pid
+_.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+
+lib-cov
+
+# Coverage directory used by tools like istanbul
+
+coverage
+*.lcov
+
+# nyc test coverage
+
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+
+bower_components
+
+# node-waf configuration
+
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+
+build/Release
+
+# Dependency directories
+
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+
+web_modules/
+
+# TypeScript cache
+
+*.tsbuildinfo
+
+# Optional npm cache directory
+
+.npm
+
+# Optional eslint cache
+
+.eslintcache
+
+# Optional stylelint cache
+
+.stylelintcache
+
+# Microbundle cache
+
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+
+.node_repl_history
+
+# Output of 'npm pack'
+
+*.tgz
+
+# Yarn Integrity file
+
+.yarn-integrity
+
+# dotenv environment variable files
+
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+
+.parcel-cache
+
+# Next.js build output
+
+.next
+out
+
+# Nuxt.js build / generate output
+
+.nuxt
+dist
+
+# Gatsby files
+
+# Comment in the public line in if your project uses Gatsby and not Next.js
+
+# https://nextjs.org/blog/next-9-1#public-directory-support
+
+# public
+
+# vuepress build output
+
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+
+.temp
+
+# Docusaurus cache and generated files
+
+.docusaurus
+
+# Serverless directories
+
+.serverless/
+
+# FuseBox cache
+
+.fusebox/
+
+# DynamoDB Local files
+
+.dynamodb/
+
+# TernJS port file
+
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+
+.vscode-test
+
+# yarn v2
+
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/prosody-ui/README.md b/packages/prosody-ui/README.md
new file mode 100644
index 0000000..e38a0b1
--- /dev/null
+++ b/packages/prosody-ui/README.md
@@ -0,0 +1,15 @@
+# prosody-ui
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
diff --git a/packages/prosody-ui/bun.lock b/packages/prosody-ui/bun.lock
new file mode 100644
index 0000000..c276ddd
--- /dev/null
+++ b/packages/prosody-ui/bun.lock
@@ -0,0 +1,318 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "prosody-ui",
+ "dependencies": {
+ "franc-all": "^7.2.0",
+ "glotscript": "file:../glotscript",
+ "motion": "^12.11.3",
+ "sortug": "file:../sortug",
+ "sortug-ai": "file:../models",
+ },
+ "devDependencies": {
+ "@types/bun": "^1.3.2",
+ "@types/react": "^19.2.6",
+ },
+ "peerDependencies": {
+ "react": ">=19.0.0",
+ "typescript": "^5.0.0",
+ },
+ },
+ },
+ "packages": {
+ "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.36.3", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-+c0mMLxL/17yFZ4P5+U6bTWiCSFZUKJddrv01ud2aFBWnTPLdRncYV76D3q1tqfnL7aCnhRtykFnoCFzvr4U3Q=="],
+
+ "@google/genai": ["@google/genai@0.13.0", "", { "dependencies": { "google-auth-library": "^9.14.2", "ws": "^8.18.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.4" } }, "sha512-eaEncWt875H7046T04mOpxpHJUM+jLIljEf+5QctRyOeChylE/nhpwm1bZWTRWoOu/t46R9r+PmgsJFhTpE7tQ=="],
+
+ "@google/generative-ai": ["@google/generative-ai@0.21.0", "", {}, "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg=="],
+
+ "@grpc/grpc-js": ["@grpc/grpc-js@1.13.3", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg=="],
+
+ "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="],
+
+ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
+
+ "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
+
+ "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
+
+ "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
+
+ "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
+
+ "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
+
+ "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
+
+ "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
+
+ "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
+
+ "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
+
+ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
+
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+
+ "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
+
+ "@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
+
+ "@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
+
+ "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="],
+
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
+ "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
+
+ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
+
+ "axios": ["axios@1.9.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg=="],
+
+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+
+ "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
+
+ "bignumber.js": ["bignumber.js@9.3.0", "", {}, "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA=="],
+
+ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+
+ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
+
+ "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
+
+ "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+
+ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
+
+ "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
+
+ "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="],
+
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
+
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
+
+ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
+
+ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
+
+ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+ "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
+
+ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
+
+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+
+ "file-type": ["file-type@18.7.0", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", "token-types": "^5.0.1" } }, "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw=="],
+
+ "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
+
+ "form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
+
+ "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
+
+ "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
+
+ "framer-motion": ["framer-motion@12.11.3", "", { "dependencies": { "motion-dom": "^12.11.2", "motion-utils": "^12.9.4", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-ksUtDFBZtrbQFt4bEMFrFgo7camhmXcLeuylKQxEYSd9czkZ4tZmFROxWczWeu51WqC2m91ifpvgGCBLd0uviQ=="],
+
+ "franc-all": ["franc-all@7.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-ZR6ciLQTDBaOvBdkOd8+vqDzaLtmIXRa9GCzcAlaBpqNAKg9QrtClPmqiKac5/xZXfCZGMo1d8dIu1T0BLhHEg=="],
+
+ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+ "gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
+
+ "gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
+
+ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
+
+ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+
+ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+ "glotscript": ["glotscript@file:../glotscript", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5.0.0" } }],
+
+ "google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="],
+
+ "google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
+
+ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+ "groq-sdk": ["groq-sdk@0.15.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-aYDEdr4qczx3cLCRRe+Beb37I7g/9bD5kHF+EEDxcrREWw1vKoRcfP3vHEkJB7Ud/8oOuF0scRwDpwWostTWuQ=="],
+
+ "gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
+
+ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+
+ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
+
+ "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+ "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
+
+ "iso-639-3": ["iso-639-3@3.0.1", "", {}, "sha512-SdljCYXOexv/JmbQ0tvigHN43yECoscVpe2y2hlEqy/CStXQlroPhZLj7zKLRiGqLJfw8k7B973UAMDoQczVgQ=="],
+
+ "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
+
+ "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
+
+ "jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="],
+
+ "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
+
+ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
+
+ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+ "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+ "motion": ["motion@12.11.3", "", { "dependencies": { "framer-motion": "^12.11.3", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-R9t8IYJ5hSl+Ao5rj6XGS4lJN+fXQstcpwKOcFA5aWjlwjf3IHcHr8DUjPV0My6T/5ZCQ1jqh0pmjggO4zUpEA=="],
+
+ "motion-dom": ["motion-dom@12.11.2", "", { "dependencies": { "motion-utils": "^12.9.4" } }, "sha512-wZ396XNNTI9GOkyrr80wFSbZc1JbIHSHTbLdririSbkEgahWWKmsHzsxyxqBBvuBU/iaQWVu1YCjdpXYNfo2yQ=="],
+
+ "motion-utils": ["motion-utils@12.9.4", "", {}, "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="],
+
+ "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
+
+ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
+
+ "openai": ["openai@4.98.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-TmDKur1WjxxMPQAtLG5sgBSCJmX7ynTsGmewKzoDwl1fRxtbLOsiR0FA/AOAAtYUmP6azal+MYQuOENfdU+7yg=="],
+
+ "peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="],
+
+ "playht": ["playht@0.16.0", "", { "dependencies": { "@grpc/grpc-js": "^1.9.4", "axios": "^1.4.0", "cross-fetch": "^4.0.0", "file-type": "^18.5.0", "protobufjs": "^7.2.5", "tslib": "^2.1.0" } }, "sha512-gwKqGcmUwrd3NaG6B2z5RZCjxPM0CI915Bmej+GXWZU2PSdN2g4hXsDMnjts+uakLaqGEY8YaIqNokyYH7SnvQ=="],
+
+ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
+
+ "protobufjs": ["protobufjs@7.5.2", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-f2ls6rpO6G153Cy+o2XQ+Y0sARLOZ17+OGVLHrc3VUKcLHYKEKWbkSujdBWQXM7gKn5NTfp0XnRPZn1MIu8n9w=="],
+
+ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
+
+ "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
+
+ "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
+
+ "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+
+ "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="],
+
+ "replicate": ["replicate@1.0.1", "", { "optionalDependencies": { "readable-stream": ">=4.0.0" } }, "sha512-EY+rK1YR5bKHcM9pd6WyaIbv6m2aRIvHfHDh51j/LahlHTLKemTYXF6ptif2sLa+YospupAsIoxw8Ndt5nI3vg=="],
+
+ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
+
+ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
+ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
+
+ "sortug": ["sortug@file:../sortug", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
+ "sortug-ai": ["models@file:../models", { "dependencies": { "@anthropic-ai/sdk": "^0.36.3", "@google/genai": "^0.13.0", "@google/generative-ai": "^0.21.0", "bcp-47": "^2.1.0", "franc-all": "^7.2.0", "groq-sdk": "^0.15.0", "iso-639-3": "^3.0.1", "openai": "^4.84.0", "playht": "^0.16.0", "replicate": "^1.0.1", "sortug": "file://home/y/code/npm/sortug" }, "devDependencies": { "@types/bun": "^1.2.12" }, "peerDependencies": { "typescript": "^5.7.3" } }],
+
+ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strtok3": ["strtok3@7.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.1.3" } }, "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg=="],
+
+ "token-types": ["token-types@5.0.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg=="],
+
+ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+
+ "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
+
+ "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
+ "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
+
+ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
+
+ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
+
+ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
+
+ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
+
+ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
+
+ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
+
+ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+
+ "zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
+
+ "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
+
+ "@anthropic-ai/sdk/@types/node": ["@types/node@18.19.100", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA=="],
+
+ "groq-sdk/@types/node": ["@types/node@18.19.100", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA=="],
+
+ "openai/@types/node": ["@types/node@18.19.100", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA=="],
+
+ "sortug-ai/@types/bun": ["@types/bun@1.2.13", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="],
+
+ "sortug-ai/sortug": ["sortug@file:../../../npm/sortug", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
+ "@anthropic-ai/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "groq-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "sortug-ai/@types/bun/bun-types": ["bun-types@1.2.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="],
+ }
+}
diff --git a/packages/prosody-ui/index.ts b/packages/prosody-ui/index.ts
new file mode 100644
index 0000000..f7339a2
--- /dev/null
+++ b/packages/prosody-ui/index.ts
@@ -0,0 +1,6 @@
+import LangText from "./src/LangText.tsx";
+import FontChanger from "./src/fonts/FontChanger.tsx";
+import Paragraph from "./src/Paragraph.tsx";
+import * as Zoom from "./src/zoom";
+
+export { LangText, FontChanger, Paragraph, Zoom };
diff --git a/packages/prosody-ui/package.json b/packages/prosody-ui/package.json
new file mode 100644
index 0000000..cf9ad48
--- /dev/null
+++ b/packages/prosody-ui/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@sortug/prosody-ui",
+ "version": "0.1.0",
+ "module": "index.ts",
+ "type": "module",
+ "peerDependencies": {
+ "typescript": "^5.0.0",
+ "react": ">=19.0.0"
+ },
+ "dependencies": {
+ "franc-all": "^7.2.0",
+ "motion": "^12.11.3",
+ "@sortug/lib": "workspace:*",
+ "@sortug/langlib": "workspace:*",
+ "@sortug/ai": "workspace:*"
+ },
+ "private": true,
+ "devDependencies": {
+ "@types/bun": "^1.3.2",
+ "@types/react": "^19.2.6"
+ }
+}
diff --git a/packages/prosody-ui/src/LangText.tsx b/packages/prosody-ui/src/LangText.tsx
new file mode 100644
index 0000000..790c499
--- /dev/null
+++ b/packages/prosody-ui/src/LangText.tsx
@@ -0,0 +1,78 @@
+import { franc } from "franc-all";
+import React, { useEffect, useState } from "react";
+import ThaiText from "./thai/ThaiText";
+import { ColoredText } from "./components/Sentence";
+import type { AnalyzeRes, WordData } from "./logic/types";
+import { detectScript, scriptFromLang } from "./logic/utils";
+import LatinText from "./latin/LatinText";
+import { buildWiktionaryURL, parseWiktionary } from "./logic/wiki";
+import type { Result } from "sortug";
+
+export default function LangText({
+ text,
+ lang,
+ theme,
+ fetchWiki,
+ handleWord,
+}: {
+ text: string;
+ lang?: string;
+ theme?: string;
+ fetchWiki?: (url: string) => Promise<string>;
+ handleWord?: (wd: Result<WordData>) => any;
+}) {
+ const [llang, setLang] = useState("");
+ const [script, setScript] = useState(scriptFromLang(lang || "", text));
+ useEffect(() => {
+ if (!lang) {
+ const res = franc(text);
+ setLang(res);
+ } else setLang(lang);
+ }, [text]);
+ console.log("langtext", { text, llang, script });
+
+ async function openWord(word: string) {
+ // console.log("looking up", word);
+ // const url = buildWiktionaryURL(word);
+ // const html = await fetchWiki(url);
+ // const parsed = parseWiktionary(html, url);
+ // console.log({ parsed });
+ // if ("error" in parsed) handleWord(parsed);
+ // else {
+ // const wd: WordData = {
+ // spelling: word,
+ // lang: llang,
+ // ipa: parsed.ok.ipa[0],
+ // meanings: parsed.ok.meanings,
+ // references: { url: parsed.ok.url },
+ // };
+ // handleWord({ ok: wd });
+ // }
+ // // const d = data[s];
+ // // setModal(d);
+ // // setModal(<WordModal data={d} lang={lang} />);
+ }
+
+ return (
+ <div className="lang-text-container">
+ {script === "Thai" ? (
+ <ThaiText text={text} openWord={openWord} />
+ ) : script === "Latin" ? (
+ <LatinText text={text} lang={llang} openWord={openWord} />
+ ) : (
+ <Generic text={text} lang={llang} />
+ )}
+ </div>
+ );
+}
+function Generic({ text, lang }: { text: string; lang: string }) {
+ const [data, setData] = useState<AnalyzeRes>();
+ useEffect(() => {
+ // segmentate(text, lang)
+ }, [text, lang]);
+ console.log({ lang }, "generic");
+ // <p className="lang-lang">{lang}</p>
+ // <p className="lang-text">{text}</p>
+ // {data && <ColoredText frags={Object.keys(data)} />}
+ return <div className="lang-text-div"></div>;
+}
diff --git a/packages/prosody-ui/src/Paragraph.tsx b/packages/prosody-ui/src/Paragraph.tsx
new file mode 100644
index 0000000..72c43a7
--- /dev/null
+++ b/packages/prosody-ui/src/Paragraph.tsx
@@ -0,0 +1,56 @@
+import { franc } from "franc-all";
+import React, { useCallback, useEffect, useState } from "react";
+import ThaiText from "./thai/ThaiText";
+import { ColoredText } from "./components/Sentence";
+import type { AnalyzeRes, WordData } from "./logic/types";
+import { detectScript, langFromScript } from "./logic/utils";
+import LatinText from "./latin/LatinText";
+import { buildWiktionaryURL, parseWiktionary } from "./logic/wiki";
+import type { Result } from "sortug";
+import * as Stanza from "./logic/stanza";
+import { iso6393To1 } from "./logic/iso6393to1";
+
+export default function Paragraph({
+ text,
+}: {
+ text: string;
+ handleWord?: (wd: Result<WordData>) => any;
+}) {
+ useEffect(() => {
+ segmentString();
+ }, [text]);
+ const [lang, setLang] = useState("");
+ const [script, setScript] = useState("");
+ const [segs, setSegs] = useState<Stanza.StanzaSegment[]>([]);
+ useEffect(() => {
+ const res = franc(text);
+ console.log();
+ console.log({ res, text });
+ if (res === "und") detectLanguage();
+ else {
+ const smol = iso6393To1[res];
+ if (!smol) console.log("lang", res);
+ else setLang(smol);
+ }
+ }, [text]);
+
+ const segmentString = useCallback(async () => {
+ if (lang) {
+ const res = await Stanza.segmenter(text, lang);
+ if ("ok" in res) setSegs(res.ok.segments);
+ console.log("stanza", res);
+ }
+ }, [text, lang]);
+ const detectLanguage = useCallback(async () => {
+ const script = detectScript(text);
+ if ("error" in script) console.log("ded");
+ else {
+ setScript(script.ok);
+ const lng = langFromScript(script.ok);
+ if ("error" in lng) console.log("ded again!");
+ else setLang(lng.ok);
+ }
+ }, [text]);
+
+ return <div className="segmented-text">{text}</div>;
+}
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/BeiShiDaJiaGuWenZiTi-1.ttf b/packages/prosody-ui/src/assets/fonts/Hani/BeiShiDaJiaGuWenZiTi-1.ttf
new file mode 100644
index 0000000..5ada19c
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/BeiShiDaJiaGuWenZiTi-1.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/GaiLiangShouJinTi-2.ttf b/packages/prosody-ui/src/assets/fonts/Hani/GaiLiangShouJinTi-2.ttf
new file mode 100644
index 0000000..2606d1c
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/GaiLiangShouJinTi-2.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/GuDianMingChaoKai-2.ttf b/packages/prosody-ui/src/assets/fonts/Hani/GuDianMingChaoKai-2.ttf
new file mode 100644
index 0000000..49f5405
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/GuDianMingChaoKai-2.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/Iansui-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Hani/Iansui-Regular.ttf
new file mode 100644
index 0000000..5da538b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/Iansui-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/LiuJianMaoCao-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Hani/LiuJianMaoCao-Regular.ttf
new file mode 100644
index 0000000..4b7e9bc
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/LiuJianMaoCao-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/LongCang-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Hani/LongCang-Regular.ttf
new file mode 100644
index 0000000..44f6a9f
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/LongCang-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/Meirenzhuan.ttf b/packages/prosody-ui/src/assets/fonts/Hani/Meirenzhuan.ttf
new file mode 100644
index 0000000..8eb1209
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/Meirenzhuan.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/No.300-ShangShouGuHuangTi-2.ttf b/packages/prosody-ui/src/assets/fonts/Hani/No.300-ShangShouGuHuangTi-2.ttf
new file mode 100644
index 0000000..1f16ba8
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/No.300-ShangShouGuHuangTi-2.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Black.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Black.ttf
new file mode 100644
index 0000000..e86fb96
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Black.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Bold.ttf
new file mode 100644
index 0000000..76812d7
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraBold.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraBold.ttf
new file mode 100644
index 0000000..db381ed
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraLight.ttf
new file mode 100644
index 0000000..e30cdd8
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Light.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Light.ttf
new file mode 100644
index 0000000..8be49fc
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Medium.ttf
new file mode 100644
index 0000000..f45d4ec
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Regular.ttf
new file mode 100644
index 0000000..2a62b97
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-SemiBold.ttf
new file mode 100644
index 0000000..bc3e266
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Thin.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Thin.ttf
new file mode 100644
index 0000000..832c29f
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-Thin.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-VariableFont_wght.ttf b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-VariableFont_wght.ttf
new file mode 100644
index 0000000..47f2c84
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/NotoSansHK-VariableFont_wght.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/YeZiGongChangZuiHanJiangXingCao-2.ttf b/packages/prosody-ui/src/assets/fonts/Hani/YeZiGongChangZuiHanJiangXingCao-2.ttf
new file mode 100644
index 0000000..b59a399
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/YeZiGongChangZuiHanJiangXingCao-2.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/YiShanBeiZhuanTi.ttf b/packages/prosody-ui/src/assets/fonts/Hani/YiShanBeiZhuanTi.ttf
new file mode 100644
index 0000000..9364f2d
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/YiShanBeiZhuanTi.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/ZhiMangXing-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Hani/ZhiMangXing-Regular.ttf
new file mode 100644
index 0000000..e2d4fad
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/ZhiMangXing-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/dingliezhuhaifont-20240831GengXinBan)-2.ttf b/packages/prosody-ui/src/assets/fonts/Hani/dingliezhuhaifont-20240831GengXinBan)-2.ttf
new file mode 100644
index 0000000..b387fc5
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/dingliezhuhaifont-20240831GengXinBan)-2.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/style.css b/packages/prosody-ui/src/assets/fonts/Hani/style.css
new file mode 100644
index 0000000..2048617
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/style.css
@@ -0,0 +1,223 @@
+/* https://www.fonts.net.cn/fonts-zh/tag-yankai-1.html */
+
+
+@font-face {
+ font-family: "BeiShiDaJiaGuWen";
+ src: url(./BeiShiDaJiaGuWenZiTi-1.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "ShanShouGuHuangTi";
+ src: url(./No.300-ShangShouGuHuangTi-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "DinglieZhuhai";
+ src: url(./dingliezhuhaifont-20240831GengXinBan-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "ShouJin";
+ src: url(./GaiLiangShouJinTi-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "GudianMingChaoKai";
+ src: url(./GuDianMingChaoKai-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-SemiBold.ttf);
+ font-weight: 500;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Black.ttf);
+ font-weight: 900;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Thin.ttf);
+ font-weight: 200;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Bold.ttf);
+ font-weight: 400;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-VariableFont_wght.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-ExtraBold.ttf);
+ font-weight: 800;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-ExtraLight.ttf);
+ font-weight: 100;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Light.ttf);
+ font-weight: 200;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(./NotoSansHK-Medium.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "Iansui";
+ src: url(./Iansui-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "YeZiGongXingCao";
+ src: url(./YeZiGongChangZuiHanJiangXingCao-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "LiuJianMaoCao";
+ src: url(./LiuJianMaoCao-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "YiShanBeiZhuanti";
+ src: url(./YiShanBeiZhuanTi.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "LongCang";
+ src: url(./LongCang-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "ZhiMangXing";
+ src: url(./ZhiMangXing-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "Meirenzhuan";
+ src: url(./Meirenzhuan.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "TianlongXingkai";
+ src: url(./字魂天龙行楷.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+.font-Hani-0 {
+ font-family: "ShouJin";
+}
+
+.font-Hani-1 {
+ font-family: "DinglieZhuhai";
+}
+
+.font-Hani-2 {
+ font-family: "GudianMingChaokai";
+}
+
+.font-Hani-3 {
+ font-family: "NotoSansHK";
+}
+
+.font-Hani-4 {
+ font-family: "ShanShouGuHuangTi";
+}
+
+.font-Hani-5 {
+ font-family: "Iansui";
+}
+
+.font-Hani-6 {
+ font-family: "YeZiGongXingCao";
+}
+
+.font-Hani-7 {
+ font-family: "LiuJianMaoCao";
+}
+
+.font-Hani-8 {
+ font-family: "YiShanBeiZhuanti";
+}
+
+.font-Hani-9 {
+ font-family: "LongCang";
+}
+
+.font-Hani-10 {
+ font-family: "ZhiMangXing";
+}
+
+.font-Hani-11 {
+ font-family: "Meirenzhuan";
+}
+
+.font-Hani-12 {
+ font-family: "TianlongXingkai";
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/fonts/Hani/字魂天龙行楷.ttf b/packages/prosody-ui/src/assets/fonts/Hani/字魂天龙行楷.ttf
new file mode 100644
index 0000000..b565e37
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Hani/字魂天龙行楷.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/Judson-Bold.ttf b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Bold.ttf
new file mode 100755
index 0000000..4b977b0
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/Judson-Italic.ttf b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Italic.ttf
new file mode 100755
index 0000000..b2ea190
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/Judson-Regular.ttf b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Regular.ttf
new file mode 100755
index 0000000..a1cbb58
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/Judson-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-Italic-VariableFont_wdth,wght.ttf b/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-Italic-VariableFont_wdth,wght.ttf
new file mode 100755
index 0000000..4e962ee
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-Italic-VariableFont_wdth,wght.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-VariableFont_wdth,wght.ttf b/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-VariableFont_wdth,wght.ttf
new file mode 100755
index 0000000..f7d0d78
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/NotoSans-VariableFont_wdth,wght.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/Voces-Regular.ttf b/packages/prosody-ui/src/assets/fonts/IPA/Voces-Regular.ttf
new file mode 100755
index 0000000..36aa975
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/Voces-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/IPA/style.css b/packages/prosody-ui/src/assets/fonts/IPA/style.css
new file mode 100644
index 0000000..ecc0a8a
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/IPA/style.css
@@ -0,0 +1,33 @@
+@font-face {
+ font-family: "Voces";
+ src: url(./Voces-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "Judson";
+ src: url(./Judson-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+/* TODO check the specifics of variable fonts */
+@font-face {
+ font-family: "Noto Sans";
+ src: url(./NotoSans-VariableFont_wdth,wght.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+.font-IPA-0 {
+ font-family: "Voces";
+}
+
+.font-IPA-1 {
+ font-family: "Judson";
+}
+
+.font-IPA-2 {
+ font-family: "Noto Sans";
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/DelaGothicOne-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/DelaGothicOne-Regular.ttf
new file mode 100644
index 0000000..475c4dd
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/DelaGothicOne-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Bold.ttf
new file mode 100644
index 0000000..7d13d05
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Medium.ttf
new file mode 100644
index 0000000..1e35712
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Regular.ttf
new file mode 100644
index 0000000..7d68508
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/KaiseiDecol-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/ZenAntiqueSoft-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/ZenAntiqueSoft-Regular.ttf
new file mode 100644
index 0000000..c78f7d6
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/ZenAntiqueSoft-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/ZenKurenaido-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Jpan/ZenKurenaido-Regular.ttf
new file mode 100644
index 0000000..cb3044e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/ZenKurenaido-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Jpan/style.css b/packages/prosody-ui/src/assets/fonts/Jpan/style.css
new file mode 100644
index 0000000..cd1b79f
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Jpan/style.css
@@ -0,0 +1,223 @@
+/* https://www.fonts.net.cn/fonts-zh/tag-yankai-1.html */
+
+
+@font-face {
+ font-family: "BeiShiDaJiaGuWen";
+ src: url(../Hani/BeiShiDaJiaGuWenZiTi-1.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "ShanShouGuHuangTi";
+ src: url(../Hani/No.300-ShangShouGuHuangTi-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "DinglieZhuhai";
+ src: url(../Hani/dingliezhuhaifont-20240831GengXinBan-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "ShouJin";
+ src: url(../Hani/GaiLiangShouJinTi-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "GudianMingChaoKai";
+ src: url(../Hani/GuDianMingChaoKai-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-SemiBold.ttf);
+ font-weight: 500;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Black.ttf);
+ font-weight: 900;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Thin.ttf);
+ font-weight: 200;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Bold.ttf);
+ font-weight: 400;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-VariableFont_wght.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-ExtraBold.ttf);
+ font-weight: 800;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-ExtraLight.ttf);
+ font-weight: 100;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Light.ttf);
+ font-weight: 200;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "NotoSansHK";
+ src: url(../Hani/NotoSansHK-Medium.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "Iansui";
+ src: url(../Hani/Iansui-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "YeZiGongXingCao";
+ src: url(../Hani/YeZiGongChangZuiHanJiangXingCao-2.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "LiuJianMaoCao";
+ src: url(../Hani/LiuJianMaoCao-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "YiShanBeiZhuanti";
+ src: url(../Hani/YiShanBeiZhuanTi.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "LongCang";
+ src: url(../Hani/LongCang-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "ZhiMangXing";
+ src: url(../Hani/ZhiMangXing-Regular.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "Meirenzhuan";
+ src: url(../Hani/Meirenzhuan.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+@font-face {
+ font-family: "TianlongXingkai";
+ src: url(../Hani/字魂天龙行楷.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+.font-Hani-0 {
+ font-family: "ShouJin";
+}
+
+.font-Hani-1 {
+ font-family: "DinglieZhuhai";
+}
+
+.font-Hani-2 {
+ font-family: "GudianMingChaokai";
+}
+
+.font-Hani-3 {
+ font-family: "NotoSansHK";
+}
+
+.font-Hani-4 {
+ font-family: "ShanShouGuHuangTi";
+}
+
+.font-Hani-5 {
+ font-family: "Iansui";
+}
+
+.font-Hani-6 {
+ font-family: "YeZiGongXingCao";
+}
+
+.font-Hani-7 {
+ font-family: "LiuJianMaoCao";
+}
+
+.font-Hani-8 {
+ font-family: "YiShanBeiZhuanti";
+}
+
+.font-Hani-9 {
+ font-family: "LongCang";
+}
+
+.font-Hani-10 {
+ font-family: "ZhiMangXing";
+}
+
+.font-Hani-11 {
+ font-family: "Meirenzhuan";
+}
+
+.font-Hani-12 {
+ font-family: "TianlongXingkai";
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/OFL.txt b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/OFL.txt
new file mode 100644
index 0000000..3e92931
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Black.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Black.ttf
new file mode 100644
index 0000000..71c0f99
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Black.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BlackItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BlackItalic.ttf
new file mode 100644
index 0000000..7aeb58b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BlackItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Bold.ttf
new file mode 100644
index 0000000..00559ee
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BoldItalic.ttf
new file mode 100644
index 0000000..e61e8e8
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-BoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBold.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBold.ttf
new file mode 100644
index 0000000..df70936
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..14d2b37
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLight.ttf
new file mode 100644
index 0000000..e76ec69
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLightItalic.ttf
new file mode 100644
index 0000000..89513d9
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ExtraLightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Italic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Italic.ttf
new file mode 100644
index 0000000..12b7b3c
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Light.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Light.ttf
new file mode 100644
index 0000000..bc36bcc
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-LightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-LightItalic.ttf
new file mode 100644
index 0000000..9e70be6
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-LightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Medium.ttf
new file mode 100644
index 0000000..6bcdcc2
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-MediumItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-MediumItalic.ttf
new file mode 100644
index 0000000..be67410
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-MediumItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Regular.ttf
new file mode 100644
index 0000000..9f0c71b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBold.ttf
new file mode 100644
index 0000000..74c726e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBoldItalic.ttf
new file mode 100644
index 0000000..3e6c942
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-SemiBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Thin.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Thin.ttf
new file mode 100644
index 0000000..03e7366
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-Thin.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ThinItalic.ttf b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ThinItalic.ttf
new file mode 100644
index 0000000..e26db5d
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/Poppins/Poppins-ThinItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Latn/style.css b/packages/prosody-ui/src/assets/fonts/Latn/style.css
new file mode 100644
index 0000000..e2aef9b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Latn/style.css
@@ -0,0 +1,11 @@
+@font-face {
+ font-family: "Poppins";
+ src: url(./Poppins.ttf);
+ font-weight: 300;
+ font-style: normal;
+}
+
+
+.font-Latn-0 {
+ font-family: "Poppins";
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Charm-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Charm-Bold.ttf
new file mode 100755
index 0000000..d32f072
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Charm-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Charm-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Charm-Regular.ttf
new file mode 100755
index 0000000..02013a4
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Charm-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Chonburi-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Chonburi-Regular.ttf
new file mode 100755
index 0000000..52fedc7
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Chonburi-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Bold.ttf
new file mode 100755
index 0000000..3a8583f
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-ExtraLight.ttf
new file mode 100755
index 0000000..c4161bd
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Light.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Light.ttf
new file mode 100755
index 0000000..a676550
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Medium.ttf
new file mode 100755
index 0000000..75e62df
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Regular.ttf
new file mode 100755
index 0000000..c31b8c2
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-SemiBold.ttf
new file mode 100755
index 0000000..9812b5a
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Thin.ttf b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Thin.ttf
new file mode 100755
index 0000000..2cc968f
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/IBMPlexSansThai-Thin.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Black.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Black.ttf
new file mode 100755
index 0000000..2e37a00
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Black.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BlackItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BlackItalic.ttf
new file mode 100755
index 0000000..dc81853
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BlackItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Bold.ttf
new file mode 100755
index 0000000..4686906
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BoldItalic.ttf
new file mode 100755
index 0000000..17eda2a
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-BoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBold.ttf
new file mode 100755
index 0000000..5240d38
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBoldItalic.ttf
new file mode 100755
index 0000000..037187a
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLight.ttf
new file mode 100755
index 0000000..404c1e0
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLightItalic.ttf
new file mode 100755
index 0000000..5d575cc
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ExtraLightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Italic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Italic.ttf
new file mode 100755
index 0000000..e6d868b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Light.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Light.ttf
new file mode 100755
index 0000000..3c9d4b5
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-LightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-LightItalic.ttf
new file mode 100755
index 0000000..59a3f2e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-LightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Medium.ttf
new file mode 100755
index 0000000..50413d0
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-MediumItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-MediumItalic.ttf
new file mode 100755
index 0000000..2c6aa14
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-MediumItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Regular.ttf
new file mode 100755
index 0000000..ef204c1
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBold.ttf
new file mode 100755
index 0000000..7501a2e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBoldItalic.ttf
new file mode 100755
index 0000000..2284650
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-SemiBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Thin.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Thin.ttf
new file mode 100755
index 0000000..5c835ad
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-Thin.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ThinItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ThinItalic.ttf
new file mode 100755
index 0000000..25fd1f9
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kanit-ThinItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Bold.ttf
new file mode 100755
index 0000000..cd36ffc
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-BoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-BoldItalic.ttf
new file mode 100755
index 0000000..f86a715
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-BoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLight.ttf
new file mode 100755
index 0000000..af416d2
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLightItalic.ttf
new file mode 100755
index 0000000..3478406
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-ExtraLightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Italic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Italic.ttf
new file mode 100755
index 0000000..3032d77
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Light.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Light.ttf
new file mode 100755
index 0000000..b3db128
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-LightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-LightItalic.ttf
new file mode 100755
index 0000000..5acfdd5
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-LightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Medium.ttf
new file mode 100755
index 0000000..de8f843
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-MediumItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-MediumItalic.ttf
new file mode 100755
index 0000000..2c0cb1e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-MediumItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Regular.ttf
new file mode 100755
index 0000000..b6cb5d5
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBold.ttf
new file mode 100755
index 0000000..917a988
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBoldItalic.ttf
new file mode 100755
index 0000000..36d3579
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Kodchasan-SemiBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Bold.ttf
new file mode 100755
index 0000000..db8c190
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-BoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-BoldItalic.ttf
new file mode 100755
index 0000000..a67a0ae
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-BoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLight.ttf
new file mode 100755
index 0000000..0e3f955
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLightItalic.ttf
new file mode 100755
index 0000000..6f4d3d3
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-ExtraLightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-Italic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Italic.ttf
new file mode 100755
index 0000000..479a8e8
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-Light.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Light.ttf
new file mode 100755
index 0000000..aaad3b6
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-LightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-LightItalic.ttf
new file mode 100755
index 0000000..cdfa5c6
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-LightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Medium.ttf
new file mode 100755
index 0000000..658a6a1
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-MediumItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-MediumItalic.ttf
new file mode 100755
index 0000000..4b88574
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-MediumItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Regular.ttf
new file mode 100755
index 0000000..70cd35b
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBold.ttf
new file mode 100755
index 0000000..acdce49
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBoldItalic.ttf
new file mode 100755
index 0000000..fc76e74
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Mali-SemiBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Bold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Bold.ttf
new file mode 100755
index 0000000..4d6bf36
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Bold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-BoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-BoldItalic.ttf
new file mode 100755
index 0000000..42857a5
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-BoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBold.ttf
new file mode 100755
index 0000000..27ba997
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBoldItalic.ttf
new file mode 100755
index 0000000..bc36f97
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLight.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLight.ttf
new file mode 100755
index 0000000..060c6c1
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLight.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLightItalic.ttf
new file mode 100755
index 0000000..beaa024
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ExtraLightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Italic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Italic.ttf
new file mode 100755
index 0000000..51d6dbe
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Italic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Light.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Light.ttf
new file mode 100755
index 0000000..d2f2291
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Light.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-LightItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-LightItalic.ttf
new file mode 100755
index 0000000..75eb8d8
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-LightItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Medium.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Medium.ttf
new file mode 100755
index 0000000..e03148e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Medium.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-MediumItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-MediumItalic.ttf
new file mode 100755
index 0000000..b172a09
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-MediumItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Regular.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Regular.ttf
new file mode 100755
index 0000000..50fa707
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Regular.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBold.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBold.ttf
new file mode 100755
index 0000000..7b760ce
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBold.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBoldItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBoldItalic.ttf
new file mode 100755
index 0000000..ecc5fb6
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-SemiBoldItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Thin.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Thin.ttf
new file mode 100755
index 0000000..cdaabcb
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-Thin.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ThinItalic.ttf b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ThinItalic.ttf
new file mode 100755
index 0000000..e96d17e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/Sarabun-ThinItalic.ttf
Binary files differ
diff --git a/packages/prosody-ui/src/assets/fonts/Thai/style.css b/packages/prosody-ui/src/assets/fonts/Thai/style.css
new file mode 100644
index 0000000..14b828c
--- /dev/null
+++ b/packages/prosody-ui/src/assets/fonts/Thai/style.css
@@ -0,0 +1,76 @@
+@font-face {
+ font-family: "Kanit";
+ src: url(./Kanit-Regular.ttf);
+}
+
+@font-face {
+ font-family: "Sarabun";
+ src: url(./Sarabun-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "IBM Plex Sans Thai";
+ src: url(./IBMPlexSansThai-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "Mali";
+ src: url(./Mali-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "Kodchasan";
+ src: url(./Kodchasan-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "Chonburi";
+ src: url(./Chonburi-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+@font-face {
+ font-family: "Charm";
+ src: url(./Charm-Regular.ttf);
+ font-weight: 300;
+ font-style: normal
+}
+
+
+
+.font-Thai-0 {
+ font-family: "Sarabun";
+}
+
+.font-Thai-1 {
+ font-family: "Kanit";
+}
+
+.font-Thai-2 {
+ font-family: "IBM Plex Sans Thai";
+}
+
+.font-Thai-3 {
+ font-family: "Mali";
+}
+
+.font-Thai-4 {
+ font-family: "Kodchasan";
+}
+
+.font-Thai-5 {
+ font-family: "Charm";
+}
+
+.font-Thai-6 {
+ font-family: "Chonburi";
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/icons/bookmark.svg b/packages/prosody-ui/src/assets/icons/bookmark.svg
new file mode 100755
index 0000000..07c1129
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/bookmark.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 19.6693V4C5 3.44772 5.44772 3 6 3H18C18.5523 3 19 3.44772 19 4V19.6693C19 20.131 18.4277 20.346 18.1237 19.9985L12 13L5.87629 19.9985C5.57227 20.346 5 20.131 5 19.6693Z" stroke="#000000" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/prosody-ui/src/assets/icons/font.svg b/packages/prosody-ui/src/assets/icons/font.svg
new file mode 100755
index 0000000..d4d5c72
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/font.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg fill="#000000" width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M4.51 2.6.25 13.67h1.34l1.49-3.86h4l1.52 3.86h1.34L5.68 2.6a.63.63 0 0 0-1.17 0zm-.95 6 1.54-4 1.53 4zm9.35-2.54a2.8 2.8 0 0 0-3 2.08l1.21.31a1.6 1.6 0 0 1 1.78-1.14c.77 0 1.59.26 1.59 1v.75c-.27 0-.63.09-.94.13a9.12 9.12 0 0 0-2.5.52 2.06 2.06 0 0 0-1.41 2.23 1.94 1.94 0 0 0 .88 1.44 3 3 0 0 0 1.62.43 4.39 4.39 0 0 0 1.36-.22 2.92 2.92 0 0 0 1-.52v.61h1.25V8.3c0-1.3-1.14-2.24-2.84-2.24zm.22 6.33a2.4 2.4 0 0 1-1.91-.07.64.64 0 0 1-.32-.52c-.1-.89.82-1.16 2.8-1.38l.76-.1c-.19 1.68-.94 1.94-1.33 2.07z"/></svg> \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/icons/heart.svg b/packages/prosody-ui/src/assets/icons/heart.svg
new file mode 100755
index 0000000..f728e1e
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/heart.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21 8.99998C21 12.7539 15.7156 17.9757 12.5857 20.5327C12.2416 20.8137 11.7516 20.8225 11.399 20.5523C8.26723 18.1523 3 13.1225 3 8.99998C3 2.00001 12 2.00002 12 8C12 2.00001 21 1.99999 21 8.99998Z" stroke="#000000" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/prosody-ui/src/assets/icons/play.svg b/packages/prosody-ui/src/assets/icons/play.svg
new file mode 100755
index 0000000..9a56073
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/play.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 -2 19 19" xmlns="http://www.w3.org/2000/svg">
+ <path fill="#000000" fill-rule="evenodd" d="M657,246.007484 C657,245.451066 657.450925,245 657.994771,245 L675.005229,245 C675.554626,245 676,245.44892 676,246.007484 L676,258.992516 C676,259.548934 675.549075,260 675.005229,260 L657.994771,260 C657.445374,260 657,259.55108 657,258.992516 L657,246.007484 Z M658,246 L675,246 L675,259 L658,259 L658,246 Z M670.113445,252.056723 C670.603076,252.301538 670.603593,252.698203 670.113445,252.943277 L664.886555,255.556723 C664.396924,255.801538 664,255.562119 664,254.997071 L664,250.002929 C664,249.449027 664.396407,249.198203 664.886555,249.443277 L670.113445,252.056723 Z" transform="translate(-657 -245)"/>
+</svg> \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/icons/quote.svg b/packages/prosody-ui/src/assets/icons/quote.svg
new file mode 100755
index 0000000..34f5de9
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/quote.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
+<g id="Quotemarks-right">
+ <path d="M14.1933422,9.4116497c-7.8260994,0-14.1922989,6.3662004-14.1922989,14.1924
+ c0,7.5498009,5.9247999,13.7420998,13.3690996,14.169899c0.1288996,1.3916016,0.0321999,5.1797028-3.5977001,10.4491997
+ C9.4980431,48.6206512,9.547843,49.1567497,9.888648,49.497551c1.4853945,1.4853973,2.4033947,2.4208984,3.0458946,3.0751991
+ c0.8408995,0.8554993,1.2247,1.2461014,1.7861996,1.7559013c0.1904001,0.1727982,0.4306002,0.259697,0.6719055,0.259697
+ c0.2342949,0,0.4676943-0.0819969,0.6561956-0.2450981c6.3251991-5.5038986,13.3515987-16.8759995,12.3349991-30.8115005
+ C27.7881413,15.3501501,21.820343,9.4116497,14.1933422,9.4116497z M15.4023428,52.2221489
+ c-0.2723999-0.2684975-0.5830002-0.5848999-1.0410004-1.0508003c-0.5565996-0.5672989-1.3203001-1.3446999-2.4784994-2.5067978
+ c4.4053001-6.7881012,3.5731993-11.6230011,3.2089996-12.3164024c-0.1729002-0.3290977-0.5274-0.5507965-0.8985004-0.5507965
+ c-6.7225995,0-12.1922989-5.4697018-12.1922989-12.1933022c0-6.7227001,5.4696999-12.1924,12.1922989-12.1924
+ c6.5489006,0,11.6777992,5.1582012,12.1963062,12.2646008C27.5322418,39.3501511,18.2168427,49.5268517,15.4023428,52.2221489z"/>
+ <path d="M63.9004402,23.5317497v-0.0009995C63.302742,15.3501501,57.3340416,9.4116497,49.7090416,9.4116497
+ c-7.8261986,0-14.1933937,6.3662004-14.1933937,14.1924c0,7.5498009,5.9257927,13.7420998,13.3710938,14.169899
+ c0.1289062,1.3906021,0.0312004,5.1767006-3.5996017,10.4491997c-0.2743988,0.3975029-0.2245979,0.9336014,0.1162033,1.2744026
+ c1.4794998,1.4794998,2.3955002,2.4130974,3.0380974,3.0663986c0.8446999,0.8613014,1.2304993,1.2538986,1.7949028,1.7656021
+ c0.1903992,0.1718979,0.4315987,0.2587967,0.6718979,0.2587967c0.2344055,0,0.4678001-0.0819969,0.6562004-0.2460976
+ C57.8896484,48.8383484,64.9160385,37.4663506,63.9004402,23.5317497z M50.917942,52.2221489
+ c-0.2743988-0.2705002-0.5877991-0.5887985-1.0498009-1.0594978c-0.5565987-0.5665016-1.3172989-1.3418007-2.4706993-2.4981003
+ c4.4053001-6.7891006,3.5742989-11.6230011,3.2109985-12.3164024c-0.1728973-0.3280983-0.5282974-0.5507965-0.8993988-0.5507965
+ c-6.7237015,0-12.1933937-5.4697018-12.1933937-12.1933022c0-6.7227001,5.4696922-12.1924,12.1933937-12.1924
+ c6.5477982,0,11.6777,5.1582012,12.1972008,12.2656002v-0.0009995
+ C63.0478401,39.3481483,53.7324409,49.5268517,50.917942,52.2221489z"/>
+</g>
+</svg> \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/icons/react.svg b/packages/prosody-ui/src/assets/icons/react.svg
new file mode 100755
index 0000000..6c87de9
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/react.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg> \ No newline at end of file
diff --git a/packages/prosody-ui/src/assets/icons/share.svg b/packages/prosody-ui/src/assets/icons/share.svg
new file mode 100755
index 0000000..92c2b94
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/share.svg
@@ -0,0 +1,28 @@
+<!--?xml version="1.0" encoding="utf-8"?-->
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#4B4B4B;}
+</style>
+<g>
+ <path class="st0" d="M398.73,227.402c62.563,0,113.27-50.793,113.27-113.359c0-62.656-50.707-113.36-113.27-113.36
+ c-62.656,0-113.364,50.704-113.364,113.36c0,11.587,1.733,22.711,4.926,33.292l-114.914,69.397
+ c-18.512-20.154-44.959-32.739-74.417-32.739C45.146,183.993,0,229.228,0,284.954c0,55.816,45.146,100.962,100.962,100.962
+ c30.736,0,58.278-13.778,76.79-35.482l86.824,45.787c-2.646,8.39-4.106,17.323-4.106,26.63
+ c0.093,48.878,39.673,88.466,88.555,88.466c48.976,0,88.556-39.588,88.556-88.466c0-48.976-39.58-88.554-88.556-88.554
+ c-26.812,0-50.886,11.942-67.122,30.825l-84.726-49.431c3.104-9.672,4.742-19.976,4.742-30.736c0-10.393-1.55-20.43-4.56-29.827
+ l118.013-64.294C335.985,213.268,365.715,227.402,398.73,227.402z M344.282,59.687c14.045-13.956,33.11-22.524,54.448-22.524
+ c21.251,0,40.31,8.567,54.356,22.524c13.862,13.956,22.434,33.016,22.434,54.356c0,21.25-8.572,40.399-22.434,54.354
+ c-14.046,13.956-33.105,22.525-54.356,22.525c-19.059,0-36.298-6.84-49.794-18.419h-0.094c-1.55-1.273-3.099-2.645-4.56-4.106
+ c-10.852-10.946-18.422-24.991-21.246-40.852c-0.824-4.382-1.189-8.942-1.189-13.502C321.846,92.703,330.419,73.644,344.282,59.687
+ z M164.343,296.532c-2.28,13.138-8.661,24.902-17.781,34.022c-0.731,0.73-1.55,1.461-2.373,2.192
+ c-11.49,10.393-26.536,16.69-43.227,16.69c-17.874,0-33.928-7.205-45.6-18.881c-11.676-11.765-18.881-27.725-18.881-45.6
+ c0-17.874,7.205-33.834,18.881-45.6c11.672-11.676,27.726-18.881,45.6-18.881c16.232,0,30.825,5.932,42.225,15.782
+ c1.185,0.997,2.28,2.004,3.376,3.099c9.027,9.12,15.413,20.698,17.781,33.746c0.73,3.83,1.095,7.748,1.095,11.854
+ C165.438,288.873,165.074,292.801,164.343,296.532z M297.773,413.73c1.915-10.767,7.022-20.253,14.499-27.725
+ c0.638-0.641,1.367-1.372,2.098-1.915c9.21-8.39,21.251-13.314,34.654-13.314c14.504,0,27.36,5.745,36.846,15.23
+ c9.485,9.485,15.23,22.346,15.23,36.845c0,14.411-5.745,27.272-15.23,36.748c-9.486,9.486-22.342,15.238-36.846,15.238
+ c-14.406,0-27.266-5.753-36.752-15.238c-9.485-9.476-15.23-22.337-15.322-36.748C296.95,419.751,297.225,416.643,297.773,413.73z"></path>
+</g>
+</svg>
diff --git a/packages/prosody-ui/src/assets/icons/speaker.svg b/packages/prosody-ui/src/assets/icons/speaker.svg
new file mode 100644
index 0000000..a16db51
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/speaker.svg
@@ -0,0 +1,32 @@
+<!--?xml version="1.0" encoding="utf-8"?-->
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#4B4B4B;}
+</style>
+<g>
+ <path class="st0" d="M242.908,77.65c-14.745,0-28.695,6.578-41.1,18.231L84.032,170.798H51.761c-4.486,0-8.883,0.893-12.887,2.457
+ c-7.042,2.77-12.814,7.426-17.569,12.941c-7.113,8.319-12.225,18.73-15.808,30.554C1.939,228.592,0,241.889,0,256
+ c0,10.751,1.126,21.037,3.235,30.546c3.19,14.28,8.418,26.855,16.086,36.828c3.861,4.969,8.382,9.312,13.816,12.556
+ c5.407,3.244,11.876,5.29,18.624,5.272h32.271l117.785,74.917c12.396,11.653,26.346,18.231,41.091,18.231
+ c51.529,0,93.3-79.849,93.3-178.35S294.437,77.65,242.908,77.65z M78.732,311.023H51.761c-0.58-0.009-1.116-0.09-1.894-0.394
+ c-1.313-0.491-3.378-1.823-5.684-4.512c-3.468-3.986-7.185-10.84-9.785-19.562c-2.628-8.723-4.219-19.258-4.219-30.555
+ c0-8.615,0.921-16.774,2.52-24.031c2.387-10.884,6.444-19.714,10.492-24.862c2.002-2.601,3.932-4.244,5.38-5.094
+ c1.483-0.858,2.342-1.01,3.19-1.037h26.971c-1.752,3.968-3.297,8.178-4.629,12.61c-3.808,12.646-5.925,27.069-5.925,42.414
+ C68.196,276.742,72.03,295.759,78.732,311.023z M166.32,357.771l-66.372-42.217c-3.915-5.648-7.463-13.101-10.081-21.877
+ c-3.289-10.948-5.228-23.861-5.228-37.677c-0.027-21.046,4.531-40.036,11.386-53.066c1.233-2.359,2.538-4.522,3.878-6.462
+ l66.417-42.244c-10.518,28.866-16.694,63.924-16.694,101.771C149.627,293.838,155.812,328.906,166.32,357.771z M242.908,404.161
+ c-25.719,0-63.102-57.712-63.102-148.161c0-90.448,37.383-148.17,63.102-148.17c25.738,0,63.111,57.722,63.111,148.17
+ C306.018,346.448,268.645,404.161,242.908,404.161z" style="fill: rgb(75, 75, 75);"></path>
+ <path class="st0" d="M243.605,212.684v-0.09c-0.169,0-0.33,0.045-0.491,0.054c-0.233-0.009-0.474-0.054-0.715-0.054v0.134
+ c-11.18,1.322-20.063,21.001-20.063,45.139s8.883,43.816,20.063,45.148v0.125c0.24,0,0.482-0.045,0.715-0.054
+ c0.161,0.009,0.322,0.054,0.491,0.054v-0.09c16.676-0.92,30.01-20.76,30.01-45.184
+ C273.614,233.452,260.281,213.604,243.605,212.684z" style="fill: rgb(75, 75, 75);"></path>
+ <path class="st0" d="M481.696,142.996l-25.085,13.986c17.185,30.859,26.658,65.524,26.666,103.013
+ c-0.008,37.472-9.481,72.137-26.666,102.996l25.085,13.995v-0.009c19.491-34.942,30.313-74.63,30.304-116.981
+ C512.009,217.626,501.187,177.938,481.696,142.996z" style="fill: rgb(75, 75, 75);"></path>
+ <path class="st0" d="M383.026,195.588c9.384,19.456,14.558,41.073,14.567,64.398c-0.009,23.325-5.183,44.952-14.567,64.389
+ l25.862,12.484c11.225-23.244,17.427-49.268,17.418-76.873c0.008-27.614-6.194-53.629-17.418-76.882L383.026,195.588z" style="fill: rgb(75, 75, 75);"></path>
+</g>
+</svg>
diff --git a/packages/prosody-ui/src/assets/icons/spinner.svg b/packages/prosody-ui/src/assets/icons/spinner.svg
new file mode 100755
index 0000000..2a516de
--- /dev/null
+++ b/packages/prosody-ui/src/assets/icons/spinner.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_z9k8{transform-origin:center;animation:spinner_StKS .75s infinite linear}@keyframes spinner_StKS{100%{transform:rotate(360deg)}}</style><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z" class="spinner_z9k8"/></svg>
diff --git a/packages/prosody-ui/src/components/Sentence.tsx b/packages/prosody-ui/src/components/Sentence.tsx
new file mode 100644
index 0000000..33144ac
--- /dev/null
+++ b/packages/prosody-ui/src/components/Sentence.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import { notRandomFromArray } from "sortug";
+import "./sentence.css";
+
+export function ColoredText({
+ frags,
+ fn,
+ lang,
+}: {
+ frags: string[];
+ fn?: (s: string) => void;
+ lang?: string;
+}) {
+ return (
+ <>
+ {frags.map((s, i) => {
+ const prev = frags[i - 1];
+ const prevC = prev ? notRandomFromArray(prev, colors) : "lol";
+ const color = notRandomFromArray(s, colors);
+ const opacity = prev && prevC === color ? 0.8 : 1;
+ const style = { color, opacity };
+ console.log({ style });
+ return <CTInner lang={lang} key={s + i} s={s} style={style} fn={fn} />;
+ })}
+ </>
+ );
+}
+
+export function CTInner({
+ s,
+ style,
+ fn,
+ lang,
+}: {
+ s: string;
+ style: any;
+ fn?: (s: string) => void;
+ lang?: string;
+}) {
+ function handleClick(e: React.MouseEvent<HTMLSpanElement>) {
+ console.log(!!fn, "fn");
+ if (fn) fn(e.currentTarget.innerText.trim());
+ }
+ return (
+ <span lang={lang} onClick={handleClick} className="word cp" style={style}>
+ {s}
+ </span>
+ );
+}
+export const colors = [
+ "#8c2c2c",
+ "#000000",
+ "#ffd400",
+ "#1513a0",
+ "#7e7e7e",
+ "1eb52d",
+];
diff --git a/packages/prosody-ui/src/components/Word.tsx b/packages/prosody-ui/src/components/Word.tsx
new file mode 100644
index 0000000..82939ce
--- /dev/null
+++ b/packages/prosody-ui/src/components/Word.tsx
@@ -0,0 +1,119 @@
+import React, { useCallback, useEffect, useState } from "react";
+import spinner from "../assets/icons/spinner.svg";
+import likeIcon from "../assets/icons/heart.svg";
+import commentsIcon from "../assets/icons/quote.svg";
+import shareIcon from "../assets/icons/share.svg";
+import fontIcon from "../assets/icons/font.svg";
+import bookmarkIcon from "../assets/icons/bookmark.svg";
+import speakerIcon from "../assets/icons/speaker.svg";
+import type { AnalyzeRes, Meaning } from "../logic/types";
+import { ColoredText } from "./Sentence.tsx";
+import { P, Span, useSpeechSynthesis } from "../hooks/useLang.tsx";
+
+function Word({ data, lang }: { data: AnalyzeRes; lang: string }) {
+ async function load() {
+ // const wiki = await fetchWiki(data.word);
+ // console.log(wiki, "wiki res");
+ // if ("ok" in wiki) setM(wiki.ok.meanings);
+ // else setError(wiki.error);
+ // setLoading(false);
+ }
+ useEffect(() => {
+ load();
+ }, []);
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(true);
+ const [meanings, setM] = useState<Meaning[]>([]);
+ const [font, setFont] = useState(0);
+
+ function changeFont() {
+ if (font === 6) setFont(0);
+ else setFont(font + 1);
+ }
+ const { voices, speaking, speak, stop } = useSpeechSynthesis();
+ function playAudio() {
+ console.log({ voices, speaking });
+ console.log("word", data.word);
+ speak(data.word);
+ }
+
+ async function saveW() {}
+
+ return (
+ <div className={`font-${font}`} id="word-modal" title={data.word}>
+ <img className="font-icon cp" onClick={changeFont} src={fontIcon} />
+ <img className="save-icon cp" onClick={saveW} src={bookmarkIcon} />
+ <div className="original">
+ <ColoredText frags={data.syllables} />
+ </div>
+ <div className="pronunciation IPA flex1 flex-center">
+ <P>{`/${data.ipa.replace(/\s/g, "")}/`}</P>
+ <img onClick={playAudio} className="icon cp" src={speakerIcon} />
+ </div>
+ <div className="meanings">
+ {loading ? (
+ <img src={spinner} className="spinner bc" />
+ ) : (
+ meanings.map((m) => (
+ <div key={JSON.stringify(m)} className="meaning">
+ <div className="pos">
+ <Span>{m.pos}</Span>
+ </div>
+ <ol>
+ {m.meaning.map((t, i) => (
+ <li key={t + i} className="translation">
+ <P>{t}</P>
+ </li>
+ ))}
+ </ol>
+ </div>
+ ))
+ )}
+ {error && <div className="error">{error}</div>}
+ </div>
+ </div>
+ );
+}
+
+export default Word;
+
+// function FloatingButtons({
+// tweet,
+// avatar,
+// changeFont,
+// }: {
+// tweet: Tweet;
+// avatar: string;
+// changeFont: any;
+// }) {
+// const { apiKeys, prompts, setModal } = useGlobalState();
+// function openUser() {}
+// function openComments() {}
+// function openShare() {}
+// async function doLike() {
+// const key = apiKeys.openai;
+// // TODO hide button if key not set
+// console.log(tweet.text, "whole text");
+// console.log(tweet.media, "media");
+// if (tweet.media[0] && !tweet.media[0].isVideo) {
+// const res = await vision(tweet.media[0].url, "", key);
+// console.log(res, "vision res");
+// }
+// // const res = await translate(tweet.text, prompts.translate,key);
+// // if ("ok" in res)
+// // setModal(<TranslationModal text={res.ok.choices[0].message.content}/>)
+// }
+// async function doBookmark() {}
+// return (
+// <div id="entry-icons">
+// <div className="avatar">
+// <img className="cp" onClick={openUser} src={avatar} />
+// </div>
+// <img className="cp" onClick={doLike} src={likeIcon} />
+// <img className="cp" onClick={openComments} src={commentsIcon} />
+// <img className="cp" onClick={doBookmark} src={bookmarkIcon} />
+// <img className="cp" onClick={openShare} src={shareIcon} />
+// <img className="cp" onClick={changeFont} src={fontIcon} />
+// </div>
+// );
+// }
diff --git a/packages/prosody-ui/src/components/sentence.css b/packages/prosody-ui/src/components/sentence.css
new file mode 100644
index 0000000..0bd0a49
--- /dev/null
+++ b/packages/prosody-ui/src/components/sentence.css
@@ -0,0 +1,272 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+#root>.spinner {
+ width: 100px;
+ height: 100px;
+}
+
+#entry>.spinner {
+ width: 80px;
+ height: 80px;
+}
+
+
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+#cookies {
+ & .active {
+ background-color: var(--huang);
+ }
+
+ & input {
+ margin-left: 1rem;
+ width: 100%;
+ }
+
+ & textarea {
+ width: 100%;
+ height: 500px;
+ resize: none;
+ outline: none;
+ }
+}
+
+#entry {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ padding: 1rem;
+ /* prov */
+ border: 2px solid black;
+
+ & div[lang="th"] {
+
+ & .tw-text,
+ & .tw-hashtag {
+ font-size: 3rem;
+ }
+ }
+
+ & .text-wrapper {
+ display: block;
+ margin: 0.5rem 0;
+ /* overflow: hidden; */
+ }
+
+ & .word {
+ display: inline-block;
+ transition: transform 0.3s ease;
+ }
+
+ & .word:hover {
+ transform: scale(1.4);
+ background-color: white;
+ }
+
+ & #tw-media {
+ max-width: 100%;
+
+ & img,
+ & video {
+ max-width: 100%;
+ }
+ }
+}
+
+#inner {
+ height: 100%;
+ max-height: 100%;
+ overflow-y: auto;
+}
+
+#entry-icons {
+ position: absolute;
+ bottom: 5%;
+ right: 5%;
+ width: 50px;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ & .avatar {
+ border: 2px solid black;
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ overflow: hidden;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
+
+ & img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+}
+
+#word-modal {
+ position: relative;
+
+ & .font-icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 32px;
+ height: 32px;
+ }
+
+ & .save-icon {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 32px;
+ height: 32px;
+ }
+
+ & .original {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+ }
+
+ & .syllable {}
+
+ & .IPA {
+ font-size: 1.6rem;
+ line-height: 1.6rem;
+ & img{
+ width: 50px;
+ margin-left: 1rem;
+ }
+ }
+
+ & .meanings {
+
+ & .spinner {
+ width: 80px;
+ height: 80px;
+ }
+
+ & .meaning {
+ margin: 1rem auto;
+ }
+
+ & .pos {
+ font-size: 1.2rem;
+ margin-bottom: 0.3rem;
+ text-align: left;
+ }
+
+ & ol {
+ word-wrap: normal;
+ margin: auto;
+ text-align: left;
+ }
+ }
+}
+
+img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+.flex1{
+ width: 100%;
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+.flex-center{
+ justify-content: center;
+}
+
+/* p { */
+/* position: absolute; */
+/* top: 50%; */
+/* left: 50%; */
+/* transform: translate(-50%, -50%); */
+/* color: white; */
+/* background-color: rgba(0, 0, 0, 0.5); */
+/* padding: 10px; */
+/* border-radius: 5px; */
+/* } */
+#modal-bg{
+ height: 100vh;
+ width: 100vw;
+ background-color: rgb(0, 0, 0, 0.9);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 100;
+}
+
+#modal-fg {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 80%;
+ z-index: 101;
+ transform: translate(-50%, -50%);
+ /* background-color: var(--background-color); */
+ background-color: lightgrey;
+ font-size: 1.2rem;
+ padding: 1rem;
+ max-height: 80%;
+ overflow-y: scroll;
+}
+
+
+.text-ipa{
+ font-size: 1.5rem;
+}
+.text-thai{
+ font-size: 1.5rem;
+}
+
+
+/* // new */
+.word.cp{
+ margin: 0 0.5ch;
+}
diff --git a/packages/prosody-ui/src/components/word.css b/packages/prosody-ui/src/components/word.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/prosody-ui/src/components/word.css
diff --git a/packages/prosody-ui/src/files.d.ts b/packages/prosody-ui/src/files.d.ts
new file mode 100644
index 0000000..5e52f80
--- /dev/null
+++ b/packages/prosody-ui/src/files.d.ts
@@ -0,0 +1,4 @@
+declare module "*.svg" {
+ const content: string;
+ export default content;
+}
diff --git a/packages/prosody-ui/src/fonts/FontChanger.tsx b/packages/prosody-ui/src/fonts/FontChanger.tsx
new file mode 100644
index 0000000..15c932e
--- /dev/null
+++ b/packages/prosody-ui/src/fonts/FontChanger.tsx
@@ -0,0 +1,64 @@
+import React, { useEffect, useState, type ReactNode } from "react";
+import fontIcon from "../assets/icons/font.svg";
+import { getScriptPredictor } from "glotscript";
+import ThaiFontLoader from "./Thai";
+
+function FontChanger({ text }: { text: string }) {
+ const [script, setScript] = useState<string | null>(null);
+ useEffect(() => {
+ const predictor = getScriptPredictor();
+ const res = predictor(text);
+ console.log("script predicted", res);
+ setScript(res[0]);
+ }, [text]);
+ useEffect(() => {
+ if (script === "Hani") setFontCount(12);
+ else if (script === "Thai") setFontCount(6);
+ else if (script === "Jpan") setFontCount(5);
+ // else if (script === "Latn") setFontCount(6)
+ }, [script]);
+ const [fontIdx, setFont] = useState(0);
+ const [fontCount, setFontCount] = useState(0);
+ function changeFont() {
+ if (fontIdx === fontCount) setFont(0);
+ else setFont((prev) => prev + 1);
+ }
+ return (
+ <div
+ className={`font-changer font-${script}-${fontIdx}`}
+ lang={script || ""}
+ >
+ <img
+ className="font-icon cp"
+ style={{ width: 25 }}
+ onClick={changeFont}
+ src={fontIcon}
+ />
+ {script === "Thai" ? <ThaiFontLoader text={text} /> : null}
+ </div>
+ );
+}
+// function FontChanger({
+// lang,
+// children,
+// }: {
+// lang: string;
+// children: ReactNode;
+// }) {
+// useEffect(() => {}, []);
+// const [script, setScript] = useState("Latn");
+// const [fontIdx, setFont] = useState(0);
+// const fontCount = 6;
+// function changeFont() {
+// if (fontIdx === fontCount) setFont(0);
+// else setFont((prev) => prev + 1);
+// }
+// return (
+// <div className="font-changer" lang={script}>
+// <img className="font-icon cp" onClick={changeFont} src={fontIcon} />
+// {children}
+// </div>
+// );
+// }
+
+export default FontChanger;
diff --git a/packages/prosody-ui/src/fonts/Hani.tsx b/packages/prosody-ui/src/fonts/Hani.tsx
new file mode 100644
index 0000000..f9cc602
--- /dev/null
+++ b/packages/prosody-ui/src/fonts/Hani.tsx
@@ -0,0 +1,14 @@
+import React, { useState, type ReactNode } from "react";
+import "../assets/fonts/Hani/style.css";
+
+function ChineseFontLoader({ children }: { children: ReactNode }) {
+ const [fontIdx, setFont] = useState(0);
+ const fontCount = 12;
+ function changeFont() {
+ if (fontIdx === fontCount) setFont(0);
+ else setFont((prev) => prev + 1);
+ }
+ return <div>{children}</div>;
+}
+
+export default ChineseFontLoader;
diff --git a/packages/prosody-ui/src/fonts/Thai.tsx b/packages/prosody-ui/src/fonts/Thai.tsx
new file mode 100644
index 0000000..0048316
--- /dev/null
+++ b/packages/prosody-ui/src/fonts/Thai.tsx
@@ -0,0 +1,8 @@
+import React, { useState, type ReactNode } from "react";
+import "../assets/fonts/Thai/style.css";
+
+function ThaiFontLoader({ text }: { text: string }) {
+ return <div>{text}</div>;
+}
+
+export default ThaiFontLoader;
diff --git a/packages/prosody-ui/src/fonts/useLangFont.tsx b/packages/prosody-ui/src/fonts/useLangFont.tsx
new file mode 100644
index 0000000..36fa603
--- /dev/null
+++ b/packages/prosody-ui/src/fonts/useLangFont.tsx
@@ -0,0 +1,44 @@
+import React, { useEffect, useState, type ReactNode } from "react";
+import fontIcon from "../assets/icons/font.svg";
+import { getScriptPredictor } from "glotscript";
+
+function useLangFont({ text }: { text: string }) {
+ useEffect(() => {
+ const predictor = getScriptPredictor();
+ const res = predictor(text);
+ console.log("script predicted", res);
+ setScript(res[0]);
+ }, [text]);
+ const [script, setScript] = useState<string | null>(null);
+ const [fontIdx, setFont] = useState(0);
+ const fontCount = 6;
+ function changeFont() {
+ if (fontIdx === fontCount) setFont(0);
+ else setFont((prev) => prev + 1);
+ }
+ // if (script === "Hani") return {}
+}
+// function FontChanger({
+// lang,
+// children,
+// }: {
+// lang: string;
+// children: ReactNode;
+// }) {
+// useEffect(() => {}, []);
+// const [script, setScript] = useState("Latn");
+// const [fontIdx, setFont] = useState(0);
+// const fontCount = 6;
+// function changeFont() {
+// if (fontIdx === fontCount) setFont(0);
+// else setFont((prev) => prev + 1);
+// }
+// return (
+// <div className="font-changer" lang={script}>
+// <img className="font-icon cp" onClick={changeFont} src={fontIcon} />
+// {children}
+// </div>
+// );
+// }
+
+export default useLangFont;
diff --git a/packages/prosody-ui/src/hooks/useLang.tsx b/packages/prosody-ui/src/hooks/useLang.tsx
new file mode 100644
index 0000000..687c81d
--- /dev/null
+++ b/packages/prosody-ui/src/hooks/useLang.tsx
@@ -0,0 +1,184 @@
+import {
+ useState,
+ useEffect,
+ createContext,
+ useContext,
+ useCallback,
+ type ElementType,
+} from "react";
+type ScriptClass =
+ | "text-ipa"
+ | "text-rtl"
+ | "text-cjk"
+ | "text-thai"
+ | "text-cyrillic"
+ | "text-latin";
+interface ScriptContextType {
+ getClass: (text: string) => ScriptClass;
+}
+const ScriptContext = createContext({
+ getClass: (text: string) => "text-latin" as ScriptClass,
+});
+
+const getScriptClass = (text: string): ScriptClass => {
+ // You can combine these with includes() if text has multiple scripts
+ if (/[\u0591-\u07FF\u200F\u202B]/.test(text)) return "text-rtl";
+
+ if (/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/.test(text)) return "text-cjk";
+
+ if (/[\u0E00-\u0E7F]/.test(text)) return "text-thai";
+
+ if (/[\u0400-\u04FF]/.test(text)) return "text-cyrillic";
+
+ if (/[\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF]/.test(text))
+ return "text-ipa";
+
+ return "text-latin"; // default
+};
+
+export const ScripProvider: React.FC<{ children: React.ReactNode }> = ({
+ children,
+}) => {
+ const getClass = useCallback((text: string) => {
+ return getScriptClass(text);
+ }, []);
+
+ return (
+ <ScriptContext.Provider value={{ getClass }}>
+ {children}
+ </ScriptContext.Provider>
+ );
+};
+
+export const useScript = () => useContext(ScriptContext);
+
+type TextElementProps = {
+ children?: React.ReactNode;
+ className?: string;
+};
+const createTextElement = (Component: ElementType) => {
+ return function TextElement({
+ children,
+ className,
+ ...rest
+ }: TextElementProps & React.ComponentPropsWithoutRef<ElementType>) {
+ const writingSystemClass =
+ typeof children === "string" ? getScriptClass(children) : "text-latin";
+
+ return (
+ <Component
+ className={`${writingSystemClass} ${className || ""}`}
+ {...rest}
+ >
+ {children}
+ </Component>
+ );
+ };
+};
+
+// Create all the text elements you need
+export const Span = createTextElement("span");
+export const P = createTextElement("p");
+export const H1 = createTextElement("h1");
+export const H2 = createTextElement("h2");
+export const H3 = createTextElement("h3");
+export const H4 = createTextElement("h4");
+export const H5 = createTextElement("h5");
+export const H6 = createTextElement("h6");
+export const Label = createTextElement("label");
+export const Small = createTextElement("small");
+
+interface Voice {
+ default: boolean;
+ lang: string;
+ localService: boolean;
+ name: string;
+ voiceURI: string;
+}
+
+export const useSpeechSynthesis = () => {
+ const [voices, setVoices] = useState<Voice[]>([]);
+ const [speaking, setSpeaking] = useState(false);
+ console.log({ voices }, "voices hook");
+
+ useEffect(() => {
+ // Function to get voices
+ const updateVoices = () => {
+ // Some browsers need a small delay for voices to be available
+ setTimeout(() => {
+ const availableVoices = window.speechSynthesis.getVoices();
+ if (availableVoices.length > 0) {
+ setVoices(availableVoices);
+ }
+ }, 100);
+ };
+
+ // Get initial voices
+ updateVoices();
+
+ window.speechSynthesis.addEventListener("voiceschanged", updateVoices);
+
+ // Cleanup
+ return () => {
+ window.speechSynthesis.removeEventListener("voiceschanged", updateVoices);
+ };
+ }, []);
+
+ const speak = (text: string, voiceName?: string) => {
+ const utterance = new SpeechSynthesisUtterance(text);
+
+ if (voiceName) {
+ const voice = voices.find((v) => v.name === voiceName);
+ if (voice) utterance.voice = voice;
+ }
+
+ utterance.onstart = () => setSpeaking(true);
+ utterance.onend = () => setSpeaking(false);
+ utterance.onerror = () => setSpeaking(false);
+
+ window.speechSynthesis.speak(utterance);
+ };
+
+ const stop = () => {
+ window.speechSynthesis.cancel();
+ setSpeaking(false);
+ };
+
+ return {
+ voices,
+ speaking,
+ speak,
+ stop,
+ };
+};
+
+// Example usage in a component:
+const SpeechComponent = () => {
+ const { voices, speaking, speak, stop } = useSpeechSynthesis();
+ const [selectedVoice, setSelectedVoice] = useState<string>("");
+
+ return (
+ <div>
+ <select
+ value={selectedVoice}
+ onChange={(e) => setSelectedVoice(e.target.value)}
+ >
+ <option value="">Default Voice</option>
+ {voices.map((voice) => (
+ <option key={voice.name} value={voice.name}>
+ {voice.name} ({voice.lang})
+ </option>
+ ))}
+ </select>
+
+ <button
+ onClick={() => speak("Hello, world!", selectedVoice)}
+ disabled={speaking}
+ >
+ {speaking ? "Speaking..." : "Speak"}
+ </button>
+
+ {speaking && <button onClick={stop}>Stop</button>}
+ </div>
+ );
+};
diff --git a/packages/prosody-ui/src/hooks/useModal.tsx b/packages/prosody-ui/src/hooks/useModal.tsx
new file mode 100644
index 0000000..7164bcb
--- /dev/null
+++ b/packages/prosody-ui/src/hooks/useModal.tsx
@@ -0,0 +1,53 @@
+import { franc } from "franc-all";
+import React, {
+ ReactElement,
+ ReactNode,
+ useCallback,
+ useEffect,
+ useRef,
+ useState,
+} from "react";
+
+function useModal() {
+ const [achild, setChild] = useState<ReactNode>(null);
+ return <Modal close={() => setChild(null)}>{achild}</Modal>;
+}
+export default useModal;
+
+function Modal({
+ children,
+ height = "fit-content",
+ width = "80%",
+ close,
+}: {
+ children: ReactNode;
+ close: () => void;
+ width?: any;
+ height?: any;
+}) {
+ function onKey(event: any) {
+ if (event.key === "Escape") close();
+
+ useEffect(() => {
+ document.addEventListener("keyup", onKey);
+ return () => {
+ document.removeEventListener("keyup", onKey);
+ };
+ }, [children]);
+ }
+
+ function clickAway(e: React.MouseEvent) {
+ e.stopPropagation();
+ if (!modalRef.current || !modalRef.current.contains(e.target as any))
+ close();
+ }
+ const modalRef = useRef<HTMLDivElement>(null);
+ const style = { width, height };
+ return (
+ <div id="modal-bg" onClick={clickAway}>
+ <div id="modal-fg" style={style} ref={modalRef}>
+ {children}
+ </div>
+ </div>
+ );
+}
diff --git a/packages/prosody-ui/src/hooks/useTTS.tsx b/packages/prosody-ui/src/hooks/useTTS.tsx
new file mode 100644
index 0000000..671d078
--- /dev/null
+++ b/packages/prosody-ui/src/hooks/useTTS.tsx
@@ -0,0 +1,3 @@
+function useTTS(text: string) {}
+
+export default useTTS;
diff --git a/packages/prosody-ui/src/latin/LatinText.tsx b/packages/prosody-ui/src/latin/LatinText.tsx
new file mode 100644
index 0000000..e5b13ff
--- /dev/null
+++ b/packages/prosody-ui/src/latin/LatinText.tsx
@@ -0,0 +1,77 @@
+import React, { useCallback, useEffect, useState } from "react";
+import type { AnalyzeRes } from "../logic/types";
+import { ColoredText } from "../components/Sentence";
+import Word from "../components/Word";
+import { iso6393To1 } from "../logic/iso6393to1";
+
+function segmentate(
+ text: string,
+ lang: string,
+ granularity: "sentence" | "word" | "grapheme",
+) {
+ // TODO proper error handling here
+ console.log("segmenting", lang);
+ const la = iso6393To1[lang];
+ const lng = la || lang;
+ const segmenter = new Intl.Segmenter(lng, { granularity });
+ const segments = Array.from(segmenter.segment(text));
+ console.log("seg", segments[0]);
+ return segments.reduce((acc: string[], s) => {
+ const trimmed = s.segment.trim();
+ if (trimmed) return [...acc, trimmed];
+ else return acc;
+ }, []);
+}
+
+export default function LatinText({
+ text,
+ lang,
+ openWord,
+}: {
+ text: string;
+ lang: string;
+ openWord?: (word: string) => void;
+}) {
+ useEffect(() => {
+ const sentences = segmentate(text, lang, "sentence");
+ if (sentences) setSentences(sentences);
+ }, [text]);
+ const [sentences, setSentences] = useState<string[]>([]);
+ console.log({ sentences });
+ return (
+ <>
+ {sentences.map((s, i) => (
+ <Sentence key={s + i} text={s} lang={lang} openWord={openWord} />
+ ))}
+ </>
+ );
+}
+
+function Sentence({
+ text,
+ lang,
+ openWord,
+}: {
+ text: string;
+ lang: string;
+
+ openWord?: (word: string) => void;
+}) {
+ useEffect(() => {
+ const w = segmentate(text, lang, "word");
+ if (w) setWords(w);
+ console.log({ words });
+ }, [text]);
+ const [words, setWords] = useState<string[]>([]);
+ console.log({ words });
+
+ // const [data, setData] = useState<Record<string, AnalyzeRes>>({});
+ const [word, setWord] = useState<AnalyzeRes>();
+
+ return (
+ <>
+ <ColoredText frags={words} fn={openWord} />;
+ {word && <Word data={word} lang={lang} />}
+ </>
+ );
+}
diff --git a/packages/prosody-ui/src/logic/iso6393to1.ts b/packages/prosody-ui/src/logic/iso6393to1.ts
new file mode 100644
index 0000000..4c4deed
--- /dev/null
+++ b/packages/prosody-ui/src/logic/iso6393to1.ts
@@ -0,0 +1,186 @@
+export const iso6393To1: Record<string, string> = {
+ aar: "aa",
+ abk: "ab",
+ afr: "af",
+ aka: "ak",
+ amh: "am",
+ ara: "ar",
+ arg: "an",
+ asm: "as",
+ ava: "av",
+ ave: "ae",
+ aym: "ay",
+ aze: "az",
+ bak: "ba",
+ bam: "bm",
+ bel: "be",
+ ben: "bn",
+ bis: "bi",
+ bod: "bo",
+ bos: "bs",
+ bre: "br",
+ bul: "bg",
+ cat: "ca",
+ ces: "cs",
+ cha: "ch",
+ che: "ce",
+ chu: "cu",
+ chv: "cv",
+ cor: "kw",
+ cos: "co",
+ cre: "cr",
+ cym: "cy",
+ dan: "da",
+ deu: "de",
+ div: "dv",
+ dzo: "dz",
+ ell: "el",
+ eng: "en",
+ epo: "eo",
+ est: "et",
+ eus: "eu",
+ ewe: "ee",
+ fao: "fo",
+ fas: "fa",
+ fij: "fj",
+ fin: "fi",
+ fra: "fr",
+ fry: "fy",
+ ful: "ff",
+ gla: "gd",
+ gle: "ga",
+ glg: "gl",
+ glv: "gv",
+ grn: "gn",
+ guj: "gu",
+ hat: "ht",
+ hau: "ha",
+ hbs: "sh",
+ heb: "he",
+ her: "hz",
+ hin: "hi",
+ hmo: "ho",
+ hrv: "hr",
+ hun: "hu",
+ hye: "hy",
+ ibo: "ig",
+ ido: "io",
+ iii: "ii",
+ iku: "iu",
+ ile: "ie",
+ ina: "ia",
+ ind: "id",
+ ipk: "ik",
+ isl: "is",
+ ita: "it",
+ jav: "jv",
+ jpn: "ja",
+ kal: "kl",
+ kan: "kn",
+ kas: "ks",
+ kat: "ka",
+ kau: "kr",
+ kaz: "kk",
+ khm: "km",
+ kik: "ki",
+ kin: "rw",
+ kir: "ky",
+ kom: "kv",
+ kon: "kg",
+ kor: "ko",
+ kua: "kj",
+ kur: "ku",
+ lao: "lo",
+ lat: "la",
+ lav: "lv",
+ lim: "li",
+ lin: "ln",
+ lit: "lt",
+ ltz: "lb",
+ lub: "lu",
+ lug: "lg",
+ mah: "mh",
+ mal: "ml",
+ mar: "mr",
+ mkd: "mk",
+ mlg: "mg",
+ mlt: "mt",
+ mon: "mn",
+ mri: "mi",
+ msa: "ms",
+ mya: "my",
+ nau: "na",
+ nav: "nv",
+ nbl: "nr",
+ nde: "nd",
+ ndo: "ng",
+ nep: "ne",
+ nld: "nl",
+ nno: "nn",
+ nob: "nb",
+ nor: "no",
+ nya: "ny",
+ oci: "oc",
+ oji: "oj",
+ ori: "or",
+ orm: "om",
+ oss: "os",
+ pan: "pa",
+ pli: "pi",
+ pol: "pl",
+ por: "pt",
+ pus: "ps",
+ que: "qu",
+ roh: "rm",
+ ron: "ro",
+ run: "rn",
+ rus: "ru",
+ sag: "sg",
+ san: "sa",
+ sin: "si",
+ slk: "sk",
+ slv: "sl",
+ sme: "se",
+ smo: "sm",
+ sna: "sn",
+ snd: "sd",
+ som: "so",
+ sot: "st",
+ spa: "es",
+ sqi: "sq",
+ srd: "sc",
+ srp: "sr",
+ ssw: "ss",
+ sun: "su",
+ swa: "sw",
+ swe: "sv",
+ tah: "ty",
+ tam: "ta",
+ tat: "tt",
+ tel: "te",
+ tgk: "tg",
+ tgl: "tl",
+ tha: "th",
+ tir: "ti",
+ ton: "to",
+ tsn: "tn",
+ tso: "ts",
+ tuk: "tk",
+ tur: "tr",
+ twi: "tw",
+ uig: "ug",
+ ukr: "uk",
+ urd: "ur",
+ uzb: "uz",
+ ven: "ve",
+ vie: "vi",
+ vol: "vo",
+ wln: "wa",
+ wol: "wo",
+ xho: "xh",
+ yid: "yi",
+ yor: "yo",
+ zha: "za",
+ zho: "zh",
+ zul: "zu",
+};
diff --git a/packages/prosody-ui/src/logic/stanza.ts b/packages/prosody-ui/src/logic/stanza.ts
new file mode 100644
index 0000000..9e59450
--- /dev/null
+++ b/packages/prosody-ui/src/logic/stanza.ts
@@ -0,0 +1,86 @@
+import type { AsyncRes, Result } from "sortug";
+
+const ENDPOINT = "http://localhost:8102";
+export async function segmenter(text: string, lang: string) {
+ try {
+ const body = JSON.stringify({ lang, string: text });
+ const opts = {
+ headers: { "Content-type": "application/json" },
+ method: "POST",
+ body,
+ };
+ const res = await fetch(ENDPOINT + "/segment", opts);
+ console.log("stanza", res);
+ const j = await res.json();
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+export async function idLang(text: string) {
+ try {
+ const body = JSON.stringify({ string: text });
+ const opts = {
+ headers: { "Content-type": "application/json" },
+ method: "POST",
+ body,
+ };
+ const res = await fetch(ENDPOINT + "/detect-lang", opts);
+ const j = await res.json();
+ return { ok: j };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+
+export type Sentence = {
+ text: string;
+ sentiment: number;
+ constituency: string;
+ dependencies: Dependency[];
+ entities: Entity[];
+ tokens: Token[];
+ words: Word[];
+};
+export type Dependency = Array<[Word, string, Word]>;
+export type Word = {
+ id: number;
+ text: string;
+ lemma: string;
+ upos: string;
+ xpos: string;
+ feats: string;
+ head: number;
+ deprel: string;
+ start_char: number;
+ end_char: number;
+};
+export type Token = {
+ id: [number, number];
+ text: string;
+ misc: string;
+ words: Word[];
+ start_char: number;
+ end_char: number;
+ ner: string;
+};
+export type Entity = {
+ text: string;
+ misc: string;
+ start_char: number;
+ end_char: number;
+ type: string;
+};
+// "amod",
+// {
+// "id": 1,
+// "text": "Stony",
+// "lemma": "Stony",
+// "upos": "ADJ",
+// "xpos": "NNP",
+// "feats": "Degree=Pos",
+// "head": 3,
+// "deprel": "amod",
+// "start_char": 0,
+// "end_char": 5
+// }
diff --git a/packages/prosody-ui/src/logic/types.ts b/packages/prosody-ui/src/logic/types.ts
new file mode 100644
index 0000000..ac308cf
--- /dev/null
+++ b/packages/prosody-ui/src/logic/types.ts
@@ -0,0 +1,48 @@
+export type Cookie = {
+ domain: string;
+ path: string;
+ hostOnly: boolean;
+ httpOnly: boolean;
+ secure: boolean;
+ session: boolean;
+ sameSite: SameSite;
+ storeId: null;
+ name: string;
+ value: string;
+};
+export type CookiesMap = Record<string, CookieMap>;
+export type CookieMap = Record<string, Cookie>;
+export type KeyMap = Record<string, string>;
+type SameSite = null | "no_restriction"; // TODO
+
+export type APIRes = { API: { app: string; api_key: string } };
+export type CookieRes = { Cookie: { app: string; cookie: CookieMap } };
+export type CookiesRes = { cookies: CookiesMap; apiKeys: KeyMap };
+
+// words
+export type Meaning = {
+ pos: string; // part of speech;
+ meaning: string[];
+ etymology: string;
+ references?: any;
+};
+
+export type Prompts = {
+ translate: string;
+};
+export type AnalyzeRes = {
+ word: string;
+ syllables: string[];
+ ipa: string;
+ pos: POS;
+};
+type PosTuple = [string, POS];
+type POS = string;
+
+export type WordData = {
+ spelling: string;
+ lang: string;
+ ipa: string;
+ meanings: Meaning[];
+ references?: any;
+};
diff --git a/packages/prosody-ui/src/logic/utils.ts b/packages/prosody-ui/src/logic/utils.ts
new file mode 100644
index 0000000..737a6ec
--- /dev/null
+++ b/packages/prosody-ui/src/logic/utils.ts
@@ -0,0 +1,66 @@
+import type { Result } from "sortug";
+
+export function detectScript(text: string): Result<string> {
+ const scripts = {
+ Latin: /[\u0000-\u007F\u00A0-\u00FF\u0100-\u017F\u0180-\u024F]/g,
+ Cyrillic: /[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F]/g,
+ Greek: /[\u0370-\u03FF\u1F00-\u1FFF]/g,
+ Hebrew: /[\u0590-\u05FF]/g,
+ Arabic: /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]/g,
+ Devanagari: /[\u0900-\u097F]/g, // Hindi, Sanskrit, etc.
+ Bengali: /[\u0980-\u09FF]/g,
+ Thai: /[\u0E00-\u0E7F]/g,
+ Chinese:
+ /[\u4E00-\u9FFF\u3400-\u4DBF\u20000-\u2A6DF\u2A700-\u2B73F\u2B740-\u2B81F]/g,
+ Japanese: /[\u3040-\u309F\u30A0-\u30FF\uFF00-\uFFEF\u4E00-\u9FAF]/g, // Includes Hiragana, Katakana
+ Korean: /[\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F]/g, // Includes Hangul
+ Armenian: /[\u0530-\u058F]/g,
+ Georgian: /[\u10A0-\u10FF]/g,
+ Khmer: /[\u1780-\u17FF]/g, // Cambodian
+ Myanmar: /[\u1000-\u109F]/g, // Burmese
+ Tamil: /[\u0B80-\u0BFF]/g,
+ Telugu: /[\u0C00-\u0C7F]/g,
+ Amharic: /[\u1200-\u137F]/g, // Ethiopian
+ };
+ const counts: Record<string, number> = {};
+
+ for (const [scriptName, regex] of Object.entries(scripts)) {
+ // Create an array of matches and count its length
+ const matches = text.match(regex) || [];
+ counts[scriptName] = matches.length;
+ }
+
+ let maxCount = 0;
+ let dominantScript = "Unknown";
+
+ for (const [scriptName, count] of Object.entries(counts)) {
+ if (count > maxCount) {
+ maxCount = count;
+ dominantScript = scriptName;
+ }
+ }
+ if (dominantScript === "Unknown") return { error: "Not detected" };
+ else return { ok: dominantScript };
+}
+
+export function langFromScript(script: string): Result<string> {
+ if (script === "Thai") return { ok: "th" };
+ if (script === "Japanese") return { ok: "ja" };
+ if (script === "Chinese") return { ok: "zh" };
+ if (script === "Korean") return { ok: "ko" };
+ else return { error: "too generic" };
+}
+export function scriptFromLang(lang: string, text: string): string {
+ if (lang == "th") return "Thai";
+ if (lang == "tha") return "Thai";
+ if (lang == "en") return "Engl";
+ if (lang == "es") return "Span";
+ if (lang == "cn") return "Hant";
+ if (lang == "zh") return "Hant";
+ if (lang == "ja") return "Japn";
+ else {
+ const res = detectScript(text);
+ if ("ok" in res) return res.ok;
+ else return "";
+ }
+}
diff --git a/packages/prosody-ui/src/logic/wiki.ts b/packages/prosody-ui/src/logic/wiki.ts
new file mode 100644
index 0000000..1325c0f
--- /dev/null
+++ b/packages/prosody-ui/src/logic/wiki.ts
@@ -0,0 +1,138 @@
+import type { AsyncRes, Result } from "sortug";
+import type { Meaning } from "./types";
+
+export function buildWiktionaryURL(word: string) {
+ const params = new URLSearchParams();
+ params.append("action", "parse");
+ params.append("page", word);
+ params.append("format", "json");
+ params.append("prop", "templates|text");
+ params.append("formatversion", "2");
+
+ const p = params.toString();
+ const url = `https://en.wiktionary.org/w/api.php?${p}`;
+ return url;
+}
+
+// export async function fetchWordInWiki(url: string) {
+// const opts = { method: "GET", body: null, headers: {} };
+// try {
+// const res = await proxyCall(url, opts);
+// console.log(res.headers.get("content-type"));
+// const j = await res.json();
+// return { ok: j };
+// } catch (e) {
+// return { error: `${e}` };
+// }
+// }
+
+export type WikiRes = {
+ url: string;
+ meanings: Meaning[];
+ ipa: string[];
+};
+const poses = [
+ "noun",
+ "verb",
+ "adjective",
+ "adverb",
+ "conjunction",
+ "determiner",
+ "preposition",
+ "definitions",
+];
+
+export function parseWiktionary(html: string, url: string): Result<WikiRes> {
+ try {
+ const dp = new DOMParser();
+ const doc = dp.parseFromString(html, "text/html");
+ const ipas = doc.querySelectorAll(".IPA");
+ const headings = doc.querySelectorAll(".mw-heading");
+ const ms: Meaning[] = [];
+ const doneIdx: number[] = [];
+ let currentRound: Meaning = { pos: "", meaning: [], etymology: "" };
+ for (let [idx, h] of Array.from(headings).entries()) {
+ const headingType: string = (h.firstChild as any).innerText;
+ if (!headingType) continue;
+ const ht = headingType.toLowerCase();
+ if (ht.includes("etymology")) currentRound.etymology = fillEtym(h);
+ else if (poses.includes(ht)) {
+ currentRound.pos = ht;
+ currentRound = fillMeaning(h, currentRound);
+ }
+ if (currentRound.pos) {
+ ms.push({ ...currentRound });
+ currentRound = { pos: "", meaning: [], etymology: "" };
+ }
+ if (ht === "references") break; // make sure it's one single language lol
+ }
+ const ipaStrings = Array.from(ipas).map((el: any) => el.innerText);
+ return { ok: { meanings: ms, ipa: ipaStrings, url } };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+
+function fillMeaning(el: Element, m: Meaning) {
+ const sibling = el.nextElementSibling;
+ if (!sibling) return m;
+ if (sibling?.tagName.toLowerCase() === "ol") {
+ for (let li of Array.from(sibling.children)) {
+ if (li.tagName.toLowerCase() !== "li") continue;
+ if (li.className.includes("empty-elt")) continue;
+ m.meaning.push(li.innerHTML);
+ }
+ }
+ if (m.meaning.length === 0) return fillMeaning(sibling, m);
+ else return m;
+}
+
+function fillEtym(el: Element, acc: string = ""): string {
+ const sibling = el.nextElementSibling;
+ if (!sibling) return acc;
+ if (sibling?.tagName.toLowerCase() === "p") acc += `\n${sibling.innerHTML}`;
+ if (!acc) return fillEtym(sibling, acc);
+ else return acc;
+}
+
+export function parseWiktionaryo(html: string, url: string): Result<WikiRes> {
+ try {
+ const dp = new DOMParser();
+ const doc = dp.parseFromString(html, "text/html");
+ const ipas = doc.querySelectorAll(".IPA");
+ const ols = doc.querySelectorAll("ol");
+ const ms = Array.from(ols).map((el) => {
+ let pos = "";
+ let etymology = "";
+ let meaning: string[] = [];
+ let posr = findPos(el);
+ if ("ok" in posr) pos = posr.ok;
+ for (let li of Array.from(el.children)) {
+ if (li.tagName !== "LI") continue;
+ meaning.push((li as any).innerText);
+ }
+ return { pos, meaning, etymology };
+ });
+ console.log(ipas, "ipa strings");
+ console.log(ols, "lists in wiki");
+ const ipaStrings = Array.from(ipas).map((el: any) => el.innerText);
+ return { ok: { meanings: ms, ipa: ipaStrings, url } };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+
+function findPos(el: Element): Result<string> {
+ let pichai = el.previousElementSibling;
+ console.log(pichai, "previous");
+ if (!pichai) return { error: "no pichai" };
+ if (pichai.classList.contains("mw-heading")) {
+ const h4 = pichai.querySelector("h4");
+ const h3 = pichai.querySelector("h3");
+ if (!h4 && !h3) return findPos(pichai);
+ else {
+ const id = (h4?.innerText || h3?.innerText)!;
+ return { ok: id };
+ }
+ } else return findPos(pichai);
+}
diff --git a/packages/prosody-ui/src/sortug.css b/packages/prosody-ui/src/sortug.css
new file mode 100644
index 0000000..c6280c0
--- /dev/null
+++ b/packages/prosody-ui/src/sortug.css
@@ -0,0 +1,248 @@
+
+/* SORTUG CSS */
+/* variables */
+:root {
+ --bai: rgba(255, 255, 255, 1);
+ --baizi: rgba(230, 230, 230);
+ --hui: rgba(130, 130, 130, 1);
+ --hei: rgba(0, 0, 0, 1);
+ --hong: rgb(141, 15, 15, 1);
+ --huang: rgb(230, 180, 60, 1);
+ --lan: rgb(30, 60, 80, 1);
+}
+
+[data-theme="dark"] {
+ --bg: hei;
+ --fg: baizi;
+}
+
+[data-theme="light"] {
+ --bg: white;
+ --fg: black;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body,
+#root {
+ height: 100%;
+ min-height: 100%;
+ overscroll-behavior: none;
+ color: var(--fg);
+ -webkit-font-smoothing: antialiased;
+ margin: 0;
+}
+
+/* tailwindy classes */
+.card {
+ padding: 1rem;
+ max-width: max-content;
+}
+
+button,
+.button {
+ max-width: max-content;
+ padding: 0.5rem;
+ border: 1px solid var(--fg);
+}
+
+/* borders */
+.nb {
+ border: none;
+}
+
+/* widths */
+.hw {
+ width: 50%;
+}
+
+.qw {
+ width: 25%;
+}
+
+.tqw {
+ width: 75%;
+}
+
+/* flex */
+.row {
+ display: flex;
+ align-items: center;
+}
+
+.sy {
+ overflow-y: scroll;
+}
+
+.fsy {
+ overflow-y: scroll;
+ height: 100%;
+}
+
+.fxc {
+ display: flex;
+ justify-content: center;
+ align-items: baseline;
+}
+
+/* flex spread */
+.fs {
+ display: flex;
+ justify-content: space-between;
+}
+
+.fsc {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.g1 {
+ gap: 0.5rem;
+}
+
+.g2 {
+ gap: 1rem;
+}
+
+.address {
+ font-family: "Courier New", Courier, monospace;
+}
+
+.spread {
+ justify-content: space-between;
+}
+
+.even {
+ justify-content: space-evenly;
+}
+
+.flexc {
+ justify-content: center;
+}
+
+.cp {
+ cursor: pointer;
+}
+
+/* centering */
+.gc {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.agc {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.ac {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.xc {
+ position: fixed;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 20;
+}
+
+.tc {
+ text-align: center;
+}
+
+.bc {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.blocks {
+ & * {
+ display: block;
+ }
+}
+
+.bold {
+ font-weight: 700;
+}
+
+.weak {
+ opacity: 0.7;
+}
+
+.all-c {
+ & * {
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.mb-1 {
+ margin-bottom: 1rem;
+}
+
+.error {
+ color: red;
+ text-align: center;
+}
+
+.tabs {
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+
+ & .tab {
+ cursor: pointer;
+ opacity: 0.5;
+ }
+
+ & .tab.active {
+ opacity: 1;
+ }
+}
+
+.disabled {
+ opacity: 0.5;
+}
+
+.smol {
+ font-size: 0.9rem;
+}
+
+/* The Modal (background) */
+#modal-bg {
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 998;
+}
+
+/* Modal Content */
+#modal-fg {
+ background-color: var(--bg);
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ padding: 20px;
+ z-index: 999;
+ max-height: 90vh;
+ min-height: 20vh;
+ max-width: 90vw;
+ overflow: auto;
+}
diff --git a/packages/prosody-ui/src/styles/styles.css b/packages/prosody-ui/src/styles/styles.css
new file mode 100644
index 0000000..69351f1
--- /dev/null
+++ b/packages/prosody-ui/src/styles/styles.css
@@ -0,0 +1,281 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+#root>.spinner {
+ width: 100px;
+ height: 100px;
+}
+
+#entry>.spinner {
+ width: 80px;
+ height: 80px;
+}
+
+
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+#cookies {
+ & .active {
+ background-color: var(--huang);
+ }
+
+ & input {
+ margin-left: 1rem;
+ width: 100%;
+ }
+
+ & textarea {
+ width: 100%;
+ height: 500px;
+ resize: none;
+ outline: none;
+ }
+}
+
+#entry {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ padding: 1rem;
+ /* prov */
+ border: 2px solid black;
+
+ & div[lang="th"] {
+
+ & .tw-text,
+ & .tw-hashtag {
+ font-size: 3rem;
+ }
+ }
+
+ & .text-wrapper {
+ display: block;
+ margin: 0.5rem 0;
+ /* overflow: hidden; */
+ }
+
+ & .word {
+ display: inline-block;
+ transition: transform 0.3s ease;
+ }
+
+ & .word:hover {
+ transform: scale(1.4);
+ background-color: white;
+ }
+
+ & #tw-media {
+ max-width: 100%;
+
+ & img,
+ & video {
+ max-width: 100%;
+ }
+ }
+}
+
+#inner {
+ height: 100%;
+ max-height: 100%;
+ overflow-y: auto;
+}
+
+#entry-icons {
+ position: absolute;
+ bottom: 5%;
+ right: 5%;
+ width: 50px;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ & .avatar {
+ border: 2px solid black;
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ overflow: hidden;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
+
+ & img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+}
+
+#word-modal {
+ position: relative;
+
+ & .font-icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 32px;
+ height: 32px;
+ }
+
+ & .save-icon {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 32px;
+ height: 32px;
+ }
+
+ & .original {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+ }
+
+ & .syllable {}
+
+ & .IPA {
+ font-size: 1.6rem;
+ line-height: 1.6rem;
+
+ & img {
+ width: 50px;
+ margin-left: 1rem;
+ }
+ }
+
+ & .meanings {
+
+ & .spinner {
+ width: 80px;
+ height: 80px;
+ }
+
+ & .meaning {
+ margin: 1rem auto;
+ }
+
+ & .pos {
+ font-size: 1.2rem;
+ margin-bottom: 0.3rem;
+ text-align: left;
+ }
+
+ & ol {
+ word-wrap: normal;
+ margin: auto;
+ text-align: left;
+ }
+ }
+}
+
+img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.flex1 {
+ width: 100%;
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+
+.flex-center {
+ justify-content: center;
+}
+
+/* p { */
+/* position: absolute; */
+/* top: 50%; */
+/* left: 50%; */
+/* transform: translate(-50%, -50%); */
+/* color: white; */
+/* background-color: rgba(0, 0, 0, 0.5); */
+/* padding: 10px; */
+/* border-radius: 5px; */
+/* } */
+#modal-bg {
+ height: 100vh;
+ width: 100vw;
+ background-color: rgb(0, 0, 0, 0.9);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 100;
+}
+
+#modal-fg {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 80%;
+ z-index: 101;
+ transform: translate(-50%, -50%);
+ /* background-color: var(--background-color); */
+ background-color: lightgrey;
+ font-size: 1.2rem;
+ padding: 1rem;
+ max-height: 80%;
+ overflow-y: scroll;
+}
+
+
+.text-ipa {
+ font-size: 1.5rem;
+}
+
+.text-thai {
+ font-size: 1.5rem;
+}
+
+
+/* // new */
+.word.cp {
+ margin: 0 0.5ch;
+}
+
+.lang-text-container {
+ display: flex;
+ flex-wrap: wrap;
+} \ No newline at end of file
diff --git a/packages/prosody-ui/src/thai/ThaiText.tsx b/packages/prosody-ui/src/thai/ThaiText.tsx
new file mode 100644
index 0000000..fc1e1e6
--- /dev/null
+++ b/packages/prosody-ui/src/thai/ThaiText.tsx
@@ -0,0 +1,49 @@
+import React, { useCallback, useEffect, useState } from "react";
+import "../assets/fonts/Thai/style.css";
+import { segmentateThai } from "./logic/thainlp";
+import type { AnalyzeRes } from "../logic/types";
+import { ColoredText } from "../components/Sentence";
+import Word from "../components/Word";
+
+export default function ThaiText({
+ text,
+ openWord,
+}: {
+ text: string;
+ openWord: (s: string) => void;
+}) {
+ useEffect(() => {
+ pythonseg();
+ }, [text]);
+
+ const [data, setData] = useState<Record<string, AnalyzeRes>>({});
+ const [modal, setModal] = useState<any>();
+ const pythonseg = useCallback(async () => {
+ const s2 = await segmentateThai(text.trim());
+ if ("ok" in s2) {
+ const ob = s2.ok.reduce((acc, item) => {
+ acc[item.word] = item;
+ return acc;
+ }, {} as any);
+ setData(ob);
+ console.log(s2, "s2");
+ } else console.error(s2.error);
+ }, [text]);
+
+ // function openWord(e: React.MouseEvent<any>) {
+ // const s = e.currentTarget.innerText;
+ // const d = data[s];
+ // setModal(d);
+ // // setModal(<WordModal data={d} lang={lang} />);
+ // }
+ return (
+ <div className="thaitext">
+ <ColoredText lang="tha" frags={Object.keys(data)} fn={openWord} />
+ {modal && <Word data={modal} lang={"tha"} />}
+ </div>
+ );
+}
+
+function ThaiWord() {
+ return <div />;
+}
diff --git a/packages/prosody-ui/src/thai/logic/thainlp.ts b/packages/prosody-ui/src/thai/logic/thainlp.ts
new file mode 100644
index 0000000..031bf4c
--- /dev/null
+++ b/packages/prosody-ui/src/thai/logic/thainlp.ts
@@ -0,0 +1,90 @@
+import type { AsyncRes } from "sortug";
+import type { AnalyzeRes } from "../../logic/types";
+
+const ENDPOINT = "http://192.168.1.110:8001";
+async function call(path: string, body: any) {
+ try {
+ const opts = {
+ method: "POST",
+ headers: { "Content-type": "application/json" },
+ body: JSON.stringify(body),
+ };
+ const r1 = await fetch(ENDPOINT + path, opts);
+ // const r2 = await fetch(`http://192.168.1.110:8000/analyze`, opts);
+ const jj = await r1.json();
+ return { ok: jj };
+ } catch (e) {
+ return { error: `${e}` };
+ }
+}
+export async function analyzeTHWord(word: string): AsyncRes<AnalyzeRes> {
+ return await call("/analyze", { word });
+}
+export async function segmentateThai(sentence: string): AsyncRes<AnalyzeRes[]> {
+ return await call("/segmentate", { word: sentence });
+}
+
+export const POSMAP: Record<string, string> = {
+ ADJ: "Adjective",
+ ADP: "Adposition",
+ ADV: "Adverb",
+ AUX: "Auxiliary",
+ CCONJ: "Coordinating conjunction",
+ DET: "Determiner",
+ INTJ: "Interjection",
+ NOUN: "Noun",
+ NUM: "Numeral",
+ PART: "Particle",
+ PRON: "Pronoun",
+ PROPN: "Proper noun",
+ PUNCT: "Punctuation",
+ SCONJ: "Subordinating conjunction",
+ VERB: "Verb",
+ NPRP: "Proper noun",
+ NCNM: "Cardinal number",
+ NONM: "Ordinal number",
+ NLBL: "Label noun",
+ NCMN: "Common noun",
+ NTTL: "Title noun",
+ PPRS: "Personal pronoun",
+ PDMN: "Demonstrative pronoun",
+ PNTR: "Interrogative pronoun",
+ PREL: "Relative pronoun",
+ VACT: "Active verb",
+ VSTA: "Stative verb",
+ VATT: "Attributive verb",
+ XVBM: "Pre-verb auxiliary, before negator “ไม่”",
+ XVAM: "Pre-verb auxiliary, after negator “ไม่”",
+ XVMM: "Pre-verb, before or after negator “ไม่”",
+ XVBB: "Pre-verb auxiliary, in imperative mood",
+ XVAE: "Post-verb auxiliary",
+ DDAN: "classifier in between",
+ DDAC: "in between",
+ DDBQ: "classifier or preceding quantitative expression",
+ DDAQ: "following quantitative expression",
+ DIAC: "classifier in between",
+ DIBQ: "classifier or preceding quantitative expression",
+ DIAQ: "following quantitative expression",
+ DCNM: "Determiner, cardinal number expression",
+ DONM: "Determiner, ordinal number expression",
+ ADVN: "Adverb with normal form",
+ ADVI: "Adverb with iterative form",
+ ADVP: "Adverb with prefixed form",
+ ADVS: "Sentential adverb",
+ CNIT: "Unit classifier",
+ CLTV: "Collective classifier",
+ CMTR: "Measurement classifier",
+ CFQC: "Frequency classifier",
+ CVBL: "Verbal classifier",
+ JCRG: "Coordinating conjunction",
+ JCMP: "Comparative conjunction",
+ JSBR: "Subordinating conjunction",
+ RPRE: "Preposition",
+ INT: "Interjection",
+ FIXN: "Nominal prefix",
+ FIXV: "Adverbial prefix",
+ EAFF: "Ending for affirmative sentence",
+ EITT: "Ending for interrogative sentence",
+ NEG: "Negator",
+ PUNC: "Punctuation",
+};
diff --git a/packages/prosody-ui/src/zoom/FullText.tsx b/packages/prosody-ui/src/zoom/FullText.tsx
new file mode 100644
index 0000000..ec85f09
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/FullText.tsx
@@ -0,0 +1,60 @@
+import React from "react";
+import { motion, AnimatePresence } from "motion/react";
+import Paragraph from "./Paragraph";
+import { useZoom } from "./hooks/useZoom";
+import { containerVariants, buttonVariants } from "./animations";
+import { NLP } from "sortug-ai";
+
+interface TextFocusMorphProps {
+ text: string;
+ doc: NLP.Spacy.SpacyRes;
+}
+
+const FullText: React.FC<TextFocusMorphProps> = ({ text, doc }) => {
+ const { viewState, navigateBack, handleElementClick } = useZoom();
+ const { level } = viewState;
+
+ // Split text into paragraphs
+ const paragraphs = text
+ .split("\n\n")
+ .map((p) => p.trim())
+ .filter(Boolean);
+
+ return (
+ <div className="text-focus-morph">
+ {level !== "text" && (
+ <AnimatePresence>
+ <motion.button
+ className="back-button"
+ onClick={navigateBack}
+ variants={buttonVariants}
+ initial="initial"
+ animate="animate"
+ exit="exit"
+ >
+ ← Back
+ </motion.button>
+ </AnimatePresence>
+ )}
+
+ <motion.div
+ className="content-container"
+ variants={containerVariants}
+ initial="text"
+ animate={level}
+ >
+ {paragraphs.map((paragraph, idx) => (
+ <Paragraph
+ doc={doc}
+ key={paragraph + idx}
+ rawText={paragraph}
+ context={{ idx, parentText: text, segmented: paragraphs }}
+ idx={idx}
+ />
+ ))}
+ </motion.div>
+ </div>
+ );
+};
+
+export default FullText;
diff --git a/packages/prosody-ui/src/zoom/Paragraph.tsx b/packages/prosody-ui/src/zoom/Paragraph.tsx
new file mode 100644
index 0000000..c26f806
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/Paragraph.tsx
@@ -0,0 +1,60 @@
+import React, { memo, useCallback, useEffect, useState } from "react";
+import { motion } from "motion/react";
+import type { ViewProps, LoadingStatus } from "./logic/types";
+import { NLP } from "sortug-ai";
+import Sentence from "./Sentence";
+import { paragraphVariants, createHoverEffect } from "./animations";
+import { useZoom } from "./hooks/useZoom";
+
+function Paragraph({ rawText, context, idx, doc }: ViewProps) {
+ const { viewState, handleElementClick } = useZoom();
+ const { level, pIndex } = viewState;
+ const selected = pIndex === idx;
+ const isFocused = level === "paragraph" && selected;
+
+ // State for sentences
+ const [loading, setLoading] = useState<LoadingStatus>("pending");
+
+ return (
+ <>
+ <motion.div
+ key={idx + rawText}
+ className={`paragraph-wrapper ${selected ? "selected" : ""}`}
+ custom={selected}
+ variants={paragraphVariants}
+ initial="text"
+ animate={level}
+ onClick={(e) => handleElementClick(e, idx)}
+ whileHover={
+ level === "text"
+ ? createHoverEffect(level, "text", "255, 255, 200")
+ : {}
+ }
+ >
+ {loading === "loading" && <div className="spinner" />}
+ {level === "text" || !selected || doc.segs.length === 0 ? (
+ <p className="paragraph">{rawText}</p>
+ ) : (
+ <div className="sentences-container">
+ {doc.segs.map((sentence, sentIdx) => (
+ <Sentence
+ key={sentence.text + sentIdx}
+ idx={sentIdx}
+ rawText={sentence.text}
+ spacy={sentence}
+ context={{
+ idx: sentIdx,
+ parentText: rawText,
+ segmented: doc.segs.map((s) => s.text),
+ }}
+ doc={doc}
+ />
+ ))}
+ </div>
+ )}
+ </motion.div>
+ </>
+ );
+}
+
+export default memo(Paragraph);
diff --git a/packages/prosody-ui/src/zoom/Sentence.tsx b/packages/prosody-ui/src/zoom/Sentence.tsx
new file mode 100644
index 0000000..1d90346
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/Sentence.tsx
@@ -0,0 +1,46 @@
+import React, { memo } from "react";
+import { motion } from "motion/react";
+import type { ViewProps, LoadingStatus } from "./logic/types";
+import { NLP } from "sortug-ai";
+import SpacyClause from "./SpacyClause";
+import { sentenceVariants, createHoverEffect } from "./animations";
+import { useZoom } from "./hooks/useZoom";
+
+interface Props extends ViewProps {
+ spacy: NLP.Spacy.Sentence;
+ stanza?: NLP.Stanza.Sentence;
+}
+
+function Sentence({ spacy, stanza, context, idx }: Props) {
+ const { viewState, handleElementClick } = useZoom();
+ const { level, sIndex } = viewState;
+ const selected = sIndex === idx;
+ const isFocused = level === "sentence" && selected;
+
+ return (
+ <>
+ <motion.span
+ key={idx + spacy.text}
+ className={`sentence-wrapper ${selected ? "selected" : ""}`}
+ custom={selected}
+ variants={sentenceVariants}
+ initial="paragraph"
+ animate={level}
+ onClick={(e) => handleElementClick(e, idx)}
+ whileHover={
+ level === "paragraph"
+ ? createHoverEffect(level, "paragraph", "200, 220, 255")
+ : {}
+ }
+ >
+ {level === "paragraph" || !selected ? (
+ <span className="sentence">{spacy.text}</span>
+ ) : (
+ <SpacyClause sentence={spacy} />
+ )}
+ </motion.span>
+ </>
+ );
+}
+
+export default memo(Sentence);
diff --git a/packages/prosody-ui/src/zoom/SpacyClause.tsx b/packages/prosody-ui/src/zoom/SpacyClause.tsx
new file mode 100644
index 0000000..6b6f178
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/SpacyClause.tsx
@@ -0,0 +1,125 @@
+import React, { memo, useState } from "react";
+import { motion } from "motion/react";
+import "./spacy.css";
+import { NLP } from "sortug-ai";
+// import { clauseVariants, createHoverEffect } from "./animations";
+// import { useZoom } from "./hooks/useZoom";
+
+function Grammar({ sentence }: { sentence: NLP.Spacy.Sentence }) {
+ const [hoveredClause, setHoveredClause] = useState<number | null>(null);
+
+ // Ref to manage the timeout for debouncing mouse leave
+ return (
+ <div className="clause-container">
+ {sentence.words.map((w, idx) => {
+ const isRoot =
+ w.ancestors.length === 0 || w.dep.toLowerCase() === "root";
+ const isSubj = NLP.Spacy.isChild(w, sentence.subj.id);
+ const isPred = !isSubj && !isRoot;
+ const predClass = isPred ? "pred" : "";
+ const relClass = isRoot ? "root" : `rel-${w.dep}`;
+ const ownClass = isRoot
+ ? ""
+ : isSubj
+ ? "subj"
+ : w.children.length === 0
+ ? ""
+ : `clause-${w.id}`;
+ const clase = w.ancestors.reduce((acc, item) => {
+ if (item === sentence.subj.id || item === sentence.root.id)
+ return acc;
+ else return `${acc} clause-${item}`;
+ }, ``);
+ const className = `suword ${relClass} ${ownClass} ${clase} ${predClass}`;
+ const isHovering =
+ !isRoot &&
+ !!hoveredClause &&
+ (w.id === hoveredClause || w.ancestors.includes(hoveredClause));
+ function handleClick(w: NLP.Spacy.Word) {
+ console.log("show the whole clause and all that", w);
+ }
+ return (
+ <ClauseSpan
+ word={w}
+ key={w.id}
+ className={className}
+ hovering={isHovering}
+ setHovering={setHoveredClause}
+ onClick={handleClick}
+ />
+ );
+ })}
+ </div>
+ );
+}
+
+const spanVariants: any = {
+ initial: {
+ // Base style
+ backgroundColor: "rgba(0, 0, 0, 0)", // Transparent background initially
+ fontWeight: "normal",
+ scale: 1,
+ zIndex: 0, // Default stacking
+ position: "relative", // Needed for zIndex to work reliably
+ // Add other base styles if needed
+ },
+ hovered: {
+ // Style when this span's group is hovered
+ backgroundColor: "rgba(255, 255, 0, 0.5)", // Yellow highlight
+ scale: 1.05,
+ zIndex: 1, // Bring hovered spans slightly forward
+ boxShadow: "0px 2px 5px rgba(0,0,0,0.2)",
+ // Add other hover effects
+ },
+};
+
+// Define the transition
+const spanTransition = {
+ type: "spring",
+ stiffness: 500,
+ damping: 30,
+ // duration: 0.1 // Or use duration for non-spring types
+};
+
+function ClauseSpan({
+ word,
+ className,
+ hovering,
+ setHovering,
+ onClick,
+}: {
+ word: NLP.Spacy.Word;
+ className: string;
+ hovering: boolean;
+ setHovering: (n: number | null) => void;
+ onClick: (w: NLP.Spacy.Word) => void;
+}) {
+ function handleMouseOver() {
+ setHovering(word.id);
+ // if (word.children.length > 0) setHovering(word.id);
+ // else setHovering(word.head);
+ }
+ function handleMouseLeave() {
+ setHovering(null);
+ }
+ function handleClick(e: React.MouseEvent) {
+ e.stopPropagation();
+ onClick(word);
+ }
+ return (
+ <motion.span
+ className={className}
+ variants={spanVariants}
+ initial="initial"
+ animate={hovering ? "hovered" : "initial"}
+ transition={spanTransition}
+ onMouseOver={handleMouseOver}
+ onMouseLeave={handleMouseLeave}
+ onClick={handleClick}
+ >
+ {word.text}
+ </motion.span>
+ );
+}
+
+export default memo(Grammar);
diff --git a/packages/prosody-ui/src/zoom/animations.ts b/packages/prosody-ui/src/zoom/animations.ts
new file mode 100644
index 0000000..6135e7f
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/animations.ts
@@ -0,0 +1,199 @@
+import type { Variants } from "motion/react";
+
+// Base transition configurations for consistent animations
+const baseTransition = {
+ duration: 0.5,
+ ease: [0.43, 0.13, 0.23, 0.96], // Improved easing for smoother feel
+};
+
+export const fadeTransition = {
+ ...baseTransition,
+ duration: 0.3,
+};
+
+// Shared variants for different view levels
+export const containerVariants: Variants = {
+ text: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ paragraph: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ sentence: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ clause: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ word: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ syllable: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+ phoneme: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.05,
+ delayChildren: 0.1,
+ },
+ },
+};
+
+// Function to create element variants based on selection state
+export const createElementVariants = (
+ currentLevel: string,
+ nextLevel: string,
+ prevLevel: string,
+ selectedOpacity = 1,
+ unselectedOpacity = 0.1,
+ selectedScale = 1.05,
+ unselectedScale = 0.95,
+ selectedBlur = "0px",
+ unselectedBlur = "2px",
+ bgColor = "rgba(255, 255, 255, 0)", // Use rgba with 0 opacity instead of transparent
+): Variants => {
+ return {
+ [prevLevel]: {
+ opacity: 1,
+ scale: 1,
+ filter: "blur(0px)",
+ backgroundColor: "rgba(255, 255, 255, 0)", // Use rgba with 0 opacity
+ transition: baseTransition,
+ },
+ [currentLevel]: (isSelected: boolean) => ({
+ opacity: isSelected ? selectedOpacity : unselectedOpacity,
+ scale: isSelected ? selectedScale : unselectedScale,
+ filter: isSelected ? `blur(${selectedBlur})` : `blur(${unselectedBlur})`,
+ backgroundColor: isSelected ? bgColor : "rgba(255, 255, 255, 0)", // Use rgba with 0 opacity
+ transition: baseTransition,
+ }),
+ [nextLevel]: (isSelected: boolean) => ({
+ opacity: isSelected ? selectedOpacity : unselectedOpacity / 2,
+ scale: isSelected ? selectedScale : unselectedScale * 0.95,
+ filter: isSelected
+ ? `blur(${selectedBlur})`
+ : `blur(${parseInt(unselectedBlur) + 1}px)`,
+ backgroundColor: isSelected ? bgColor : "rgba(255, 255, 255, 0)", // Use rgba with 0 opacity
+ transition: baseTransition,
+ }),
+ };
+};
+
+// Pre-configured variants for each level
+export const paragraphVariants = createElementVariants(
+ "paragraph",
+ "sentence",
+ "text",
+ 1,
+ 0.1,
+ 1.05,
+ 0.95,
+ "0px",
+ "2px",
+ "rgba(200, 220, 255, 0.1)",
+);
+
+export const sentenceVariants = createElementVariants(
+ "sentence",
+ "clause",
+ "paragraph",
+ 1,
+ 0.1,
+ 1.1,
+ 0.95,
+ "0px",
+ "2px",
+ "rgba(200, 220, 255, 0.2)",
+);
+
+export const clauseVariants = createElementVariants(
+ "clause",
+ "word",
+ "sentence",
+ 1,
+ 0.1,
+ 1.1,
+ 0.95,
+ "0px",
+ "2px",
+ "rgba(220, 200, 255, 0.2)",
+);
+
+export const wordVariants = createElementVariants(
+ "word",
+ "syllable",
+ "clause",
+ 1,
+ 0.1,
+ 1.15,
+ 0.9,
+ "0px",
+ "2px",
+ "rgba(255, 200, 200, 0.2)",
+);
+
+export const syllableVariants = createElementVariants(
+ "syllable",
+ "phoneme",
+ "word",
+ 1,
+ 0.1,
+ 1.2,
+ 0.9,
+ "0px",
+ "2px",
+ "rgba(200, 255, 200, 0.2)",
+);
+
+// Button animations
+export const buttonVariants: Variants = {
+ initial: { opacity: 0, x: -20 },
+ animate: { opacity: 1, x: 0, transition: fadeTransition },
+ exit: { opacity: 0, x: -20, transition: fadeTransition },
+};
+
+// Hover effects
+export const createHoverEffect = (
+ level: string,
+ currentLevel: string,
+ color: string,
+) => {
+ if (level === currentLevel) {
+ return {
+ scale: 1.02,
+ backgroundColor: `rgba(${color}, 0.3)`,
+ transition: { duration: 0.2 },
+ };
+ }
+ return {
+ // Return empty animation with same properties to avoid errors
+ scale: 1,
+ backgroundColor: "rgba(255, 255, 255, 0)",
+ transition: { duration: 0.2 },
+ };
+};
diff --git a/packages/prosody-ui/src/zoom/hooks/useZoom.tsx b/packages/prosody-ui/src/zoom/hooks/useZoom.tsx
new file mode 100644
index 0000000..733ca06
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/hooks/useZoom.tsx
@@ -0,0 +1,135 @@
+import React, {
+ createContext,
+ useState,
+ useContext,
+ type ReactNode,
+} from "react";
+import type { ViewLevel, ViewState } from "../logic/types";
+
+// Type definitions for the context
+interface ZoomContextType {
+ viewState: ViewState;
+ setLevel: (level: ViewLevel) => void;
+ setParagraphIndex: (idx: number | null) => void;
+ setSentenceIndex: (idx: number | null) => void;
+ setClauseIndex: (idx: number | null) => void;
+ setWordIndex: (idx: number | null) => void;
+ setSyllableIndex: (idx: number | null) => void;
+ setPhonemeIndex: (idx: number | null) => void;
+ navigateBack: () => void;
+ handleElementClick: (e: React.MouseEvent, idx: number) => void;
+}
+
+// Create the context with default empty values
+const ZoomContext = createContext<ZoomContextType>({
+ viewState: {
+ level: "text",
+ pIndex: null,
+ sIndex: null,
+ cIndex: null,
+ wIndex: null,
+ yIndex: null,
+ fIndex: null,
+ },
+ setLevel: () => {},
+ setParagraphIndex: () => {},
+ setSentenceIndex: () => {},
+ setClauseIndex: () => {},
+ setWordIndex: () => {},
+ setSyllableIndex: () => {},
+ setPhonemeIndex: () => {},
+ navigateBack: () => {},
+ handleElementClick: () => {},
+});
+
+// Provider component
+export const ZoomProvider: React.FC<{ children: ReactNode }> = ({
+ children,
+}) => {
+ const [viewState, setViewState] = useState<ViewState>({
+ level: "text",
+ pIndex: null,
+ sIndex: null,
+ cIndex: null,
+ wIndex: null,
+ yIndex: null,
+ fIndex: null,
+ });
+
+ // Helper functions to update individual parts of the state
+ const setLevel = (level: ViewLevel) =>
+ setViewState((prev) => ({ ...prev, level }));
+ const setParagraphIndex = (pIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, pIndex }));
+ const setSentenceIndex = (sIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, sIndex }));
+ const setClauseIndex = (cIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, cIndex }));
+ const setWordIndex = (wIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, wIndex }));
+ const setSyllableIndex = (yIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, yIndex }));
+ const setPhonemeIndex = (fIndex: number | null) =>
+ setViewState((prev) => ({ ...prev, fIndex }));
+
+ // Handle navigation levels
+ const navigateBack = () => {
+ const { level } = viewState;
+
+ if (level === "paragraph") {
+ setViewState((prev) => ({ ...prev, level: "text", pIndex: null }));
+ } else if (level === "sentence") {
+ setViewState((prev) => ({ ...prev, level: "paragraph", sIndex: null }));
+ } else if (level === "clause") {
+ setViewState((prev) => ({ ...prev, level: "sentence", cIndex: null }));
+ } else if (level === "word") {
+ setViewState((prev) => ({ ...prev, level: "clause", wIndex: null }));
+ } else if (level === "syllable") {
+ setViewState((prev) => ({ ...prev, level: "word", yIndex: null }));
+ } else if (level === "phoneme") {
+ setViewState((prev) => ({ ...prev, level: "syllable", fIndex: null }));
+ }
+ };
+
+ // Handle clicks on elements to navigate forward
+ const handleElementClick = (e: React.MouseEvent, idx: number) => {
+ e.stopPropagation();
+ const { level } = viewState;
+
+ if (level === "text") {
+ setViewState((prev) => ({ ...prev, level: "paragraph", pIndex: idx }));
+ } else if (level === "paragraph") {
+ setViewState((prev) => ({ ...prev, level: "sentence", sIndex: idx }));
+ } else if (level === "sentence") {
+ setViewState((prev) => ({ ...prev, level: "clause", cIndex: idx }));
+ } else if (level === "clause") {
+ setViewState((prev) => ({ ...prev, level: "word", wIndex: idx }));
+ } else if (level === "word") {
+ setViewState((prev) => ({ ...prev, level: "syllable", yIndex: idx }));
+ } else if (level === "syllable") {
+ setViewState((prev) => ({ ...prev, level: "phoneme", fIndex: idx }));
+ }
+ };
+
+ return (
+ <ZoomContext.Provider
+ value={{
+ viewState,
+ setLevel,
+ setParagraphIndex,
+ setSentenceIndex,
+ setClauseIndex,
+ setWordIndex,
+ setSyllableIndex,
+ setPhonemeIndex,
+ navigateBack,
+ handleElementClick,
+ }}
+ >
+ {children}
+ </ZoomContext.Provider>
+ );
+};
+
+// Custom hook to use the zoom context
+export const useZoom = () => useContext(ZoomContext);
diff --git a/packages/prosody-ui/src/zoom/index.ts b/packages/prosody-ui/src/zoom/index.ts
new file mode 100644
index 0000000..baf5db1
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/index.ts
@@ -0,0 +1,8 @@
+export { ZoomProvider, useZoom } from "./hooks/useZoom";
+import Paragraph from "./Paragraph";
+import FullText from "./FullText";
+import Sentence from "./Paragraph";
+import SpacyClause from "./SpacyClause";
+import type * as Types from "./logic/types";
+
+export { Paragraph, FullText, Sentence, SpacyClause, Types };
diff --git a/packages/prosody-ui/src/zoom/logic/types.ts b/packages/prosody-ui/src/zoom/logic/types.ts
new file mode 100644
index 0000000..bea68ff
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/logic/types.ts
@@ -0,0 +1,53 @@
+import type { NLP } from "sortug-ai";
+
+export type ViewLevel =
+ | "text"
+ | "paragraph"
+ | "sentence"
+ | "clause"
+ | "word"
+ | "syllable"
+ | "phoneme";
+export interface ViewState {
+ level: ViewLevel;
+ pIndex: number | null;
+ sIndex: number | null;
+ cIndex: number | null;
+ wIndex: number | null;
+ yIndex: number | null;
+ fIndex: number | null;
+}
+
+export interface ViewProps {
+ idx: number;
+ rawText: string;
+ context: Context;
+ doc: NLP.Spacy.SpacyRes;
+}
+export type Context = {
+ parentText: string;
+ segmented: string[];
+ idx: number;
+};
+
+export type WordData = {
+ confidence: number;
+ frequency: number | null;
+ id: number;
+ ipa: Array<{ ipa: string; tags: string[] }>;
+ spelling: string;
+ type: ExpressionType;
+ syllables: number;
+ lang: string;
+ prosody: any;
+ senses: Sense[];
+};
+export type ExpressionType = "word" | "expression" | "syllable";
+export type Sense = {
+ etymology: string;
+ pos: string;
+ forms: Array<{ form: string; tags: string[] }>;
+ related: any;
+ senses: Array<{ glosses: string[]; links: Array<[string, string]> }>;
+};
+export type LoadingStatus = "pending" | "loading" | "success" | "error";
diff --git a/packages/prosody-ui/src/zoom/spacy.css b/packages/prosody-ui/src/zoom/spacy.css
new file mode 100644
index 0000000..0077119
--- /dev/null
+++ b/packages/prosody-ui/src/zoom/spacy.css
@@ -0,0 +1,39 @@
+.suword {
+ margin-left: 0.5ch;
+ margin-right: 0.5ch;
+}
+
+/* .suword.pred { */
+/* color: gold; */
+/* } */
+
+/* Clause level */
+.clause-container {
+ max-width: 600px;
+ white-space: normal !important;
+ hyphens: auto;
+
+ padding: 2px;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+ will-change: transform, opacity, filter, background-color;
+
+ span {
+ white-space: normal !important;
+ }
+}
+
+.clause-container.selected {
+ background-color: rgba(220, 200, 255, 0.2);
+ z-index: 3;
+}
+
+.suword.subj {
+ color: blue;
+ /* border-bottom: 2px solid blue; */
+}
+
+.suword.root {
+ color: darkred;
+} \ No newline at end of file
diff --git a/packages/prosody-ui/tsconfig.json b/packages/prosody-ui/tsconfig.json
new file mode 100644
index 0000000..238655f
--- /dev/null
+++ b/packages/prosody-ui/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ // Enable latest features
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/packages/sortug/.gitignore b/packages/sortug/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/packages/sortug/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/sortug/README.md b/packages/sortug/README.md
new file mode 100644
index 0000000..fe083f8
--- /dev/null
+++ b/packages/sortug/README.md
@@ -0,0 +1,15 @@
+# sortug
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.12. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
diff --git a/packages/sortug/bun.lock b/packages/sortug/bun.lock
new file mode 100644
index 0000000..d50feb5
--- /dev/null
+++ b/packages/sortug/bun.lock
@@ -0,0 +1,25 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "sortug",
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="],
+
+ "@types/node": ["@types/node@24.0.1", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw=="],
+
+ "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="],
+
+ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
+
+ "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
+ }
+}
diff --git a/packages/sortug/index.ts b/packages/sortug/index.ts
new file mode 100644
index 0000000..039454f
--- /dev/null
+++ b/packages/sortug/index.ts
@@ -0,0 +1,4 @@
+export type * from "./src/types";
+export * from "./src/utils";
+import styles from "./src/styles.module.css";
+export { styles };
diff --git a/packages/sortug/package.json b/packages/sortug/package.json
new file mode 100644
index 0000000..4d03854
--- /dev/null
+++ b/packages/sortug/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@sortug/lib",
+ "module": "index.ts",
+ "type": "module",
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ }
+}
diff --git a/packages/sortug/src/styles.module.css b/packages/sortug/src/styles.module.css
new file mode 100644
index 0000000..0e7db6a
--- /dev/null
+++ b/packages/sortug/src/styles.module.css
@@ -0,0 +1,247 @@
+/* SORTUG CSS */
+/* variables */
+:root {
+ --bai: rgba(255, 255, 255, 1);
+ --baizi: rgba(230, 230, 230);
+ --hui: rgba(130, 130, 130, 1);
+ --hei: rgba(0, 0, 0, 1);
+ --hong: rgb(141, 15, 15, 1);
+ --huang: rgb(230, 180, 60, 1);
+ --lan: rgb(30, 60, 80, 1);
+}
+
+[data-theme="dark"] {
+ --bg: hei;
+ --fg: baizi;
+}
+
+[data-theme="light"] {
+ --bg: white;
+ --fg: black;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body,
+#root {
+ height: 100%;
+ min-height: 100%;
+ overscroll-behavior: none;
+ color: var(--fg);
+ -webkit-font-smoothing: antialiased;
+ margin: 0;
+}
+
+/* tailwindy classes */
+.card {
+ padding: 1rem;
+ max-width: max-content;
+}
+
+button,
+.button {
+ max-width: max-content;
+ padding: 0.5rem;
+ border: 1px solid var(--fg);
+}
+
+/* borders */
+.nb {
+ border: none;
+}
+
+/* widths */
+.hw {
+ width: 50%;
+}
+
+.qw {
+ width: 25%;
+}
+
+.tqw {
+ width: 75%;
+}
+
+/* flex */
+.row {
+ display: flex;
+ align-items: center;
+}
+
+.sy {
+ overflow-y: scroll;
+}
+
+.fsy {
+ overflow-y: scroll;
+ height: 100%;
+}
+
+.fxc {
+ display: flex;
+ justify-content: center;
+ align-items: baseline;
+}
+
+/* flex spread */
+.fs {
+ display: flex;
+ justify-content: space-between;
+}
+
+.fsc {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.g1 {
+ gap: 0.5rem;
+}
+
+.g2 {
+ gap: 1rem;
+}
+
+.address {
+ font-family: "Courier New", Courier, monospace;
+}
+
+.spread {
+ justify-content: space-between;
+}
+
+.even {
+ justify-content: space-evenly;
+}
+
+.flexc {
+ justify-content: center;
+}
+
+.cp {
+ cursor: pointer;
+}
+
+/* centering */
+.gc {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.agc {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.ac {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.xc {
+ position: fixed;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 20;
+}
+
+.tc {
+ text-align: center;
+}
+
+.bc {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.blocks {
+ & * {
+ display: block;
+ }
+}
+
+.bold {
+ font-weight: 700;
+}
+
+.weak {
+ opacity: 0.7;
+}
+
+.all-c {
+ & * {
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.mb-1 {
+ margin-bottom: 1rem;
+}
+
+.error {
+ color: red;
+ text-align: center;
+}
+
+.tabs {
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+
+ & .tab {
+ cursor: pointer;
+ opacity: 0.5;
+ }
+
+ & .tab.active {
+ opacity: 1;
+ }
+}
+
+.disabled {
+ opacity: 0.5;
+}
+
+.smol {
+ font-size: 0.9rem;
+}
+
+/* The Modal (background) */
+#modal-bg {
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 998;
+}
+
+/* Modal Content */
+#modal-fg {
+ background-color: var(--bg);
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ padding: 20px;
+ z-index: 999;
+ max-height: 90vh;
+ min-height: 20vh;
+ max-width: 90vw;
+ overflow: auto;
+} \ No newline at end of file
diff --git a/packages/sortug/src/types.ts b/packages/sortug/src/types.ts
new file mode 100644
index 0000000..4d8ba56
--- /dev/null
+++ b/packages/sortug/src/types.ts
@@ -0,0 +1,9 @@
+export type BResult<Success, Error> = { ok: Success } | { error: Error };
+
+export type Result<Success> = { ok: Success } | { error: string };
+
+export type AsyncRes<Success> = Promise<Result<Success>>;
+
+export type HexString = `0x${string}`;
+
+export type UrbitID = `~${string}`;
diff --git a/packages/sortug/src/utils.ts b/packages/sortug/src/utils.ts
new file mode 100644
index 0000000..687b8db
--- /dev/null
+++ b/packages/sortug/src/utils.ts
@@ -0,0 +1,87 @@
+export function randomFromArray<T>(a: Array<T>): T {
+ const l = a.length;
+ const ind = Math.floor(Math.random() * l);
+ if (ind === l) return a[ind - 1];
+ else return a[ind];
+}
+export function randomFromArrayAcc<T>(a: Array<T>, s?: Set<T>): T {
+ const st = s ? s : new Set(a);
+ const l = a.length;
+ const ind = Math.floor(Math.random() * l);
+ const res = ind === l ? a[ind - 1] : a[ind];
+ if (st.has(res)) return randomFromArrayAcc(a, st);
+ else {
+ st.add(res);
+ // TODO have to return this too?
+ return res;
+ }
+}
+export function notRandomFromArray<T>(data: string, a: Array<T>): T {
+ const l = a.length;
+ const ind = hashTextToNumber(data, l - 1);
+ return a[ind];
+}
+
+function hashTextToNumber(text: string, max: number): number {
+ let hash = 0;
+ for (let i = 0; i < text.length; i++) {
+ const char = text.charCodeAt(i);
+ hash = (hash << 5) - hash + char;
+ hash = hash & hash; // Convert to 32-bit integer
+ }
+ // Make sure the hash is positive and within the given range
+ return Math.abs(hash) % max;
+}
+
+// Format time for display (HH:MM:SS)
+export const formatTime = (seconds: number): string => {
+ const hrs = Math.floor(seconds / 3600);
+ const mins = Math.floor((seconds % 3600) / 60);
+ const secs = Math.floor(seconds % 60);
+
+ return `${hrs.toString().padStart(2, "0")}:${mins
+ .toString()
+ .padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
+};
+
+export function date_diff(date: number, type: "short" | "long") {
+ const now = new Date().getTime();
+ const diff = now - new Date(date).getTime();
+ if (type == "short") {
+ return to_string(diff / 1000);
+ } else {
+ return to_string_long(diff / 1000);
+ }
+}
+
+function to_string(s: number) {
+ if (s < 60) {
+ return "now";
+ } else if (s < 3600) {
+ return `${Math.ceil(s / 60)}m`;
+ } else if (s < 86400) {
+ return `${Math.ceil(s / 60 / 60)}h`;
+ } else if (s < 2678400) {
+ return `${Math.ceil(s / 60 / 60 / 24)}d`;
+ } else if (s < 32140800) {
+ return `${Math.ceil(s / 60 / 60 / 24 / 30)}mo`;
+ } else {
+ return `${Math.ceil(s / 60 / 60 / 24 / 30 / 12)}y`;
+ }
+}
+
+function to_string_long(s: number) {
+ if (s < 60) {
+ return "right now";
+ } else if (s < 3600) {
+ return `${Math.ceil(s / 60)} minutes ago`;
+ } else if (s < 86400) {
+ return `${Math.ceil(s / 60 / 60)} hours ago`;
+ } else if (s < 2678400) {
+ return `${Math.ceil(s / 60 / 60 / 24)} days ago`;
+ } else if (s < 32140800) {
+ return `${Math.ceil(s / 60 / 60 / 24 / 30)} months ago`;
+ } else {
+ return `${Math.ceil(s / 60 / 60 / 24 / 30 / 12)} years ago`;
+ }
+}
diff --git a/packages/sortug/tsconfig.json b/packages/sortug/tsconfig.json
new file mode 100644
index 0000000..9c62f74
--- /dev/null
+++ b/packages/sortug/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/packages/tweetdeck/.gitignore b/packages/tweetdeck/.gitignore
new file mode 100644
index 0000000..0aa738a
--- /dev/null
+++ b/packages/tweetdeck/.gitignore
@@ -0,0 +1,35 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
+XClientTransaction
diff --git a/packages/tweetdeck/AGENTS.md b/packages/tweetdeck/AGENTS.md
new file mode 100644
index 0000000..b2aa31f
--- /dev/null
+++ b/packages/tweetdeck/AGENTS.md
@@ -0,0 +1,109 @@
+
+Default to using Bun instead of Node.js.
+
+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
+- Use `bun test` instead of `jest` or `vitest`
+- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
+- Bun automatically loads .env, so don't use dotenv.
+
+## APIs
+
+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
+- `Bun.redis` for Redis. Don't use `ioredis`.
+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
+- `WebSocket` is built-in. Don't use `ws`.
+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
+- Bun.$`ls` instead of execa.
+
+## Testing
+
+Use `bun test` to run tests.
+
+```ts#index.test.ts
+import { test, expect } from "bun:test";
+
+test("hello world", () => {
+ expect(1).toBe(1);
+});
+```
+
+## Frontend
+
+Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
+
+Server:
+
+```ts#index.ts
+import index from "./index.html"
+
+Bun.serve({
+ routes: {
+ "/": index,
+ "/api/users/:id": {
+ GET: (req) => {
+ return new Response(JSON.stringify({ id: req.params.id }));
+ },
+ },
+ },
+ // optional websocket support
+ websocket: {
+ open: (ws) => {
+ ws.send("Hello, world!");
+ },
+ message: (ws, message) => {
+ ws.send(message);
+ },
+ close: (ws) => {
+ // handle close
+ }
+ },
+ development: {
+ hmr: true,
+ console: true,
+ }
+})
+```
+
+HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
+
+```html#index.html
+<html>
+ <body>
+ <h1>Hello, world!</h1>
+ <script type="module" src="./frontend.tsx"></script>
+ </body>
+</html>
+```
+
+With the following `frontend.tsx`:
+
+```tsx#frontend.tsx
+import React from "react";
+
+// import .css files directly and it works
+import './index.css';
+
+import { createRoot } from "react-dom/client";
+
+const root = createRoot(document.body);
+
+export default function Frontend() {
+ return <h1>Hello, world!</h1>;
+}
+
+root.render(<Frontend />);
+```
+
+Then, run index.ts
+
+```sh
+bun --hot ./index.ts
+```
+
+For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
+
+
+codex resume 019a215f-7256-74a0-aaee-554a11867cd5
diff --git a/packages/tweetdeck/GEMINI.md b/packages/tweetdeck/GEMINI.md
new file mode 100644
index 0000000..c75a387
--- /dev/null
+++ b/packages/tweetdeck/GEMINI.md
@@ -0,0 +1,71 @@
+# Project Context: Bun React TweetDeck
+
+## Overview
+This project is a **TweetDeck-like Twitter client** built with **Bun** and **React 19**. It utilizes Bun's native server and bundler capabilities, bypassing the need for tools like Webpack or Vite. It features a column-based layout for viewing different Twitter feeds (For You, Following, Lists, etc.).
+
+## Tech Stack
+- **Runtime & Bundler:** [Bun](https://bun.com) (v1.3.1+)
+- **Frontend:** React 19, `prosody-ui` (local lib), Lucide React (icons).
+- **Backend:** `Bun.serve()` (Native Bun HTTP server).
+- **Data Fetching:** Custom Twitter API wrapper (`src/lib/fetching/twitter-api.ts`) using cookie-based authentication.
+- **State Persistence:** Custom `usePersistentState` hook.
+
+## Architecture
+The application is a **Hybrid SSR/SPA**:
+1. **Server (`src/index.ts`):**
+ * Serves the static `index.html`.
+ * Provides API endpoints at `/api/twitter/*`.
+ * Handles Twitter API requests by proxying them with a user-provided cookie.
+2. **Client (`src/frontend.tsx` -> `src/App.tsx`):**
+ * Bootstrapped via `src/index.html`.
+ * Manages the UI state (columns, decks).
+ * Communicates with the local `/api/twitter` endpoints.
+
+## Key Directories
+- `src/index.ts`: **Server Entry Point**. Defines API routes and serves static files.
+- `src/frontend.tsx`: **Client Entry Point**. Hydrates the React app.
+- `src/App.tsx`: Main application component.
+- `src/components/`: UI components.
+ * `ColumnBoard.tsx`: Manages the grid/layout of columns.
+ * `ChatColumn.tsx` / `TimelineColumn.tsx`: Individual feed columns.
+ * `TweetCard.tsx`: Displays a single tweet.
+- `src/lib/fetching/`: Backend logic for data fetching.
+ * `twitter-api.ts`: The core service communicating with Twitter.
+- `twatter-cookies.ts`: (Likely) Contains logic or types related to the auth cookie structure.
+
+## Development & Usage
+
+### Prerequisites
+- **Bun** must be installed.
+
+### Commands
+- **Install Dependencies:**
+ ```bash
+ bun install
+ ```
+- **Start Development Server (Hot Reload):**
+ ```bash
+ bun dev
+ ```
+ *Access at `http://localhost:3010`*
+- **Build for Production:**
+ ```bash
+ bun build
+ ```
+ *Outputs to `dist/`*
+- **Start Production Server:**
+ ```bash
+ bun start
+ ```
+
+### Conventions
+- **Bun-First:** Always use `bun` commands (`bun install`, `bun test`, `bun run`). Do not use `npm` or `yarn`.
+- **Styles:** CSS is imported directly into TSX files (e.g., `import './index.css'`).
+- **Local Libs:** The project relies on local libraries (`sortug`, `prosody-ui`) linked via `file:` paths in `package.json`.
+- **Auth:** Authentication is handled via a raw Twitter cookie string passed in API requests.
+
+## Notes for AI Agents
+- When adding new API routes, define them in `src/index.ts`.
+- When modifying the UI, look for components in `src/components/` first.
+- The project uses **React 19**, so use modern React patterns (Hooks, Functional Components).
+- Ensure `bun-python` usage is checked if modifying Python integration files (`tests/python.ts`, `src/lib/fetching/python.ts`).
diff --git a/packages/tweetdeck/NOTES.md b/packages/tweetdeck/NOTES.md
new file mode 100644
index 0000000..63a2ff6
--- /dev/null
+++ b/packages/tweetdeck/NOTES.md
@@ -0,0 +1,3 @@
+createBookmark and createRT return a freaking 404.
+
+which happens if the tweet in question is already bookmarked/RT'ed but it's not the case here!!
diff --git a/packages/tweetdeck/README.md b/packages/tweetdeck/README.md
new file mode 100644
index 0000000..0e71df9
--- /dev/null
+++ b/packages/tweetdeck/README.md
@@ -0,0 +1,21 @@
+# bun-react-template
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To start a development server:
+
+```bash
+bun dev
+```
+
+To run for production:
+
+```bash
+bun start
+```
+
+This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
diff --git a/packages/tweetdeck/bun-env.d.ts b/packages/tweetdeck/bun-env.d.ts
new file mode 100644
index 0000000..72f1c26
--- /dev/null
+++ b/packages/tweetdeck/bun-env.d.ts
@@ -0,0 +1,17 @@
+// Generated by `bun init`
+
+declare module "*.svg" {
+ /**
+ * A path to the SVG file
+ */
+ const path: `${string}.svg`;
+ export = path;
+}
+
+declare module "*.module.css" {
+ /**
+ * A record of class names to their corresponding CSS module classes
+ */
+ const classes: { readonly [key: string]: string };
+ export = classes;
+}
diff --git a/packages/tweetdeck/bun.lock b/packages/tweetdeck/bun.lock
new file mode 100644
index 0000000..a51402d
--- /dev/null
+++ b/packages/tweetdeck/bun.lock
@@ -0,0 +1,410 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "bun-react-tweetdeck",
+ "dependencies": {
+ "bun_python": "^0.1.10",
+ "lucide-react": "latest",
+ "node-html-parser": "^7.0.1",
+ "prosody-ui": "file:../../libs/prosody-ui",
+ "react": "^19",
+ "react-dom": "^19",
+ "sortug": "file:../../libs/sortug",
+ "sortug-ai": "file:../../libs/models",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ },
+ },
+ },
+ "packages": {
+ "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.70.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-AGEhifuvE22VxfQ5ROxViTgM8NuVQzEvqcN8bttR4AP24ythmNE/cL/SrOz79xiv7/osrsmCyErjsistJi7Z8A=="],
+
+ "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
+
+ "@elevenlabs/elevenlabs-js": ["@elevenlabs/elevenlabs-js@2.24.1", "", { "dependencies": { "command-exists": "^1.2.9", "node-fetch": "^2.7.0", "ws": "^8.18.3" } }, "sha512-i6bDExgK9lYne1vLhy85JJ3O8bNi5vPTfcgq8kT3HG4+3rgkUJtg5UP29Mn1KONc4ZOeYUomzxJ820uLkT9z6g=="],
+
+ "@google/genai": ["@google/genai@1.30.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.20.1" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w=="],
+
+ "@grpc/grpc-js": ["@grpc/grpc-js@1.14.1", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ=="],
+
+ "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
+
+ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
+
+ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
+
+ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
+
+ "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
+
+ "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
+
+ "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
+
+ "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
+
+ "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
+
+ "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
+
+ "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
+
+ "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
+
+ "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
+
+ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
+
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+
+ "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
+
+ "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="],
+
+ "@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
+
+ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
+
+ "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="],
+
+ "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
+
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
+ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
+
+ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
+
+ "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+
+ "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
+
+ "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
+
+ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
+
+ "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+
+ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+
+ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
+
+ "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
+
+ "bun_python": ["bun_python@0.1.10", "", { "peerDependencies": { "typescript": "^5.7.3" } }, "sha512-6c5owYOI7lYI7lBbYX99L6SQ5dT4jsabsV8yKDX15zi3cRurl/nWO576L3KbSTpGUR8wqQ8TDGRS7Wqwg9gunQ=="],
+
+ "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+
+ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
+
+ "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
+
+ "command-exists": ["command-exists@1.2.9", "", {}, "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="],
+
+ "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
+
+ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
+
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
+
+ "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
+
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
+
+ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
+
+ "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
+
+ "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
+
+ "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
+
+ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
+
+ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
+
+ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
+
+ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+ "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+
+ "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
+
+ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
+
+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+
+ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
+
+ "file-type": ["file-type@18.7.0", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", "token-types": "^5.0.1" } }, "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw=="],
+
+ "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
+
+ "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
+
+ "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
+
+ "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
+
+ "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
+
+ "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
+
+ "framer-motion": ["framer-motion@12.23.24", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="],
+
+ "franc-all": ["franc-all@7.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-ZR6ciLQTDBaOvBdkOd8+vqDzaLtmIXRa9GCzcAlaBpqNAKg9QrtClPmqiKac5/xZXfCZGMo1d8dIu1T0BLhHEg=="],
+
+ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+ "gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
+
+ "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
+
+ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
+
+ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+
+ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+ "glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
+
+ "google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
+
+ "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
+
+ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+ "groq-sdk": ["groq-sdk@0.36.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-wvxl7i6QWxLcIfM00mQQybYk15OAXJG0NBBQuMDHrQ2vi68uz2RqFTBKUNfEOVz8Lwy4eAgQIPBEFW5P3cXybA=="],
+
+ "gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
+
+ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+
+ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+ "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
+
+ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
+
+ "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
+
+ "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
+
+ "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
+
+ "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
+
+ "jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="],
+
+ "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
+
+ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
+
+ "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "lucide-react": ["lucide-react@0.554.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-St+z29uthEJVx0Is7ellNkgTEhaeSoA42I7JjOCBCrc5X6LYMGSv0P/2uS5HDLTExP5tpiqRD2PyUEOS6s9UXA=="],
+
+ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+ "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+ "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "motion": ["motion@12.23.24", "", { "dependencies": { "framer-motion": "^12.23.24", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="],
+
+ "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="],
+
+ "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="],
+
+ "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
+
+ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
+
+ "node-html-parser": ["node-html-parser@7.0.1", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA=="],
+
+ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
+
+ "openai": ["openai@6.9.1", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-vQ5Rlt0ZgB3/BNmTa7bIijYFhz3YBceAA3Z4JuoMSBftBF9YqFHIEhZakSs+O/Ad7EaoEimZvHxD5ylRjN11Lg=="],
+
+ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+ "peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="],
+
+ "playht": ["playht@0.21.0", "", { "dependencies": { "@grpc/grpc-js": "^1.9.4", "axios": "^1.4.0", "cross-fetch": "^4.0.0", "deepmerge-ts": "^7.1.5", "file-type": "^18.5.0", "protobufjs": "^7.2.5", "tslib": "^2.1.0" } }, "sha512-63dWfsoGNOxfl91U3knrON4HcgtdPZ+e0Q3F8JX22T6dvX17i217lfw8cq1OzIBWVxpHms8ebhgUU/Gvs0/8Eg=="],
+
+ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
+
+ "prosody-ui": ["prosody-ui@file:../../libs/prosody-ui", { "dependencies": { "franc-all": "^7.2.0", "glotscript": "file:../glotscript", "motion": "^12.11.3", "sortug": "file:../sortug", "sortug-ai": "file:../models" }, "peerDependencies": { "react": ">=19.0.0", "react-dom": ">=19.0.0", "typescript": "^5.0.0" } }],
+
+ "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
+
+ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
+
+ "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
+
+ "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
+
+ "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+
+ "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="],
+
+ "replicate": ["replicate@1.4.0", "", { "optionalDependencies": { "readable-stream": ">=4.0.0" } }, "sha512-1ufKejfUVz/azy+5TnzQP7U1+MHVWZ6psnQ06az8byUUnRhT+DZ/MvewzB1NQYBVMgNKR7xPDtTwlcP5nv/5+w=="],
+
+ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
+
+ "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
+
+ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
+ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
+ "sortug": ["@sortug/lib@file:../../libs/sortug", { "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
+ "sortug-ai": ["@sortug/ai@file:../../libs/models", { "dependencies": { "@anthropic-ai/sdk": "latest", "@elevenlabs/elevenlabs-js": "^2.24.1", "@google/genai": "latest", "bcp-47": "^2.1.0", "franc-all": "^7.2.0", "groq-sdk": "latest", "iso-639-3": "file:../lang", "openai": "latest", "playht": "latest", "replicate": "latest", "sortug": "file:../:sortug" }, "devDependencies": { "@types/bun": "latest", "@types/mime-types": "^3.0.1" }, "peerDependencies": { "typescript": "latest" } }],
+
+ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strtok3": ["strtok3@7.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.1.3" } }, "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg=="],
+
+ "token-types": ["token-types@5.0.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg=="],
+
+ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+
+ "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="],
+
+ "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
+
+ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
+
+ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
+
+ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
+
+ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
+
+ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+
+ "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
+
+ "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
+ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
+
+ "fetch-blob/web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
+
+ "gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
+
+ "prosody-ui/glotscript": ["glotscript@file:../../libs/glotscript", {}],
+
+ "prosody-ui/sortug": ["sortug@file:../../libs/sortug", {}],
+
+ "prosody-ui/sortug-ai": ["sortug-ai@file:../../libs/models", {}],
+
+ "sortug-ai/iso-639-3": ["iso-639-3@file:../../libs/lang", {}],
+
+ "sortug-ai/sortug": ["sortug@file:../../libs/:sortug", {}],
+
+ "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
+
+ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
+ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+ }
+}
diff --git a/packages/tweetdeck/bunfig.toml b/packages/tweetdeck/bunfig.toml
new file mode 100644
index 0000000..9819bf6
--- /dev/null
+++ b/packages/tweetdeck/bunfig.toml
@@ -0,0 +1,2 @@
+[serve.static]
+env = "BUN_PUBLIC_*" \ No newline at end of file
diff --git a/packages/tweetdeck/package.json b/packages/tweetdeck/package.json
new file mode 100644
index 0000000..bc9a71c
--- /dev/null
+++ b/packages/tweetdeck/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "bun-react-tweetdeck",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "bun --hot src/index.ts",
+ "build": "bun build ./src/index.html --outdir=dist --sourcemap --target=browser --minify --define:process.env.NODE_ENV='\"production\"' --env='BUN_PUBLIC_*'",
+ "start": "NODE_ENV=production bun src/index.ts"
+ },
+ "dependencies": {
+ "bun_python": "^0.1.10",
+ "lucide-react": "latest",
+ "node-html-parser": "^7.0.1",
+ "react": "^19",
+ "react-dom": "^19",
+ "@sortug/lib": "workspce:*",
+ "@sortug/prosody-ui": "workspce:*",
+ "@sortug/ai": "workspce:*"
+ },
+ "devDependencies": {
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "@types/bun": "latest"
+ }
+}
diff --git a/packages/tweetdeck/src/APITester.tsx b/packages/tweetdeck/src/APITester.tsx
new file mode 100644
index 0000000..fd2af48
--- /dev/null
+++ b/packages/tweetdeck/src/APITester.tsx
@@ -0,0 +1,39 @@
+import { useRef, type FormEvent } from "react";
+
+export function APITester() {
+ const responseInputRef = useRef<HTMLTextAreaElement>(null);
+
+ const testEndpoint = async (e: FormEvent<HTMLFormElement>) => {
+ e.preventDefault();
+
+ try {
+ const form = e.currentTarget;
+ const formData = new FormData(form);
+ const endpoint = formData.get("endpoint") as string;
+ const url = new URL(endpoint, location.href);
+ const method = formData.get("method") as string;
+ const res = await fetch(url, { method });
+
+ const data = await res.json();
+ responseInputRef.current!.value = JSON.stringify(data, null, 2);
+ } catch (error) {
+ responseInputRef.current!.value = String(error);
+ }
+ };
+
+ return (
+ <div className="api-tester">
+ <form onSubmit={testEndpoint} className="endpoint-row">
+ <select name="method" className="method">
+ <option value="GET">GET</option>
+ <option value="PUT">PUT</option>
+ </select>
+ <input type="text" name="endpoint" defaultValue="/api/hello" className="url-input" placeholder="/api/hello" />
+ <button type="submit" className="send-button">
+ Send
+ </button>
+ </form>
+ <textarea ref={responseInputRef} readOnly placeholder="Response will appear here..." className="response-area" />
+ </div>
+ );
+}
diff --git a/packages/tweetdeck/src/App.tsx b/packages/tweetdeck/src/App.tsx
new file mode 100644
index 0000000..924ff9a
--- /dev/null
+++ b/packages/tweetdeck/src/App.tsx
@@ -0,0 +1,310 @@
+import { useCallback, useEffect, useMemo, useState } from "react";
+import "./styles/normalize.css";
+import "./styles/index.css";
+import { Sidebar, type NewAccountInput } from "./components/Sidebar";
+import { ColumnBoard } from "./components/ColumnBoard";
+import { AddColumnModal } from "./components/AddColumnModal";
+import { usePersistentState } from "./hooks/usePersistentState";
+import type {
+ ColumnSnapshot,
+ ColumnState,
+ DeckAccount,
+ DeckColumn,
+ DeckListsCache,
+ FullscreenState,
+} from "./types/app";
+import type { Tweet } from "./lib/fetching/types";
+import { generateId } from "./lib/utils/id";
+import { twitterClient } from "./lib/client/twitterClient";
+import { FullscreenColumn } from "./components/FullscreenColumn";
+
+const ACCOUNTS_KEY = "tweetdeck.accounts";
+const COLUMNS_KEY = "tweetdeck.columns";
+
+export function App() {
+ const [accounts, setAccounts] = usePersistentState<DeckAccount[]>(
+ ACCOUNTS_KEY,
+ [],
+ );
+ const [columns, setColumns] = usePersistentState<DeckColumn[]>(
+ COLUMNS_KEY,
+ [],
+ );
+ const [listsCache, setListsCache] = useState<DeckListsCache>({});
+ const [activeAccountId, setActiveAccountId] = useState<string | undefined>(
+ () => accounts[0]?.id,
+ );
+ const [isModalOpen, setModalOpen] = useState(false);
+ const [toast, setToast] = useState<string | null>(null);
+ const [fullscreen, setFullscreen] = useState<FullscreenState | null>(null);
+ const [columnSnapshots, setColumnSnapshots] = useState<
+ Record<string, ColumnSnapshot>
+ >({});
+
+ useEffect(() => {
+ if (!activeAccountId) {
+ const firstAccount = accounts[0];
+ if (firstAccount) {
+ setActiveAccountId(firstAccount.id);
+ }
+ }
+ }, [accounts, activeAccountId]);
+
+ useEffect(() => {
+ const acs = accounts.filter((a) => !a.avatar || !a.username);
+ console.log({ acs });
+ const nacs = acs.map(async (acc) => {
+ const our = await twitterClient.own({ cookie: acc.cookie });
+ const nacc = {
+ ...acc,
+ handle: our.username,
+ label: our.name,
+ avatar: our.avatar,
+ };
+ return nacc;
+ });
+ Promise.all(nacs)
+ .then((acs) => setAccounts(acs))
+ .catch((err) => console.error(err));
+ }, []);
+
+ const handleAddAccount = useCallback(
+ (payload: NewAccountInput) => {
+ const label = `Session ${accounts.length + 1}`;
+ const account: DeckAccount = {
+ id: generateId(),
+ label,
+ accent: randomAccent(),
+ cookie: payload.cookie.trim(),
+ createdAt: Date.now(),
+ };
+ setAccounts((prev) => [...prev, account]);
+ setActiveAccountId(account.id);
+ setToast(`${account.label} is ready`);
+ },
+ [accounts.length, setAccounts],
+ );
+
+ const handleRemoveAccount = useCallback(
+ (accountId: string) => {
+ setAccounts((prev) => prev.filter((account) => account.id !== accountId));
+ setColumns((prev) =>
+ prev.filter((column) => column.accountId !== accountId),
+ );
+ setListsCache((prev) => {
+ const next = { ...prev };
+ delete next[accountId];
+ return next;
+ });
+ if (activeAccountId === accountId) {
+ setActiveAccountId(undefined);
+ }
+ },
+ [activeAccountId, setAccounts, setColumns],
+ );
+
+ const handleAddColumn = useCallback(
+ (column: Omit<DeckColumn, "id">) => {
+ const nextColumn = { ...column, id: generateId() };
+ setColumns((prev) => [...prev, nextColumn]);
+ setToast(`${nextColumn.title} added to deck`);
+ },
+ [setColumns],
+ );
+
+ const handleRemoveColumn = useCallback(
+ (id: string) => {
+ setColumns((prev) => prev.filter((column) => column.id !== id));
+ },
+ [setColumns],
+ );
+
+ const fetchLists = useCallback(
+ async (accountId: string) => {
+ const account = accounts.find((acc) => acc.id === accountId);
+ if (!account) throw new Error("Account not found");
+ if (listsCache[accountId]) return listsCache[accountId];
+ console.log({ listsCache });
+ const lists = await twitterClient.lists({ cookie: account.cookie });
+ console.log({ lists });
+ setListsCache((prev) => ({ ...prev, [accountId]: lists }));
+ return lists;
+ },
+ [accounts, listsCache],
+ );
+
+ const handleColumnStateChange = useCallback(
+ (columnId: string, state: ColumnState) => {
+ setColumns((prev) =>
+ prev.map((column) =>
+ column.id === columnId ? { ...column, state } : column,
+ ),
+ );
+ },
+ [setColumns],
+ );
+
+ const handleColumnSnapshot = useCallback(
+ (columnId: string, snapshot: ColumnSnapshot) => {
+ setColumnSnapshots((prev) => {
+ const existing = prev[columnId];
+ if (
+ existing &&
+ existing.tweets === snapshot.tweets &&
+ existing.label === snapshot.label
+ ) {
+ return prev;
+ }
+ return {
+ ...prev,
+ [columnId]: { tweets: snapshot.tweets, label: snapshot.label },
+ };
+ });
+ },
+ [],
+ );
+
+ const openFullscreen = useCallback(
+ (payload: FullscreenState) => {
+ const snapshot = columnSnapshots[payload.column.id];
+ setFullscreen({
+ ...payload,
+ tweets: snapshot?.tweets ?? payload.tweets,
+ columnLabel: snapshot?.label ?? payload.columnLabel,
+ });
+ },
+ [columnSnapshots],
+ );
+
+ useEffect(() => {
+ if (!fullscreen) return;
+ const snapshot = columnSnapshots[fullscreen.column.id];
+ if (!snapshot) return;
+ if (
+ snapshot.tweets === fullscreen.tweets &&
+ snapshot.label === fullscreen.columnLabel
+ ) {
+ return;
+ }
+ setFullscreen((prev) => {
+ if (!prev) return prev;
+ if (prev.column.id !== fullscreen.column.id) return prev;
+ const tweets = snapshot.tweets;
+ const index = Math.min(prev.index, Math.max(tweets.length - 1, 0));
+ return {
+ ...prev,
+ tweets,
+ columnTitle: snapshot.label,
+ index,
+ };
+ });
+ }, [columnSnapshots, fullscreen]);
+
+ const content = useMemo(
+ () => (
+ <ColumnBoard
+ columns={columns}
+ accounts={accounts}
+ onRemove={handleRemoveColumn}
+ onStateChange={handleColumnStateChange}
+ onSnapshot={handleColumnSnapshot}
+ onEnterFullscreen={openFullscreen}
+ />
+ ),
+ [
+ accounts,
+ columns,
+ handleRemoveColumn,
+ handleColumnStateChange,
+ handleColumnSnapshot,
+ openFullscreen,
+ ],
+ );
+
+ return (
+ <div className="app-shell">
+ <Sidebar
+ accounts={accounts}
+ activeAccountId={activeAccountId}
+ onActivate={(id) => setActiveAccountId(id)}
+ onAddAccount={handleAddAccount}
+ onRemoveAccount={handleRemoveAccount}
+ onAddColumn={() => setModalOpen(true)}
+ />
+
+ <main>{content}</main>
+
+ <AddColumnModal
+ accounts={accounts}
+ activeAccountId={activeAccountId}
+ isOpen={isModalOpen}
+ onClose={() => setModalOpen(false)}
+ onAdd={handleAddColumn}
+ fetchLists={fetchLists}
+ listsCache={listsCache}
+ />
+
+ {toast && (
+ <div className="toast" onAnimationEnd={() => setToast(null)}>
+ {toast}
+ </div>
+ )}
+
+ {fullscreen && (
+ <FullscreenColumn
+ state={fullscreen}
+ onExit={() => setFullscreen(null)}
+ onNavigate={(step) => {
+ setFullscreen((prev) => {
+ if (!prev) return prev;
+ if (!prev.tweets.length) return prev;
+ const nextIndex = Math.min(
+ prev.tweets.length - 1,
+ Math.max(0, prev.index + step),
+ );
+ if (nextIndex === prev.index) return prev;
+ return { ...prev, index: nextIndex };
+ });
+ }}
+ hasPrevColumn={fullscreen.columnIndex > 0}
+ hasNextColumn={fullscreen.columnIndex < columns.length - 1}
+ onSwitchColumn={(direction) => {
+ setFullscreen((prev) => {
+ if (!prev) return prev;
+ const nextIndex = prev.columnIndex + direction;
+ if (nextIndex < 0) return prev;
+ if (nextIndex >= columns.length) {
+ setModalOpen(true);
+ return prev;
+ }
+ const nextColumn = columns[nextIndex];
+ if (!nextColumn) return prev;
+ const snapshot = columnSnapshots[nextColumn.id];
+ const account = accounts.find(
+ (acc) => acc.id === nextColumn.accountId,
+ );
+ const tweets = snapshot?.tweets ?? [];
+ return {
+ column: nextColumn,
+ columnIndex: nextIndex,
+ columnLabel: snapshot?.label ?? nextColumn.title,
+ accent: account?.accent ?? prev.accent,
+ tweets,
+ index: 0,
+ };
+ });
+ }}
+ onAddColumn={() => setModalOpen(true)}
+ />
+ )}
+ </div>
+ );
+}
+
+function randomAccent(): string {
+ const palette = ["#7f5af0", "#2cb67d", "#f25f4c", "#f0a500", "#19a7ce"];
+ const pick = palette[Math.floor(Math.random() * palette.length)];
+ return pick ?? "#7f5af0";
+}
+
+export default App;
diff --git a/packages/tweetdeck/src/components/AddColumnModal.tsx b/packages/tweetdeck/src/components/AddColumnModal.tsx
new file mode 100644
index 0000000..b7098ed
--- /dev/null
+++ b/packages/tweetdeck/src/components/AddColumnModal.tsx
@@ -0,0 +1,234 @@
+import { useEffect, useMemo, useState } from "react";
+import type { DeckAccount, DeckColumn, DeckListsCache } from "../types/app";
+import type { TwitterList } from "../lib/fetching/types";
+
+interface AddColumnModalProps {
+ accounts: DeckAccount[];
+ isOpen: boolean;
+ onClose: () => void;
+ onAdd: (column: Omit<DeckColumn, "id">) => void;
+ activeAccountId?: string;
+ fetchLists: (accountId: string) => Promise<TwitterList[]>;
+ listsCache: DeckListsCache;
+}
+
+const columnOptions = [
+ {
+ id: "foryou",
+ label: "For You",
+ description: "Twitter's AI-ranked firehose",
+ },
+ {
+ id: "following",
+ label: "Following",
+ description: "Chronological feed of people you follow",
+ },
+ { id: "bookmarks", label: "Bookmarks", description: "Your saved deep dives" },
+ { id: "list", label: "List", description: "Curate a topic-specific stream" },
+ { id: "chat", label: "Chat", description: "Mentions & notifications" },
+] as const;
+
+export function AddColumnModal({
+ accounts,
+ activeAccountId,
+ isOpen,
+ onClose,
+ onAdd,
+ fetchLists,
+ listsCache,
+}: AddColumnModalProps) {
+ const [kind, setKind] = useState<DeckColumn["kind"]>("foryou");
+ const [accountId, setAccountId] = useState<string>(
+ activeAccountId || accounts[0]?.id || "",
+ );
+ const [title, setTitle] = useState("For You");
+ const [listId, setListId] = useState<string>("");
+ const [listOptions, setListOptions] = useState<TwitterList[]>([]);
+ const [listsLoading, setListsLoading] = useState(false);
+ const [listsError, setListsError] = useState<string | undefined>();
+
+ useEffect(() => {
+ if (!isOpen) return;
+ setAccountId(activeAccountId || accounts[0]?.id || "");
+ }, [isOpen, activeAccountId, accounts]);
+
+ useEffect(() => {
+ setTitle(defaultTitle(kind));
+ }, [kind]);
+
+ useEffect(() => {
+ if (!isOpen || kind !== "list" || !accountId) return;
+ let mounted = true;
+ async function loadLists() {
+ try {
+ setListsLoading(true);
+ setListsError(undefined);
+ const cached = listsCache[accountId];
+ if (cached) {
+ setListOptions(cached);
+ return;
+ }
+ const lists = await fetchLists(accountId);
+ if (!mounted) return;
+ setListOptions(lists);
+ } catch (error) {
+ setListsError(
+ error instanceof Error ? error.message : "Failed to load lists",
+ );
+ } finally {
+ if (mounted) setListsLoading(false);
+ }
+ }
+ loadLists();
+ return () => {
+ mounted = false;
+ };
+ }, [accountId, fetchLists, isOpen, kind, listsCache]);
+
+ useEffect(() => {
+ if (!isOpen) return;
+ setListId("");
+ setListOptions([]);
+ }, [accountId, isOpen, kind]);
+
+ const selectedAccount = useMemo(
+ () => accounts.find((account) => account.id === accountId),
+ [accountId, accounts],
+ );
+
+ const canSubmit = Boolean(selectedAccount && (kind !== "list" || listId));
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ if (!canSubmit || !selectedAccount) return;
+ onAdd({
+ accountId: selectedAccount.id,
+ account: selectedAccount.username || "",
+ kind,
+ title: title.trim() || defaultTitle(kind),
+ listId: listId || undefined,
+ listName:
+ kind === "list"
+ ? listOptions.find((list) => list.id === listId)?.name
+ : undefined,
+ });
+ onClose();
+ };
+
+ useEffect(() => {
+ if (!isOpen) return;
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "Escape") onClose();
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [isOpen, onClose]);
+
+ if (!isOpen) return null;
+
+ return (
+ <div
+ className="modal-backdrop"
+ role="dialog"
+ aria-modal="true"
+ onMouseDown={(event) => {
+ if (event.target === event.currentTarget) {
+ onClose();
+ }
+ }}
+ >
+ <div className="modal">
+ <header>
+ <div>
+ <p className="eyebrow">New column</p>
+ <h2>Design your stream</h2>
+ </div>
+ <button
+ className="ghost"
+ onClick={onClose}
+ aria-label="Close add column modal"
+ >
+ ×
+ </button>
+ </header>
+ <form onSubmit={handleSubmit} className="modal-body">
+ <label>
+ Account
+ <select
+ value={accountId}
+ onChange={(e) => setAccountId(e.target.value)}
+ >
+ {accounts.map((account) => (
+ <option value={account.id} key={account.id}>
+ {account.label}
+ {account.handle ? ` (@${account.handle})` : ""}
+ </option>
+ ))}
+ </select>
+ </label>
+ <div className="option-grid">
+ {columnOptions.map((option) => (
+ <button
+ type="button"
+ key={option.id}
+ className={`option ${kind === option.id ? "selected" : ""}`}
+ onClick={() => setKind(option.id)}
+ >
+ <div>
+ <strong>{option.label}</strong>
+ <p>{option.description}</p>
+ </div>
+ </button>
+ ))}
+ </div>
+ {kind === "list" && (
+ <label>
+ Choose a list
+ {listsError && <span className="error">{listsError}</span>}
+ <select
+ value={listId}
+ disabled={listsLoading}
+ onChange={(e) => setListId(e.target.value)}
+ >
+ <option value="" disabled>
+ {listsLoading ? "Loading lists..." : "Select a list"}
+ </option>
+ {listOptions.map((list) => (
+ <option key={list.id} value={list.id}>
+ {list.name} ({list.member_count})
+ </option>
+ ))}
+ </select>
+ </label>
+ )}
+ <label>
+ Column title
+ <input
+ value={title}
+ onChange={(e) => setTitle(e.target.value)}
+ placeholder="Custom title"
+ />
+ </label>
+ <button className="primary" type="submit" disabled={!canSubmit}>
+ Add column
+ </button>
+ </form>
+ </div>
+ </div>
+ );
+}
+
+function defaultTitle(kind: DeckColumn["kind"]) {
+ switch (kind) {
+ case "following":
+ return "Following";
+ case "bookmarks":
+ return "Bookmarks";
+ case "list":
+ return "List";
+ case "chat":
+ return "Chat";
+ default:
+ return "For You";
+ }
+}
diff --git a/packages/tweetdeck/src/components/ChatCard.tsx b/packages/tweetdeck/src/components/ChatCard.tsx
new file mode 100644
index 0000000..d7d885c
--- /dev/null
+++ b/packages/tweetdeck/src/components/ChatCard.tsx
@@ -0,0 +1,56 @@
+import type { TwitterNotification } from "../lib/fetching/types";
+import { timeAgo } from "../lib/utils/time";
+
+interface ChatCardProps {
+ notification: TwitterNotification;
+ accent: string;
+}
+
+export function ChatCard({ notification, accent }: ChatCardProps) {
+ const firstUser = Object.values(notification.users)[0];
+ const timestamp = timeAgo(Number(notification.timestampMs));
+
+ return (
+ <article className="chat-card" style={{ borderColor: accent }}>
+ <div className="chat-avatar">
+ {firstUser?.profile_image_url_https ? (
+ <img src={firstUser.profile_image_url_https} alt={firstUser.name} loading="lazy" />
+ ) : (
+ <span>{firstUser?.name?.[0] ?? "?"}</span>
+ )}
+ </div>
+ <div className="chat-body">
+ <header>
+ <strong>{firstUser?.name ?? "Notification"}</strong>
+ {firstUser?.screen_name && <span className="muted">@{firstUser.screen_name}</span>}
+ <span className="muted dot" aria-hidden="true">
+ •
+ </span>
+ <span className="muted">{timestamp}</span>
+ </header>
+ <p>{highlight(notification.message.text)}</p>
+ </div>
+ </article>
+ );
+}
+
+function highlight(text: string) {
+ const parts = text.split(/([@#][A-Za-z0-9_]+)/g);
+ return parts.map((part, index) => {
+ if (part.startsWith("@")) {
+ return (
+ <span key={index} className="mention">
+ {part}
+ </span>
+ );
+ }
+ if (part.startsWith("#")) {
+ return (
+ <span key={index} className="hashtag">
+ {part}
+ </span>
+ );
+ }
+ return <span key={index}>{part}</span>;
+ });
+}
diff --git a/packages/tweetdeck/src/components/ChatColumn.tsx b/packages/tweetdeck/src/components/ChatColumn.tsx
new file mode 100644
index 0000000..0c336e5
--- /dev/null
+++ b/packages/tweetdeck/src/components/ChatColumn.tsx
@@ -0,0 +1,62 @@
+import { useCallback, useEffect, useState } from "react";
+import type { DeckAccount, DeckColumn, ChatState } from "../types/app";
+import { twitterClient } from "../lib/client/twitterClient";
+import { ChatCard } from "./ChatCard";
+
+interface ChatColumnProps {
+ column: DeckColumn & { kind: "chat" };
+ account: DeckAccount;
+ onRemove: () => void;
+}
+
+export function ChatColumn({ column, account, onRemove }: ChatColumnProps) {
+ const [state, setState] = useState<ChatState>({ entries: [], isLoading: false });
+ const [error, setError] = useState<string | undefined>();
+
+ const refresh = useCallback(async () => {
+ setState(prev => ({ ...prev, isLoading: true }));
+ setError(undefined);
+ try {
+ const notifications = await twitterClient.notifications({ cookie: account.cookie });
+ setState({ entries: notifications, isLoading: false });
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to refresh chat");
+ setState(prev => ({ ...prev, isLoading: false }));
+ }
+ }, [account.cookie]);
+
+ useEffect(() => {
+ refresh();
+ }, [refresh, account.id]);
+
+ return (
+ <article className="column">
+ <header>
+ <div>
+ <p className="eyebrow">Signal</p>
+ <h3>{column.title || "Chat"}</h3>
+ <p className="muted tiny">Mentions, follows and notifications for {account.label}</p>
+ </div>
+ <div className="column-actions">
+ <button className="ghost" onClick={refresh} aria-label="Refresh chat">
+ ↻
+ </button>
+ <button className="ghost" onClick={onRemove} aria-label="Remove column">
+ ×
+ </button>
+ </div>
+ </header>
+ {error && <p className="error">{error}</p>}
+ {state.isLoading && !state.entries.length ? (
+ <div className="column-loading">Loading…</div>
+ ) : (
+ <div className="chat-stack">
+ {state.entries.map(entry => (
+ <ChatCard key={entry.id} notification={entry} accent={account.accent} />
+ ))}
+ {!state.entries.length && <p className="muted">No recent notifications.</p>}
+ </div>
+ )}
+ </article>
+ );
+}
diff --git a/packages/tweetdeck/src/components/ColumnBoard.tsx b/packages/tweetdeck/src/components/ColumnBoard.tsx
new file mode 100644
index 0000000..87da3e1
--- /dev/null
+++ b/packages/tweetdeck/src/components/ColumnBoard.tsx
@@ -0,0 +1,93 @@
+import { useMemo } from "react";
+import type {
+ ColumnSnapshot,
+ ColumnState,
+ DeckAccount,
+ DeckColumn,
+ FullscreenState,
+} from "../types/app";
+import { TimelineColumn, type TimelineConfig } from "./TimelineColumn";
+import { ChatColumn } from "./ChatColumn";
+
+interface ColumnBoardProps {
+ columns: DeckColumn[];
+ accounts: DeckAccount[];
+ onRemove: (id: string) => void;
+ onStateChange: (columnId: string, state: ColumnState) => void;
+ onSnapshot: (columnId: string, snapshot: ColumnSnapshot) => void;
+ onEnterFullscreen: (payload: FullscreenState) => void;
+}
+
+export function ColumnBoard({
+ columns,
+ accounts,
+ onRemove,
+ onStateChange,
+ onSnapshot,
+ onEnterFullscreen,
+}: ColumnBoardProps) {
+ const accountMap = useMemo(
+ () => Object.fromEntries(accounts.map(account => [account.id, account])),
+ [accounts],
+ );
+
+ if (!columns.length) {
+ return (
+ <section className="empty-board">
+ <p className="eyebrow">No columns yet</p>
+ <h2>Build your deck</h2>
+ <p>Add some columns from the left panel to start streaming timelines.</p>
+ </section>
+ );
+ }
+
+ return (
+ <section className="column-board">
+ {columns.map((column, columnIndex) => {
+ const account = accountMap[column.accountId];
+ if (!account) {
+ return (
+ <div className="column missing" key={column.id}>
+ <header>
+ <div>
+ <p className="eyebrow">Account missing</p>
+ <h3>{column.title}</h3>
+ </div>
+ <button className="ghost" onClick={() => onRemove(column.id)}>
+ Remove
+ </button>
+ </header>
+ <p className="muted">The account for this column was removed.</p>
+ </div>
+ );
+ }
+ if (isChatColumn(column)) {
+ return (
+ <ChatColumn
+ key={column.id}
+ column={column}
+ account={account}
+ onRemove={() => onRemove(column.id)}
+ />
+ );
+ }
+ return (
+ <TimelineColumn
+ key={column.id}
+ column={column as TimelineConfig}
+ account={account}
+ onRemove={() => onRemove(column.id)}
+ onStateChange={onStateChange}
+ onSnapshot={onSnapshot}
+ onEnterFullscreen={onEnterFullscreen}
+ columnIndex={columnIndex}
+ />
+ );
+ })}
+ </section>
+ );
+}
+
+function isChatColumn(column: DeckColumn): column is DeckColumn & { kind: "chat" } {
+ return column.kind === "chat";
+}
diff --git a/packages/tweetdeck/src/components/FullscreenColumn.tsx b/packages/tweetdeck/src/components/FullscreenColumn.tsx
new file mode 100644
index 0000000..66959b8
--- /dev/null
+++ b/packages/tweetdeck/src/components/FullscreenColumn.tsx
@@ -0,0 +1,134 @@
+import { useEffect } from "react";
+import type { FullscreenState } from "../types/app";
+import { TweetCard } from "./TweetCard";
+
+interface FullscreenColumnProps {
+ state: FullscreenState;
+ onExit: () => void;
+ onNavigate: (step: number) => void;
+ onSwitchColumn: (step: number) => void;
+ hasPrevColumn: boolean;
+ hasNextColumn: boolean;
+ onAddColumn: () => void;
+}
+
+export function FullscreenColumn({
+ state,
+ onExit,
+ onNavigate,
+ onSwitchColumn,
+ hasPrevColumn,
+ hasNextColumn,
+ onAddColumn,
+}: FullscreenColumnProps) {
+ console.log({ state });
+ const tweet = state.tweets[state.index];
+ const hasTweets = state.tweets.length > 0;
+
+ useEffect(() => {
+ const handler = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ onExit();
+ return;
+ }
+ if (event.key === "ArrowDown") {
+ event.preventDefault();
+ onNavigate(1);
+ }
+ if (event.key === "ArrowUp") {
+ event.preventDefault();
+ onNavigate(-1);
+ }
+ if (event.key === "ArrowRight") {
+ event.preventDefault();
+ if (hasNextColumn) {
+ onSwitchColumn(1);
+ } else {
+ onAddColumn();
+ }
+ }
+ if (event.key === "ArrowLeft") {
+ event.preventDefault();
+ if (hasPrevColumn) {
+ onSwitchColumn(-1);
+ }
+ }
+ };
+ window.addEventListener("keydown", handler);
+ return () => window.removeEventListener("keydown", handler);
+ }, [
+ onExit,
+ onNavigate,
+ onSwitchColumn,
+ hasPrevColumn,
+ hasNextColumn,
+ onAddColumn,
+ ]);
+
+ return (
+ <div className="fullscreen-overlay">
+ <button
+ className="ghost fullscreen-close"
+ onClick={onExit}
+ aria-label="Close fullscreen view"
+ >
+ ×
+ </button>
+ <div className="fullscreen-content">
+ <header>
+ <p className="eyebrow">
+ {state.columnLabel}@{state.column.account}
+ </p>
+ <p className="muted tiny">
+ {hasTweets
+ ? `${state.index + 1} / ${state.tweets.length}`
+ : "0 / 0"}
+ </p>
+ </header>
+ <div className="fullscreen-card">
+ {hasTweets && tweet ? (
+ <TweetCard tweet={tweet} accent={state.accent} />
+ ) : (
+ <div className="fullscreen-empty">
+ <p>No tweets loaded for this column yet.</p>
+ <p className="muted">
+ Try refreshing the column or exit fullscreen.
+ </p>
+ </div>
+ )}
+ </div>
+ <div className="fullscreen-controls">
+ <button
+ className="ghost"
+ onClick={() => onNavigate(-1)}
+ disabled={!hasTweets || state.index === 0}
+ >
+ ↑ Previous tweet
+ </button>
+ <button
+ className="ghost"
+ onClick={() => onNavigate(1)}
+ disabled={!hasTweets || state.index >= state.tweets.length - 1}
+ >
+ Next tweet ↓
+ </button>
+ </div>
+ <div className="fullscreen-column-controls">
+ <button
+ className="ghost"
+ onClick={() => onSwitchColumn(-1)}
+ disabled={!hasPrevColumn}
+ >
+ ← Previous column
+ </button>
+ <button
+ className="ghost"
+ onClick={() => (hasNextColumn ? onSwitchColumn(1) : onAddColumn())}
+ >
+ {hasNextColumn ? "Next column →" : "+ Add column →"}
+ </button>
+ </div>
+ </div>
+ </div>
+ );
+}
diff --git a/packages/tweetdeck/src/components/Sidebar.tsx b/packages/tweetdeck/src/components/Sidebar.tsx
new file mode 100644
index 0000000..3d9b85d
--- /dev/null
+++ b/packages/tweetdeck/src/components/Sidebar.tsx
@@ -0,0 +1,153 @@
+import { useEffect, useState } from "react";
+import type { DeckAccount } from "../types/app";
+import { twitterClient } from "@/lib/client/twitterClient";
+
+export interface NewAccountInput {
+ cookie: string;
+}
+
+interface SidebarProps {
+ accounts: DeckAccount[];
+ activeAccountId?: string;
+ onActivate: (id: string) => void;
+ onAddAccount: (payload: NewAccountInput) => void;
+ onRemoveAccount: (id: string) => void;
+ onAddColumn: () => void;
+}
+
+export function Sidebar({
+ accounts,
+ activeAccountId,
+ onActivate,
+ onAddAccount,
+ onRemoveAccount,
+ onAddColumn,
+}: SidebarProps) {
+ const [isAdding, setIsAdding] = useState(!accounts.length);
+ const [cookie, setCookie] = useState("");
+ const [showCookie, setShowCookie] = useState(false);
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ if (!cookie.trim()) return;
+ onAddAccount({ cookie: decodeURIComponent(cookie.trim()) });
+ setCookie("");
+ setIsAdding(false);
+ };
+
+ return (
+ <aside className="sidebar">
+ <div>
+ <div className="brand">
+ <div className="brand-glow" />
+ <div>
+ <p className="eyebrow">Project Starling</p>
+ <h1>Open TweetDeck</h1>
+ <p className="tagline">
+ Multi-account Twitter cockpit powered by Bun.
+ </p>
+ </div>
+ </div>
+
+ <section className="sidebar-section">
+ <header>
+ <p className="eyebrow">Accounts</p>
+ <button className="ghost" onClick={() => setIsAdding((v) => !v)}>
+ {isAdding ? "Close" : "Add"}
+ </button>
+ </header>
+ {!accounts.length && !isAdding && (
+ <p className="muted">
+ Add a Twitter session cookie to start streaming timelines. You can
+ rename the account later once data loads.
+ </p>
+ )}
+ {accounts.map((account) => (
+ <div
+ role="button"
+ tabIndex={0}
+ key={account.id}
+ className={`account-chip ${account.id === activeAccountId ? "active" : ""}`}
+ onClick={() => onActivate(account.id)}
+ onKeyDown={(event) => {
+ if (event.key === "Enter" || event.key === " ") {
+ event.preventDefault();
+ onActivate(account.id);
+ }
+ }}
+ >
+ <span
+ className="chip-accent"
+ style={{ background: account.accent }}
+ />
+ <span>
+ <strong>{account.label}</strong>
+ {account.handle ? <small>@{account.handle}</small> : null}
+ </span>
+ <span className="chip-actions">
+ <button
+ type="button"
+ className="ghost"
+ onClick={(event) => {
+ event.stopPropagation();
+ onRemoveAccount(account.id);
+ }}
+ aria-label={`Remove ${account.label}`}
+ >
+ ×
+ </button>
+ </span>
+ </div>
+ ))}
+ {isAdding && (
+ <form className="account-form" onSubmit={handleSubmit}>
+ <label>
+ Twitter session cookie
+ <textarea
+ className={!showCookie ? "masked" : undefined}
+ placeholder="Paste the entire Cookie header"
+ value={cookie}
+ onChange={(e) => setCookie(e.target.value)}
+ rows={4}
+ />
+ </label>
+ <label className="checkbox">
+ <input
+ type="checkbox"
+ checked={showCookie}
+ onChange={(e) => setShowCookie(e.target.checked)}
+ />
+ Reveal cookie contents
+ </label>
+ <small className="muted">
+ Cookie stays in your browser via localStorage. It is only sent
+ to your Bun server when fetching timelines.
+ </small>
+ <button
+ className="primary"
+ type="submit"
+ disabled={!cookie.trim()}
+ >
+ Save account
+ </button>
+ </form>
+ )}
+ </section>
+ </div>
+
+ <div className="sidebar-footer">
+ <button
+ className="primary wide"
+ onClick={onAddColumn}
+ disabled={!accounts.length}
+ >
+ + Add column
+ </button>
+ <p className="muted tiny">
+ Need a cookie? Open x.com, inspect network requests and copy the
+ request `Cookie` header. Keep it secret.
+ </p>
+ </div>
+ </aside>
+ );
+}
diff --git a/packages/tweetdeck/src/components/TimelineColumn.tsx b/packages/tweetdeck/src/components/TimelineColumn.tsx
new file mode 100644
index 0000000..534b2dd
--- /dev/null
+++ b/packages/tweetdeck/src/components/TimelineColumn.tsx
@@ -0,0 +1,500 @@
+import { useCallback, useEffect, useMemo, useState } from "react";
+import type {
+ ColumnSnapshot,
+ ColumnState,
+ ColumnView,
+ DeckAccount,
+ DeckColumn,
+ FullscreenState,
+ TimelineState,
+} from "../types/app";
+import type { Tweet } from "../lib/fetching/types";
+import { twitterClient } from "../lib/client/twitterClient";
+import { TweetCard } from "./TweetCard";
+
+export type TimelineConfig = DeckColumn & {
+ kind: Exclude<DeckColumn["kind"], "chat">;
+};
+
+type TimelineView = Extract<ColumnView, { type: "timeline" }>;
+
+interface TimelineColumnProps {
+ column: TimelineConfig;
+ account: DeckAccount;
+ onRemove: () => void;
+ onStateChange: (columnId: string, state: ColumnState) => void;
+ onSnapshot: (columnId: string, snapshot: ColumnSnapshot) => void;
+ onEnterFullscreen: (payload: FullscreenState) => void;
+ columnIndex: number;
+}
+
+export function TimelineColumn({
+ column,
+ account,
+ onRemove,
+ onStateChange,
+ onSnapshot,
+ onEnterFullscreen,
+ columnIndex,
+}: TimelineColumnProps) {
+ const [state, setState] = useState<TimelineState>({
+ tweets: [],
+ cursorTop: "",
+ cursorBottom: "",
+ isLoading: false,
+ isAppending: false,
+ });
+ const [error, setError] = useState<string | undefined>();
+ const [transitionDirection, setTransitionDirection] = useState<
+ "forward" | "backward" | null
+ >(null);
+
+ const baseView = useMemo(
+ () => createBaseView(column),
+ [column.kind, column.title, column.listId, column.listName],
+ );
+ const initialStack = useMemo<ColumnView[]>(() => {
+ return column.state?.stack?.length ? column.state.stack : [baseView];
+ }, [column.state, baseView]);
+
+ const [viewStack, setViewStack] = useState<ColumnView[]>(initialStack);
+
+ useEffect(() => {
+ setViewStack(initialStack);
+ }, [initialStack]);
+
+ const activeView = viewStack[viewStack.length - 1] ?? baseView;
+ const canGoBack = viewStack.length > 1;
+
+ const descriptor = useMemo(
+ () => describeView(column, activeView),
+ [column, activeView],
+ );
+
+ const handleMaximize = useCallback(() => {
+ onEnterFullscreen({
+ column: column,
+ columnLabel: descriptor.label,
+ accent: account.accent,
+ tweets: state.tweets,
+ index: 0,
+ columnIndex,
+ });
+ }, [
+ state.tweets,
+ onEnterFullscreen,
+ column.id,
+ descriptor.label,
+ account.accent,
+ columnIndex,
+ ]);
+
+ const handleAnimationEnd = useCallback(() => {
+ setTransitionDirection(null);
+ }, []);
+
+ const pushView = useCallback((view: ColumnView) => {
+ setTransitionDirection("forward");
+ setViewStack((prev) => [...prev, view]);
+ }, []);
+
+ const popView = useCallback(() => {
+ setViewStack((prev) => {
+ if (prev.length <= 1) return prev;
+ setTransitionDirection("backward");
+ return prev.slice(0, -1);
+ });
+ }, []);
+
+ useEffect(() => {
+ onStateChange(column.id, { stack: viewStack });
+ }, [column.id, onStateChange, viewStack]);
+
+ useEffect(() => {
+ onSnapshot(column.id, { tweets: state.tweets, label: descriptor.label });
+ }, [column.id, state.tweets, descriptor.label, onSnapshot]);
+
+ const fetchPage = useCallback(
+ async (cursor?: string) => {
+ const payload: Record<string, unknown> = { cookie: account.cookie };
+ //
+ let mode: string;
+
+ if (activeView.type === "thread") {
+ mode = "thread";
+ payload.tweetId = activeView.tweetId;
+ } else if (activeView.type === "user") {
+ mode = "user";
+ payload.userId = activeView.userId;
+ } else {
+ mode = activeView.mode;
+ if (activeView.mode === "list" && activeView.listId) {
+ payload.listId = activeView.listId;
+ }
+ }
+
+ if (cursor) payload.cursor = cursor;
+ return twitterClient.timeline(mode, payload);
+ },
+ [account.cookie, activeView],
+ );
+
+ const refresh = useCallback(async () => {
+ if (
+ activeView.type === "timeline" &&
+ activeView.mode === "list" &&
+ !activeView.listId
+ ) {
+ setError("Select a list for this column");
+ return;
+ }
+ setState((prev) => ({ ...prev, isLoading: true }));
+ setError(undefined);
+ try {
+ const data = await fetchPage(state.cursorTop);
+ setState({
+ tweets: data.tweets,
+ cursorTop: data.cursorTop,
+ cursorBottom: data.cursorBottom,
+ isLoading: false,
+ isAppending: false,
+ });
+ } catch (err) {
+ console.error(err);
+ setError(err instanceof Error ? err.message : "Failed to load timeline");
+ setState((prev) => ({ ...prev, isLoading: false, isAppending: false }));
+ }
+ }, [activeView, fetchPage]);
+
+ useEffect(() => {
+ setState({
+ tweets: [],
+ cursorTop: "",
+ cursorBottom: "",
+ isLoading: true,
+ isAppending: false,
+ });
+ }, [activeView]);
+
+ useEffect(() => {
+ refresh();
+ }, [refresh]);
+
+ const loadMore = useCallback(async () => {
+ if (!state.cursorBottom) return;
+ setState((prev) => ({ ...prev, isAppending: true }));
+ try {
+ const data = await fetchPage(state.cursorBottom);
+ setState((prev) => ({
+ tweets: [...prev.tweets, ...data.tweets],
+ cursorTop: prev.cursorTop || data.cursorTop,
+ cursorBottom: data.cursorBottom,
+ isLoading: false,
+ isAppending: false,
+ }));
+ } catch (err) {
+ console.error(err);
+ setError(err instanceof Error ? err.message : "Failed to load more");
+ setState((prev) => ({ ...prev, isAppending: false }));
+ }
+ }, [fetchPage, state.cursorBottom, state.cursorTop]);
+
+ const updateTweetById = useCallback(
+ (tweetId: string, updater: (tweet: Tweet) => Tweet) => {
+ setState((prev) => ({
+ ...prev,
+ tweets: prev.tweets.map((tweet) =>
+ tweet.id === tweetId ? updater(tweet) : tweet,
+ ),
+ }));
+ },
+ [],
+ );
+
+ const handleRemoveBookmark = useCallback(
+ async (tweetId: string) => {
+ try {
+ await twitterClient.removeBookmark({ cookie: account.cookie, tweetId });
+ setState((prev) => ({
+ ...prev,
+ tweets: prev.tweets.filter((tweet) => tweet.id !== tweetId),
+ }));
+ } catch (err) {
+ setError(
+ err instanceof Error
+ ? err.message
+ : "Unable to remove bookmark right now",
+ );
+ }
+ },
+ [account.cookie],
+ );
+
+ const likeTweet = useCallback(
+ async (tweetId: string, nextState: boolean) => {
+ try {
+ await twitterClient.like(tweetId, {
+ cookie: account.cookie,
+ undo: !nextState,
+ });
+ updateTweetById(tweetId, (tweet) => ({ ...tweet, liked: nextState }));
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Unable to update like");
+ }
+ },
+ [account.cookie, updateTweetById],
+ );
+
+ const retweet = useCallback(
+ async (tweetId: string, nextState: boolean) => {
+ try {
+ await twitterClient.retweet(tweetId, {
+ cookie: account.cookie,
+ undo: !nextState,
+ });
+ updateTweetById(tweetId, (tweet) => ({ ...tweet, rted: nextState }));
+ } catch (err) {
+ setError(
+ err instanceof Error ? err.message : "Unable to update retweet",
+ );
+ }
+ },
+ [account.cookie, updateTweetById],
+ );
+
+ const bookmarkTweet = useCallback(
+ async (tweetId: string, nextState: boolean) => {
+ try {
+ await twitterClient.bookmark(tweetId, {
+ cookie: account.cookie,
+ undo: !nextState,
+ });
+ if (column.kind === "bookmarks" && !nextState) {
+ setState((prev) => ({
+ ...prev,
+ tweets: prev.tweets.filter((tweet) => tweet.id !== tweetId),
+ }));
+ } else {
+ updateTweetById(tweetId, (tweet) => ({
+ ...tweet,
+ bookmarked: nextState,
+ }));
+ }
+ } catch (err) {
+ setError(
+ err instanceof Error ? err.message : "Unable to update bookmark",
+ );
+ }
+ },
+ [account.cookie, column.kind, updateTweetById],
+ );
+
+ const replyToTweet = useCallback(
+ (tweetId: string, text: string) =>
+ twitterClient.reply(tweetId, {
+ cookie: account.cookie,
+ text,
+ }),
+ [account.cookie],
+ );
+
+ const handleOpenAuthor = useCallback(
+ (author: Tweet["author"]) => {
+ if (!author?.id) return;
+ pushView({
+ type: "user",
+ userId: author.id,
+ username: author.username,
+ title: author.name || `@${author.username}`,
+ });
+ },
+ [pushView],
+ );
+
+ const handleOpenThread = useCallback(
+ (tweet: Tweet) => {
+ pushView({
+ type: "thread",
+ tweetId: tweet.id,
+ title: `Thread · ${tweet.author.name}`,
+ });
+ },
+ [pushView],
+ );
+
+ const breadcrumbs = useMemo(
+ () => viewStack.map((view) => labelForView(view)).join(" / "),
+ [viewStack],
+ );
+
+ return (
+ <article className="column">
+ <header>
+ <div>
+ <p className="eyebrow">{descriptor.badge}</p>
+ <h3>{descriptor.label}</h3>
+ <p className="muted tiny">
+ {descriptor.description} · {account.label}
+ </p>
+ {viewStack.length > 1 && <p className="muted tiny">{breadcrumbs}</p>}
+ </div>
+ <div className="column-actions">
+ <button
+ className="ghost"
+ onClick={handleMaximize}
+ aria-label="Maximize column"
+ >
+ ⤢
+ </button>
+ {canGoBack && (
+ <button className="ghost" onClick={popView} aria-label="Go back">
+ ←
+ </button>
+ )}
+ <button
+ className="ghost"
+ onClick={refresh}
+ aria-label="Refresh column"
+ >
+ ↻
+ </button>
+ <button
+ className="ghost"
+ onClick={onRemove}
+ aria-label="Remove column"
+ >
+ ×
+ </button>
+ </div>
+ </header>
+ <div
+ className={`column-content ${transitionDirection ? `slide-${transitionDirection}` : ""}`}
+ onAnimationEnd={handleAnimationEnd}
+ >
+ {error && <p className="error">{error}</p>}
+ {state.isLoading && !state.tweets.length ? (
+ <div className="column-loading">Loading…</div>
+ ) : (
+ <div className="tweet-stack">
+ {state.tweets
+ .filter((t) => t.language === "th")
+ .slice(0, 10)
+ .map((tweet) => (
+ <TweetCard
+ key={tweet.id}
+ tweet={tweet}
+ accent={account.accent}
+ allowBookmarkRemoval={column.kind === "bookmarks"}
+ onRemoveBookmark={handleRemoveBookmark}
+ onToggleLike={likeTweet}
+ onToggleRetweet={retweet}
+ onToggleBookmark={bookmarkTweet}
+ onReply={replyToTweet}
+ onOpenAuthor={handleOpenAuthor}
+ onOpenThread={handleOpenThread}
+ />
+ ))}
+ {!state.tweets.length && !state.isLoading && (
+ <p className="muted">No tweets yet. Try refreshing.</p>
+ )}
+ {state.cursorBottom ? (
+ <div className="load-more-row">
+ <button
+ className="ghost"
+ disabled={state.isAppending}
+ onClick={loadMore}
+ >
+ {state.isAppending ? "Loading…" : "Load more"}
+ </button>
+ </div>
+ ) : (
+ state.tweets.length > 0 && (
+ <div className="load-more-row">
+ <p className="muted">End of feed</p>
+ </div>
+ )
+ )}
+ </div>
+ )}
+ </div>
+ </article>
+ );
+}
+
+function createBaseView(column: TimelineConfig): TimelineView {
+ return {
+ type: "timeline",
+ mode: column.kind,
+ title: column.title || describeTimeline(column.kind).label,
+ listId: column.listId,
+ listName: column.listName,
+ };
+}
+
+function labelForView(view: ColumnView): string {
+ if (view.type === "timeline") {
+ return view.title || describeTimeline(view.mode).label;
+ }
+ if (view.type === "user") {
+ return view.title || `@${view.username}`;
+ }
+ return view.title || "Thread";
+}
+
+function describeView(column: TimelineConfig, view: ColumnView) {
+ if (view.type === "timeline") {
+ const base = describeTimeline(view.mode);
+ if (view.mode === "list" && view.listName) {
+ return {
+ ...base,
+ label: view.title || view.listName,
+ description: `Tweets from ${view.listName}`,
+ };
+ }
+ return {
+ ...base,
+ label: view.title || base.label,
+ };
+ }
+ if (view.type === "user") {
+ return {
+ label: view.title || `@${view.username}`,
+ badge: "Profile",
+ description: `Posts from @${view.username}`,
+ };
+ }
+ return {
+ label: view.title || "Thread",
+ badge: "Thread",
+ description: "Deep dive into the conversation",
+ };
+}
+
+function describeTimeline(kind: TimelineConfig["kind"]) {
+ switch (kind) {
+ case "following":
+ return {
+ label: "Following",
+ badge: "Chrono",
+ description: "Latest posts from people you follow",
+ };
+ case "bookmarks":
+ return {
+ label: "Bookmarks",
+ badge: "Library",
+ description: "Saved gems queued for later",
+ };
+ case "list":
+ return {
+ label: "List",
+ badge: "Curated",
+ description: "Tweets from a Twitter List",
+ };
+ default:
+ return {
+ label: "For You",
+ badge: "Ranked",
+ description: "AI-ranked home timeline",
+ };
+ }
+}
diff --git a/packages/tweetdeck/src/components/TweetCard.tsx b/packages/tweetdeck/src/components/TweetCard.tsx
new file mode 100644
index 0000000..7cd2936
--- /dev/null
+++ b/packages/tweetdeck/src/components/TweetCard.tsx
@@ -0,0 +1,337 @@
+import { useCallback, useState } from "react";
+import { Bookmark, Heart, Link2, MessageCircle, Repeat2 } from "lucide-react";
+import type { Tweet } from "../lib/fetching/types";
+import { timeAgo } from "../lib/utils/time";
+import { LangText } from "prosody-ui";
+
+interface TweetCardProps {
+ tweet: Tweet;
+ accent: string;
+ allowBookmarkRemoval?: boolean;
+ onRemoveBookmark?: (tweetId: string) => void;
+ onReply?: (tweetId: string, text: string) => Promise<unknown>;
+ onToggleRetweet?: (tweetId: string, next: boolean) => Promise<unknown>;
+ onToggleLike?: (tweetId: string, next: boolean) => Promise<unknown>;
+ onToggleBookmark?: (tweetId: string, next: boolean) => Promise<unknown>;
+ onOpenAuthor?: (author: Tweet["author"]) => void;
+ onOpenThread?: (tweet: Tweet) => void;
+ isQuote?: boolean;
+}
+
+type ActionKind = "reply" | "retweet" | "like" | "bookmark" | null;
+
+export function TweetCard(props: TweetCardProps) {
+ const {
+ tweet,
+ accent,
+ allowBookmarkRemoval,
+ onRemoveBookmark,
+ onOpenAuthor,
+ onOpenThread,
+ isQuote,
+ } = props;
+ const timestamp = timeAgo(tweet.time);
+
+ return (
+ <article
+ className="tweet-card"
+ lang={tweet.language}
+ style={{ borderColor: accent }}
+ >
+ {tweet.retweeted_by && (
+ <p
+ onClick={() => onOpenAuthor?.(tweet.retweeted_by!.author)}
+ className="muted tiny retweet-banner"
+ >
+ Retweeted by {tweet.retweeted_by.author.name}
+ <span>{timeAgo(tweet.retweeted_by.time)}</span>
+ </p>
+ )}
+ <header>
+ <div className="author">
+ <img
+ src={tweet.author.avatar}
+ alt={tweet.author.name}
+ loading="lazy"
+ />
+ <button
+ type="button"
+ className="link-button author-meta"
+ onClick={() => onOpenAuthor?.(tweet.author)}
+ title={`View @${tweet.author.username}`}
+ disabled={!onOpenAuthor}
+ >
+ <strong>{tweet.author.name}</strong>
+ <span className="muted">@{tweet.author.username}</span>
+ </button>
+ </div>
+ <div className="meta">
+ <button
+ type="button"
+ className="link-button muted"
+ onClick={() => onOpenThread?.(tweet)}
+ title="Open thread"
+ disabled={!onOpenThread}
+ >
+ {timestamp}
+ </button>
+ {allowBookmarkRemoval && onRemoveBookmark && (
+ <button
+ className="ghost"
+ onClick={() => onRemoveBookmark(tweet.id)}
+ >
+ Remove
+ </button>
+ )}
+ </div>
+ </header>
+ {isQuote && tweet.replyingTo.length > 0 && (
+ <div className="tweet-replying-to">
+ <span>replying to</span>
+ {tweet.replyingTo.map((rt) => (
+ <span key={rt.username}>@{rt.username}</span>
+ ))}
+ </div>
+ )}
+ <div className="tweet-body">
+ <div className="tweet-text">
+ <LangText lang={tweet.language} text={tweet.text} />
+ {/*renderText(tweet.text)}*/}
+ </div>
+ {!!tweet.media.pics.length && (
+ <div
+ className={`media-grid pics-${Math.min(tweet.media.pics.length, 4)}`}
+ >
+ {tweet.media.pics.map((pic) => (
+ <img key={pic} src={pic} alt="Tweet media" loading="lazy" />
+ ))}
+ </div>
+ )}
+ {tweet.media.video.url && (
+ <div className="video-wrapper">
+ <video controls preload="none" poster={tweet.media.video.thumb}>
+ <source src={tweet.media.video.url} />
+ </video>
+ </div>
+ )}
+ {!!tweet.urls.length && (
+ <div className="link-chips">
+ {tweet.urls.map((link) => (
+ <a
+ key={link.expandedUrl}
+ href={link.expandedUrl}
+ target="_blank"
+ rel="noreferrer"
+ >
+ {link.displayUrl}
+ </a>
+ ))}
+ </div>
+ // end body
+ )}
+ {tweet.quoting && <Quote tweet={tweet.quoting} />}
+ </div>
+ {!isQuote && <Actions {...props} />}
+ </article>
+ );
+}
+
+type Token = { type: "text" | "mention" | "hashtag" | "url"; value: string };
+
+function tokenize(text: string): Token[] {
+ const tokens: Token[] = [];
+ const regex = /(https?:\/\/\S+|@[A-Za-z0-9_]+|#[A-Za-z0-9_]+)/g;
+ let lastIndex = 0;
+ for (const match of text.matchAll(regex)) {
+ const value = match[0];
+ const index = match.index ?? 0;
+ if (index > lastIndex) {
+ tokens.push({ type: "text", value: text.slice(lastIndex, index) });
+ }
+ if (value.startsWith("http")) {
+ tokens.push({ type: "url", value });
+ } else if (value.startsWith("@")) {
+ tokens.push({ type: "mention", value });
+ } else if (value.startsWith("#")) {
+ tokens.push({ type: "hashtag", value });
+ }
+ lastIndex = index + value.length;
+ }
+ if (lastIndex < text.length) {
+ tokens.push({ type: "text", value: text.slice(lastIndex) });
+ }
+ return tokens;
+}
+
+function renderText(text: string) {
+ return tokenize(text).map((token, index) => {
+ if (token.type === "text") {
+ return token.value.split("\n").map((segment, segmentIndex, arr) => (
+ <span key={`${index}-${segmentIndex}`}>
+ {segment}
+ {segmentIndex < arr.length - 1 ? <br /> : null}
+ </span>
+ ));
+ }
+ if (token.type === "url") {
+ return (
+ <a key={index} href={token.value} target="_blank" rel="noreferrer">
+ {token.value}
+ </a>
+ );
+ }
+ if (token.type === "mention") {
+ return (
+ <span key={index} className="mention">
+ {token.value}
+ </span>
+ );
+ }
+ return (
+ <span key={index} className="hashtag">
+ {token.value}
+ </span>
+ );
+ });
+}
+
+function Actions(props: TweetCardProps) {
+ const { tweet, onReply, onToggleRetweet, onToggleLike, onToggleBookmark } =
+ props;
+
+ const tweetUrl = `https://x.com/${tweet.author.username}/status/${tweet.id}`;
+ const [copied, setCopied] = useState(false);
+ const [pendingAction, setPendingAction] = useState<ActionKind>(null);
+
+ const copyLink = useCallback(async () => {
+ try {
+ if (navigator?.clipboard?.writeText) {
+ await navigator.clipboard.writeText(tweetUrl);
+ } else if (typeof document !== "undefined") {
+ const textarea = document.createElement("textarea");
+ textarea.value = tweetUrl;
+ textarea.style.position = "fixed";
+ textarea.style.opacity = "0";
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand("copy");
+ document.body.removeChild(textarea);
+ }
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1800);
+ } catch (error) {
+ console.warn("Failed to copy tweet link", error);
+ }
+ }, [tweetUrl]);
+
+ const executeAction = useCallback(
+ async (kind: ActionKind, fn?: () => Promise<unknown>) => {
+ if (!fn) return;
+ setPendingAction(kind);
+ try {
+ await fn();
+ } catch (error) {
+ console.error(`Failed to perform ${kind ?? "action"}`, error);
+ } finally {
+ setPendingAction(null);
+ }
+ },
+ [],
+ );
+
+ const handleReply = useCallback(() => {
+ if (!onReply || typeof window === "undefined") return;
+ const prefill = `@${tweet.author.username} `;
+ const text = window.prompt("Reply", prefill);
+ if (!text || !text.trim()) return;
+ executeAction("reply", () => onReply(tweet.id, text.trim()));
+ }, [executeAction, onReply, tweet.author.username, tweet.id]);
+
+ const handleRetweet = useCallback(() => {
+ if (!onToggleRetweet) return;
+ const next = !tweet.rted;
+ executeAction("retweet", () => onToggleRetweet(tweet.id, next));
+ }, [executeAction, onToggleRetweet, tweet.id, tweet.rted]);
+
+ const handleLike = useCallback(() => {
+ if (!onToggleLike) return;
+ const next = !tweet.liked;
+ executeAction("like", () => onToggleLike(tweet.id, next));
+ }, [executeAction, onToggleLike, tweet.id, tweet.liked]);
+
+ const handleBookmark = useCallback(() => {
+ if (!onToggleBookmark) return;
+ const next = !tweet.bookmarked;
+ executeAction("bookmark", () => onToggleBookmark(tweet.id, next));
+ }, [executeAction, onToggleBookmark, tweet.bookmarked, tweet.id]);
+
+ return (
+ <footer className="tweet-actions">
+ <button
+ type="button"
+ className={`action ${pendingAction === "reply" ? "in-flight" : ""}`}
+ aria-label="Reply"
+ title="Reply"
+ disabled={pendingAction === "reply"}
+ onClick={handleReply}
+ >
+ <MessageCircle />
+ <span className="sr-only">Reply</span>
+ </button>
+ <button
+ type="button"
+ className={`action retweet ${tweet.rted ? "active" : ""} ${pendingAction === "retweet" ? "in-flight" : ""}`}
+ aria-label={tweet.rted ? "Undo Retweet" : "Retweet"}
+ aria-pressed={tweet.rted}
+ title={tweet.rted ? "Undo Retweet" : "Retweet"}
+ disabled={pendingAction === "retweet"}
+ onClick={handleRetweet}
+ >
+ <Repeat2 />
+ <span className="sr-only">Retweet</span>
+ </button>
+ <button
+ type="button"
+ className={`action like ${tweet.liked ? "active" : ""} ${pendingAction === "like" ? "in-flight" : ""}`}
+ aria-label={tweet.liked ? "Undo like" : "Like"}
+ aria-pressed={tweet.liked}
+ title={tweet.liked ? "Undo like" : "Like"}
+ disabled={pendingAction === "like"}
+ onClick={handleLike}
+ >
+ <Heart />
+ <span className="sr-only">Like</span>
+ </button>
+ <button
+ type="button"
+ className={`action bookmark ${tweet.bookmarked ? "active" : ""} ${pendingAction === "bookmark" ? "in-flight" : ""}`}
+ aria-label={tweet.bookmarked ? "Remove bookmark" : "Bookmark"}
+ aria-pressed={tweet.bookmarked}
+ title={tweet.bookmarked ? "Remove bookmark" : "Bookmark"}
+ disabled={pendingAction === "bookmark"}
+ onClick={handleBookmark}
+ >
+ <Bookmark />
+ <span className="sr-only">Bookmark</span>
+ </button>
+ <button
+ type="button"
+ className={`action ${copied ? "copied" : ""}`}
+ aria-label="Copy link"
+ title="Copy link"
+ onClick={copyLink}
+ >
+ <Link2 />
+ <span className="sr-only">Copy link</span>
+ </button>
+ </footer>
+ );
+}
+
+function Quote({ tweet }: { tweet: Tweet }) {
+ return (
+ <div className="tweet-quote">
+ <TweetCard tweet={tweet} accent="" isQuote={true} />
+ </div>
+ );
+}
diff --git a/packages/tweetdeck/src/frontend.tsx b/packages/tweetdeck/src/frontend.tsx
new file mode 100644
index 0000000..5691535
--- /dev/null
+++ b/packages/tweetdeck/src/frontend.tsx
@@ -0,0 +1,26 @@
+/**
+ * This file is the entry point for the React app, it sets up the root
+ * element and renders the App component to the DOM.
+ *
+ * It is included in `src/index.html`.
+ */
+
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { App } from "./App";
+
+const elem = document.getElementById("root")!;
+const app = (
+ // <StrictMode>
+ <App />
+ // </StrictMode>
+);
+
+if (import.meta.hot) {
+ // With hot module reloading, `import.meta.hot.data` is persisted.
+ const root = (import.meta.hot.data.root ??= createRoot(elem));
+ root.render(app);
+} else {
+ // The hot module reloading API is not available in production.
+ createRoot(elem).render(app);
+}
diff --git a/packages/tweetdeck/src/hooks/usePersistentState.ts b/packages/tweetdeck/src/hooks/usePersistentState.ts
new file mode 100644
index 0000000..7465f53
--- /dev/null
+++ b/packages/tweetdeck/src/hooks/usePersistentState.ts
@@ -0,0 +1,39 @@
+import { useEffect, useRef, useState } from "react";
+
+type Initializer<T> = T | (() => T);
+
+const isBrowser = typeof window !== "undefined";
+
+function readFromStorage<T>(key: string, fallback: Initializer<T>): T {
+ if (!isBrowser) {
+ return typeof fallback === "function" ? (fallback as () => T)() : fallback;
+ }
+ try {
+ const raw = window.localStorage.getItem(key);
+ if (raw) {
+ return JSON.parse(raw) as T;
+ }
+ } catch (error) {
+ console.warn("Failed to parse localStorage value", { key, error });
+ }
+ return typeof fallback === "function" ? (fallback as () => T)() : fallback;
+}
+
+export function usePersistentState<T>(key: string, initial: Initializer<T>) {
+ const initialRef = useRef<T | null>(null);
+ if (initialRef.current === null) {
+ initialRef.current = readFromStorage<T>(key, initial);
+ }
+ const [value, setValue] = useState<T>(() => initialRef.current as T);
+
+ useEffect(() => {
+ if (!isBrowser) return;
+ try {
+ window.localStorage.setItem(key, JSON.stringify(value));
+ } catch (error) {
+ console.warn("Failed to write localStorage value", { key, error });
+ }
+ }, [key, value]);
+
+ return [value, setValue] as const;
+}
diff --git a/packages/tweetdeck/src/index.html b/packages/tweetdeck/src/index.html
new file mode 100644
index 0000000..fa411d2
--- /dev/null
+++ b/packages/tweetdeck/src/index.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link rel="icon" type="image/svg+xml" href="./logo.svg" />
+ <title>Sordeck</title>
+</head>
+
+<body>
+ <div id="root"></div>
+ <script type="module" src="./frontend.tsx"></script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/packages/tweetdeck/src/index.ts b/packages/tweetdeck/src/index.ts
new file mode 100644
index 0000000..ccc86e7
--- /dev/null
+++ b/packages/tweetdeck/src/index.ts
@@ -0,0 +1,242 @@
+import { serve } from "bun";
+import index from "./index.html";
+import { TwitterApiService } from "./lib/fetching/twitter-api";
+
+const jsonResponse = (data: unknown, init?: ResponseInit) =>
+ Response.json(data, init);
+
+async function withTwitterService(
+ req: Request,
+ handler: (
+ service: TwitterApiService,
+ payload: Record<string, any>,
+ ) => Promise<Response>,
+) {
+ try {
+ const payload = await req.json();
+ const cookie = payload?.cookie;
+
+ if (!cookie || typeof cookie !== "string") {
+ return jsonResponse(
+ { error: "Missing twitter auth cookie" },
+ { status: 400 },
+ );
+ }
+
+ const service = new TwitterApiService(cookie);
+ return await handler(service, payload);
+ } catch (error) {
+ console.error("Twitter API route error", error);
+ return jsonResponse(
+ { error: error instanceof Error ? error.message : "Unknown error" },
+ { status: 500 },
+ );
+ }
+}
+
+const server = serve({
+ port: 3010,
+ routes: {
+ // Serve index.html for all unmatched routes.
+ "/*": index,
+
+ "/api/hello": {
+ async GET(req) {
+ return Response.json({
+ message: "Hello, world!",
+ method: "GET",
+ });
+ },
+ async PUT(req) {
+ return Response.json({
+ message: "Hello, world!",
+ method: "PUT",
+ });
+ },
+ },
+
+ "/api/hello/:name": async (req) => {
+ const name = req.params.name;
+ return Response.json({
+ message: `Hello, ${name}!`,
+ });
+ },
+
+ "/api/twitter/our": {
+ async POST(req) {
+ return withTwitterService(req, async (service, _payload) => {
+ return jsonResponse(await service.findOwn());
+ });
+ },
+ },
+ "/api/twitter/timeline/:mode": {
+ async POST(req) {
+ const { mode } = req.params;
+ console.log("fetching tweets", mode);
+ return withTwitterService(req, async (service, payload) => {
+ const cursor =
+ typeof payload.cursor === "string" ? payload.cursor : undefined;
+ switch (mode) {
+ case "foryou":
+ return jsonResponse(await service.fetchForyou(cursor));
+ case "following":
+ return jsonResponse(await service.fetchFollowing(cursor));
+ case "bookmarks":
+ return jsonResponse(await service.fetchBookmarks(cursor));
+ case "list":
+ if (!payload.listId) {
+ return jsonResponse(
+ { error: "Missing listId" },
+ { status: 400 },
+ );
+ }
+ return jsonResponse(
+ await service.fetchList(String(payload.listId), cursor),
+ );
+ case "user":
+ if (!payload.userId) {
+ return jsonResponse(
+ { error: "Missing userId" },
+ { status: 400 },
+ );
+ }
+ return jsonResponse(
+ await service.fetchUserTweets(String(payload.userId), cursor),
+ );
+ case "thread":
+ if (!payload.tweetId) {
+ return jsonResponse(
+ { error: "Missing tweetId" },
+ { status: 400 },
+ );
+ }
+ return jsonResponse(
+ await service.fetchThread(String(payload.tweetId), cursor),
+ );
+ default:
+ return jsonResponse(
+ { error: `Unknown timeline mode: ${mode}` },
+ { status: 400 },
+ );
+ }
+ });
+ },
+ },
+
+ "/api/twitter/lists": {
+ async POST(req) {
+ return withTwitterService(req, async (service) => {
+ return jsonResponse(await service.fetchLists());
+ });
+ },
+ },
+
+ "/api/twitter/notifications": {
+ async POST(req) {
+ return withTwitterService(req, async (service, payload) => {
+ const cursor =
+ typeof payload.cursor === "string" ? payload.cursor : undefined;
+ return jsonResponse(await service.fetchNotifications(cursor));
+ });
+ },
+ },
+
+ "/api/twitter/bookmarks/remove": {
+ async POST(req) {
+ return withTwitterService(req, async (service, payload) => {
+ const tweetId = payload?.tweetId;
+ if (!tweetId) {
+ return jsonResponse({ error: "Missing tweetId" }, { status: 400 });
+ }
+ await service.removeBookmark(String(tweetId));
+ return jsonResponse({ status: "ok" });
+ });
+ },
+ },
+
+ "/api/twitter/tweets/:tweetId/like": {
+ async POST(req) {
+ const { tweetId } = req.params;
+ return withTwitterService(req, async (service, payload) => {
+ if (!tweetId) {
+ return jsonResponse({ error: "Missing tweetId" }, { status: 400 });
+ }
+ const undo = Boolean(payload?.undo);
+ if (undo) {
+ await service.removeLike(tweetId);
+ } else {
+ await service.addLike(tweetId);
+ }
+ return jsonResponse({ status: "ok" });
+ });
+ },
+ },
+
+ "/api/twitter/tweets/:tweetId/retweet": {
+ async POST(req) {
+ const { tweetId } = req.params;
+ return withTwitterService(req, async (service, payload) => {
+ if (!tweetId) {
+ return jsonResponse({ error: "Missing tweetId" }, { status: 400 });
+ }
+ const undo = Boolean(payload?.undo);
+ if (undo) {
+ await service.removeRT(tweetId);
+ } else {
+ await service.addRT(tweetId);
+ }
+ return jsonResponse({ status: "ok" });
+ });
+ },
+ },
+
+ "/api/twitter/tweets/:tweetId/bookmark": {
+ async POST(req) {
+ const { tweetId } = req.params;
+ return withTwitterService(req, async (service, payload) => {
+ if (!tweetId) {
+ return jsonResponse({ error: "Missing tweetId" }, { status: 400 });
+ }
+ const undo = Boolean(payload?.undo);
+ if (undo) {
+ await service.removeBookmark(tweetId);
+ } else {
+ await service.addBookmark(tweetId);
+ }
+ return jsonResponse({ status: "ok" });
+ });
+ },
+ },
+
+ "/api/twitter/tweets/:tweetId/reply": {
+ async POST(req) {
+ const { tweetId } = req.params;
+ return withTwitterService(req, async (service, payload) => {
+ if (!tweetId) {
+ return jsonResponse({ error: "Missing tweetId" }, { status: 400 });
+ }
+ const text =
+ typeof payload?.text === "string" ? payload.text.trim() : "";
+ if (!text) {
+ return jsonResponse(
+ { error: "Missing reply text" },
+ { status: 400 },
+ );
+ }
+ await service.createTweet(text, { reply: tweetId });
+ return jsonResponse({ status: "ok" });
+ });
+ },
+ },
+ },
+
+ development: process.env.NODE_ENV !== "production" && {
+ // Enable browser hot reloading in development
+ hmr: true,
+
+ // Echo console logs from the browser to the server
+ console: true,
+ },
+});
+
+console.log(`🚀 Server running at ${server.url}`);
diff --git a/packages/tweetdeck/src/lib/client/twitterClient.ts b/packages/tweetdeck/src/lib/client/twitterClient.ts
new file mode 100644
index 0000000..b8914b5
--- /dev/null
+++ b/packages/tweetdeck/src/lib/client/twitterClient.ts
@@ -0,0 +1,75 @@
+import {
+ type TwitterUser,
+ type TweetList,
+ type TwitterList,
+ type TwitterNotification,
+} from "../fetching/types";
+
+const headers = { "Content-Type": "application/json" };
+
+async function postJson<T>(
+ url: string,
+ body: Record<string, unknown>,
+): Promise<T> {
+ const res = await fetch(url, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(body),
+ });
+
+ if (!res.ok) {
+ const text = await res.text();
+ throw new Error(text || `Request failed (${res.status})`);
+ }
+
+ return (await res.json()) as T;
+}
+
+export const twitterClient = {
+ own(payload: Record<string, unknown>) {
+ // return postJson<TwitterUser>(`/api/twitter/our`, payload);
+ },
+ timeline(mode: string, payload: Record<string, unknown>) {
+ console.log("fetching tweets", mode);
+ return postJson<TweetList>(`/api/twitter/timeline/${mode}`, payload);
+ },
+ lists(payload: Record<string, unknown>) {
+ return postJson<TwitterList[]>("/api/twitter/lists", payload);
+ },
+ notifications(payload: Record<string, unknown>) {
+ return postJson<TwitterNotification[]>(
+ "/api/twitter/notifications",
+ payload,
+ );
+ },
+ removeBookmark(payload: Record<string, unknown>) {
+ return postJson<{ status: string }>(
+ "/api/twitter/bookmarks/remove",
+ payload,
+ );
+ },
+ like(tweetId: string, payload: Record<string, unknown>) {
+ return postJson<{ status: string }>(
+ `/api/twitter/tweets/${tweetId}/like`,
+ payload,
+ );
+ },
+ retweet(tweetId: string, payload: Record<string, unknown>) {
+ return postJson<{ status: string }>(
+ `/api/twitter/tweets/${tweetId}/retweet`,
+ payload,
+ );
+ },
+ bookmark(tweetId: string, payload: Record<string, unknown>) {
+ return postJson<{ status: string }>(
+ `/api/twitter/tweets/${tweetId}/bookmark`,
+ payload,
+ );
+ },
+ reply(tweetId: string, payload: Record<string, unknown>) {
+ return postJson<{ status: string }>(
+ `/api/twitter/tweets/${tweetId}/reply`,
+ payload,
+ );
+ },
+};
diff --git a/packages/tweetdeck/src/lib/fetching/python.ts b/packages/tweetdeck/src/lib/fetching/python.ts
new file mode 100644
index 0000000..760cb2c
--- /dev/null
+++ b/packages/tweetdeck/src/lib/fetching/python.ts
@@ -0,0 +1,52 @@
+import python from "bun_python";
+
+export class TransactionIdGenerator {
+ private initialHtmlContent!: string;
+ private client_transaction: any;
+ private cookie: string;
+ private headers: any;
+
+ private BeautifulSoup: any;
+ private ClientTransaction: any;
+
+ constructor(cookie: string) {
+ this.cookie = cookie;
+ }
+ public async init() {
+ const genheaders = await python.import("x_client_transaction.utils")
+ .generate_headers;
+ const hs = genheaders();
+ this.headers = { ...hs, Cookie: this.cookie };
+ const currentUrl = "https://x.com";
+ const response = await fetch(currentUrl, { headers: this.headers });
+ const html = await response.text();
+ this.initialHtmlContent = html;
+ }
+
+ public async getTransactionId(method: string, path: string): Promise<string> {
+ if (!this.BeautifulSoup || !this.ClientTransaction) {
+ this.BeautifulSoup = await python.import("bs4").BeautifulSoup;
+ this.ClientTransaction = await python.import("x_client_transaction")
+ .ClientTransaction;
+ }
+
+ if (!this.client_transaction) {
+ const soup = this.BeautifulSoup(this.initialHtmlContent, "lxml");
+ const onDemand = await python.import("x_client_transaction.utils")
+ .get_ondemand_file_url;
+ const file = onDemand(soup);
+ const ondemand_res = await fetch(file, {
+ method: "GET",
+ headers: this.headers,
+ });
+ const ondemand_text = await ondemand_res.text();
+ this.client_transaction = this.ClientTransaction(soup, ondemand_text);
+ }
+
+ const transaction_id = this.client_transaction.generate_transaction_id(
+ method,
+ path,
+ );
+ return transaction_id;
+ }
+}
diff --git a/packages/tweetdeck/src/lib/fetching/twitter-api.ts b/packages/tweetdeck/src/lib/fetching/twitter-api.ts
new file mode 100644
index 0000000..8ea4709
--- /dev/null
+++ b/packages/tweetdeck/src/lib/fetching/twitter-api.ts
@@ -0,0 +1,1178 @@
+import type {
+ Tweet,
+ TweetList,
+ TweetResult,
+ TwitterBookmarkResponse,
+ TwitterList,
+ TwitterListTimelineResponse,
+ TwitterListsManagementResponse,
+ TwitterNotification,
+ TwitterNotificationsTimelineResponse,
+ TimelineEntry,
+ TwitterTimelineResponse,
+ TwitterTweetDetailResponse,
+ TwitterUserTweetsResponse,
+ APITwitterList,
+ TweetWithVisibilityResult,
+ TwitterUser,
+ RTMetadata,
+ TwitterProfilesResponse,
+ UserResult,
+} from "./types";
+import { TransactionIdGenerator } from "./python";
+
+const TWITTER_INTERNAL_API_KEY =
+ "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA";
+
+export class TwitterApiService {
+ cookie: string;
+ constructor(cookie: string) {
+ this.cookie = cookie;
+ }
+ // Read endpoints
+ private static readonly BOOKMARKS_URL = new URL(
+ "https://x.com/i/api/graphql/C7CReOA1R0PwKorWAxnNUQ/Bookmarks",
+ );
+ private static readonly FOLLOWING_URL = new URL(
+ "https://x.com/i/api/graphql/fhqL7Cgmvax9jOhRMOhWpA/HomeLatestTimeline",
+ );
+ private static readonly FORYOU_URL = new URL(
+ "https://x.com/i/api/graphql/sMNeM4wvNe4JnRUZ2jd2zw/HomeTimeline",
+ );
+ private static readonly LISTS_URL = new URL(
+ "https://x.com/i/api/graphql/wLXb5F6pIEOrYtTjXFLQsA/ListsManagementPageTimeline",
+ );
+ private static readonly LIST_URL = new URL(
+ "https://x.com/i/api/graphql/p-5fXSlJaR-aZ4UUBdPMAg/ListLatestTweetsTimeline",
+ );
+ private static readonly NOTIFICATIONS_URL = new URL(
+ "https://api.x.com/1.1/notifications/timeline.json",
+ );
+ private static readonly USERDATA_URL = new URL(
+ "https://x.com/i/api/graphql/2AtIgw7Kz26sV6sEBrQjSQ/UsersByRestIds",
+ );
+ private static readonly USER_URL = new URL(
+ "https://x.com/i/api/graphql/Le1DChzkS7ioJH_yEPMi3w/UserTweets",
+ );
+ private static readonly THREAD_URL = new URL(
+ "https://x.com/i/api/graphql/aTYmkYpjWyvUyrinVWSiYA/TweetDetail",
+ );
+
+ private static readonly HEADERS = {
+ "User-Agent":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
+ Accept: "*/*",
+ Referer: "https://x.com/i/bookmarks",
+ "Content-Type": "application/json",
+ "X-Twitter-Auth-Type": "OAuth2Session",
+ "X-Twitter-Active-User": "yes",
+ "X-Twitter-Client-Language": "en",
+ };
+
+ private get csrfToken() {
+ return this.cookie.match(/ct0=([^;]+)/)?.[1];
+ }
+
+ private buildHeaders(extra?: Record<string, string>) {
+ const headers: Record<string, string> = {
+ ...TwitterApiService.HEADERS,
+ Authorization: TWITTER_INTERNAL_API_KEY,
+ Cookie: this.cookie,
+ ...(extra ?? {}),
+ };
+ const csrf = this.csrfToken;
+ if (csrf) headers["X-Csrf-Token"] = csrf;
+ return headers;
+ }
+
+ private async request(url: URL, init: RequestInit) {
+ const headers = this.buildHeaders(init.headers as Record<string, string>);
+
+ const xcs = new TransactionIdGenerator("");
+ await xcs.init();
+ const xclientid = await xcs.getTransactionId(init.method!, url.pathname);
+ headers["X-Client-Transaction-Id"] = xclientid;
+ const response = await fetch(url, { ...init, headers });
+ if (!response.ok) {
+ console.log(headers);
+ console.log(response);
+ throw new Error(
+ `Twitter API request failed: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ return await response.json();
+ }
+ async postCall(url: URL, payload: Record<string, unknown>) {
+ const body = JSON.stringify(payload);
+ return this.request(url, {
+ method: "POST",
+ body,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ async getCall(url: URL) {
+ return this.request(url, { method: "GET" });
+ }
+ async findOwn(): Promise<TwitterUser> {
+ const cookie = decodeURIComponent(this.cookie);
+ const match = cookie.match(/twid=u=([^;]+)/);
+ const id = match![1]!;
+ const profs = await this.fetchProfiles([id]);
+ return profs[0]!;
+ }
+ async fetchProfiles(userIds: string[]): Promise<TwitterUser[]> {
+ const variables = {
+ userIds,
+ };
+ const features = {
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ responsive_web_profile_redirect_enabled: false,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ };
+ const url = new URL(TwitterApiService.USERDATA_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+
+ const data = (await this.getCall(url)) as TwitterProfilesResponse;
+ const users = data?.data?.users;
+ if (!users) throw new Error("error parsing ids");
+ const parsed = users.map((u) =>
+ TwitterApiService.extractUserData(u.result),
+ );
+ return parsed;
+ }
+ async fetchForyou(cursor?: string): Promise<TweetList> {
+ const payload = {
+ variables: {
+ count: 50,
+ cursor: cursor || "DAABCgABGv-ytnDAJxEKAAIa_qVgidrhcwgAAwAAAAEAAA",
+ includePromotedContent: true,
+ latestControlAvailable: true,
+ withCommunity: true,
+ seenTweetIds: [],
+ },
+ features: {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ },
+ queryId: "sMNeM4wvNe4JnRUZ2jd2zw",
+ };
+ const data = (await this.postCall(
+ TwitterApiService.FORYOU_URL,
+ payload,
+ )) as TwitterTimelineResponse;
+ try {
+ return TwitterApiService.parseTimelineResponse(data, "foryou");
+ } catch (e) {
+ console.error(e);
+ console.dir(data, { depth: null });
+ throw new Error("wtf");
+ }
+ }
+
+ async fetchFollowing(cursor?: string) {
+ const payload = {
+ variables: {
+ count: 50,
+ cursor: cursor || "DAABCgABGv-ytnDAJxEKAAIa_qVgidrhcwgAAwAAAAEAAA",
+ includePromotedContent: true,
+ latestControlAvailable: true,
+ requestContext: "launch",
+ seenTweetIds: [],
+ },
+ features: {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ },
+ queryId: "fhqL7Cgmvax9jOhRMOhWpA",
+ };
+ const data = (await this.postCall(
+ TwitterApiService.FOLLOWING_URL,
+ payload,
+ )) as TwitterTimelineResponse;
+ try {
+ return TwitterApiService.parseTimelineResponse(data, "following");
+ } catch (e) {
+ console.error(e);
+ console.dir(data, { depth: null });
+ throw new Error("wtf");
+ }
+ }
+
+ async fetchList(listId: string, cursor?: string): Promise<TweetList> {
+ const variables = { listId, count: 20, cursor };
+ const features = {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ const url = new URL(TwitterApiService.LIST_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+ const data = (await this.getCall(url)) as TwitterListTimelineResponse;
+ return TwitterApiService.parseListTimelineResponse(data);
+ }
+ async fetchLists(): Promise<TwitterList[]> {
+ const variables = { count: 100 };
+
+ const features = {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ const url = new URL(TwitterApiService.LISTS_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+ const data = (await this.getCall(url)) as TwitterListsManagementResponse;
+ try {
+ return TwitterApiService.parseListsManagementResponse(data);
+ } catch (e) {
+ console.error(e);
+ // console.dir(data.data.viewer.list_management_timeline, { depth: null });
+ throw e;
+ }
+ }
+
+ async fetchNotifications(cursor?: string): Promise<TwitterNotification[]> {
+ const variables: Record<string, string | number> = {
+ include_profile_interstitial_type: 1,
+ include_blocking: 1,
+ include_blocked_by: 1,
+ include_followed_by: 1,
+ include_want_retweets: 1,
+ include_mute_edge: 1,
+ include_can_dm: 1,
+ include_can_media_tag: 1,
+ include_ext_has_nft_avatar: 1,
+ include_ext_is_blue_verified: 1,
+ include_ext_verified_type: 1,
+ include_ext_profile_image_shape: 1,
+ skip_status: 1,
+ cards_platform: "Web-12",
+ include_cards: 1,
+ include_composer_source: "true",
+ include_ext_alt_text: "true",
+ include_ext_limited_action_results: "false",
+ include_reply_count: 1,
+ tweet_mode: "extended",
+ include_entities: "true",
+ include_user_entities: "true",
+ include_ext_media_color: "true",
+ include_ext_media_availability: "true",
+ include_ext_sensitive_media_warning: "true",
+ include_ext_trusted_friends_metadata: "true",
+ send_error_codes: "true",
+ simple_quoted_tweet: "true",
+ count: 40,
+ ext: "mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,enrichments,superFollowMetadata,unmentionInfo,editControl,vibe",
+ };
+ if (cursor) {
+ variables.cursor = cursor;
+ }
+ const url = new URL(TwitterApiService.NOTIFICATIONS_URL);
+ Object.keys(variables).forEach((key) =>
+ url.searchParams.set(key, variables[key]!.toString()),
+ );
+
+ const data = (await this.getCall(
+ url,
+ )) as TwitterNotificationsTimelineResponse;
+
+ return TwitterApiService.parseNotificationsResponse(data);
+ }
+ async fetchUserTweets(userId: string, cursor?: string): Promise<TweetList> {
+ const variables = {
+ userId,
+ count: 50,
+ includePromotedContent: true,
+ withQuickPromoteEligibilityTweetFields: true,
+ withVoice: true,
+ };
+ const features = {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ const fieldToggles = { withArticlePlainText: true };
+
+ const url = new URL(TwitterApiService.USER_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+ url.searchParams.set("fieldToggles", JSON.stringify(fieldToggles));
+ const data = (await this.getCall(url)) as TwitterUserTweetsResponse;
+
+ try {
+ return TwitterApiService.parseUserTweetsResponse(data);
+ } catch (e) {
+ console.error(e);
+ console.dir(data, { depth: null });
+ throw new Error("bad");
+ }
+ }
+ async fetchThread(tweetId: string, cursor?: string): Promise<TweetList> {
+ const variables = {
+ focalTweetId: tweetId,
+ referrer: "profile",
+ with_rux_injections: false,
+ rankingMode: "Relevance",
+ includePromotedContent: true,
+ withCommunity: true,
+ withQuickPromoteEligibilityTweetFields: true,
+ withBirdwatchNotes: true,
+ withVoice: true,
+ };
+ const features = {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ const fieldToggles = {
+ withArticleRichContentState: true,
+ withArticlePlainText: true,
+ withGrokAnalyze: true,
+ withDisallowedReplyControls: false,
+ };
+
+ const url = new URL(TwitterApiService.THREAD_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+ url.searchParams.set("fieldToggles", JSON.stringify(fieldToggles));
+ const data = (await this.getCall(url)) as TwitterTweetDetailResponse;
+
+ try {
+ return TwitterApiService.parseThreadResponse(data);
+ } catch (e) {
+ console.error(e);
+ console.dir(data, { depth: null });
+ throw new Error("Bad");
+ }
+ }
+ async fetchBookmarks(cursor?: string): Promise<TweetList> {
+ const variables = {
+ count: 40,
+ cursor: cursor || null,
+ includePromotedContent: true,
+ };
+
+ const features = {
+ rweb_video_screen_enabled: false,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ creator_subscriptions_tweet_preview_api_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ articles_preview_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ const url = new URL(TwitterApiService.BOOKMARKS_URL);
+ url.searchParams.set("variables", JSON.stringify(variables));
+ url.searchParams.set("features", JSON.stringify(features));
+ const data = (await this.getCall(url)) as TwitterBookmarkResponse;
+
+ return TwitterApiService.parseBookmarkResponse(data);
+ }
+
+ private static parseUserTweetsResponse(
+ response: TwitterUserTweetsResponse,
+ ): TweetList {
+ const tweets: Tweet[] = [];
+ let cursorBottom = "";
+ let cursorTop = "";
+ const instructions =
+ response.data?.user?.result?.timeline?.timeline?.instructions;
+ if (!instructions || !Array.isArray(instructions))
+ throw new Error("error parsing user feed");
+
+ for (const instruction of instructions) {
+ if (instruction.type === "TimelineAddEntries") {
+ if (!instruction.entries || !Array.isArray(instruction.entries))
+ throw new Error("error parsing user feed");
+ for (const entry of instruction.entries) {
+ if (entry.content?.entryType === "TimelineTimelineItem") {
+ const tweetData = entry.content.itemContent?.tweet_results?.result;
+ if (tweetData) {
+ const tweet = this.extractTweetData(tweetData);
+ if (tweet) {
+ tweets.push(tweet);
+ }
+ }
+ } else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Bottom"
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Top"
+ )
+ cursorTop = entry.content?.value || "";
+ }
+ }
+ }
+
+ return { tweets, cursorBottom, cursorTop };
+ }
+
+ private static parseThreadResponse(
+ response: TwitterTweetDetailResponse,
+ ): TweetList {
+ const tweets: Tweet[] = [];
+ let cursorBottom = "";
+ let cursorTop = "";
+
+ const instructions =
+ response.data?.threaded_conversation_with_injections_v2?.instructions;
+ if (!instructions || !Array.isArray(instructions))
+ throw new Error("error parsing thread ");
+
+ for (const instruction of instructions) {
+ if (instruction.type === "TimelineAddEntries") {
+ if (!instruction.entries || !Array.isArray(instruction.entries))
+ throw new Error("error parsing thread feed");
+ for (const entry of instruction.entries) {
+ if (entry.content?.entryType === "TimelineTimelineItem") {
+ const tweetData = entry.content.itemContent?.tweet_results?.result;
+ if (tweetData) {
+ const tweet = this.extractTweetData(tweetData);
+ if (tweet) {
+ tweets.push(tweet);
+ }
+ }
+ } else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Bottom"
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Top"
+ )
+ cursorTop = entry.content?.value || "";
+ }
+ }
+ }
+ return { tweets, cursorBottom, cursorTop };
+ }
+
+ private static parseBookmarkResponse(
+ response: TwitterBookmarkResponse,
+ ): TweetList {
+ const tweets: Tweet[] = [];
+ let cursorBottom = "";
+ let cursorTop = "";
+
+ const instructions =
+ response.data?.bookmark_timeline_v2?.timeline?.instructions || [];
+
+ for (const instruction of instructions) {
+ if (
+ instruction.type === "TimelineAddEntries" ||
+ instruction.type === "TimelineReplaceEntry" ||
+ instruction.type === "TimelineShowMoreEntries"
+ ) {
+ const entries = [
+ ...(instruction.entries || []),
+ ...(((instruction as { entry?: TimelineEntry }).entry
+ ? [(instruction as { entry?: TimelineEntry }).entry!]
+ : []) as TimelineEntry[]),
+ ];
+
+ for (const entry of entries) {
+ const content = entry.content;
+
+ if (content?.entryType === "TimelineTimelineItem") {
+ const tweetData = content.itemContent?.tweet_results?.result;
+ if (tweetData) {
+ const bookmark = this.extractTweetData(tweetData);
+ if (bookmark) {
+ tweets.push(bookmark);
+ }
+ } else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Bottom"
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Top"
+ )
+ cursorTop = entry.content?.value || "";
+ }
+ }
+ }
+ }
+
+ return {
+ tweets,
+ cursorTop,
+ cursorBottom,
+ };
+ }
+
+ private static parseTimelineResponse(
+ response: TwitterTimelineResponse,
+ type: "foryou" | "following",
+ ): TweetList {
+ const tweets: Tweet[] = [];
+ let cursorBottom = "";
+ let cursorTop = "";
+
+ const instructions = response.data?.home?.home_timeline_urt?.instructions;
+ response.data?.home_timeline_urt?.instructions;
+
+ if (!instructions || !Array.isArray(instructions))
+ throw new Error("error parsing thread ");
+
+ for (const instruction of instructions) {
+ if (instruction.type === "TimelineAddEntries") {
+ if (!instruction.entries || !Array.isArray(instruction.entries))
+ throw new Error("error parsing thread feed");
+
+ for (const entry of instruction.entries) {
+ // if (entry.content.entryType.includes("ursor")) console.log(entry);
+ // if (entry.content.entryType.includes("odule"))
+ // console.log("module", entry);
+
+ if (entry.content?.entryType === "TimelineTimelineItem") {
+ const tweetData = entry.content?.itemContent?.tweet_results?.result;
+ if (tweetData) {
+ try {
+ const tweet = this.extractTweetData(tweetData);
+ tweets.push(tweet);
+ } catch (e) {
+ console.error(e);
+ // console.dir(entry, { depth: null });
+ }
+ }
+ } else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Bottom"
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Gap" // TODO wtf???
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Top"
+ )
+ cursorTop = entry.content?.value || "";
+ }
+ }
+ }
+
+ return { tweets, cursorTop, cursorBottom };
+ }
+
+ private static parseListTimelineResponse(
+ response: TwitterListTimelineResponse,
+ ): TweetList {
+ const tweets: Tweet[] = [];
+ let cursorBottom = "";
+ let cursorTop = "";
+
+ const instructions =
+ response.data?.list?.tweets_timeline?.timeline?.instructions;
+ if (!instructions || !Array.isArray(instructions))
+ throw new Error("error parsing tw timeline res");
+
+ for (const instruction of instructions) {
+ if (instruction.type === "TimelineAddEntries") {
+ if (!instruction.entries || !Array.isArray(instruction.entries))
+ throw new Error("error parsing tw timeline res");
+ for (const entry of instruction.entries) {
+ if (entry.content?.entryType === "TimelineTimelineItem") {
+ const tweetData = entry.content.itemContent?.tweet_results?.result;
+ if (tweetData) {
+ const tweet = this.extractTweetData(tweetData);
+ tweets.push(tweet);
+ }
+ } else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Bottom"
+ )
+ cursorBottom = entry.content?.value || "";
+ else if (
+ entry.content?.entryType === "TimelineTimelineCursor" &&
+ entry.content?.cursorType === "Top"
+ )
+ cursorTop = entry.content?.value || "";
+ }
+ }
+ }
+
+ return { tweets, cursorBottom, cursorTop };
+ }
+
+ private static parseListsManagementResponse(
+ response: TwitterListsManagementResponse,
+ ): TwitterList[] {
+ const lists: TwitterList[] = [];
+ const instructions =
+ response.data?.viewer?.list_management_timeline?.timeline?.instructions;
+ if (!instructions || !Array.isArray(instructions))
+ throw new Error("error parsing tw lists res");
+ for (const instruction of instructions) {
+ if (instruction.type === "TimelineAddEntries") {
+ if (!instruction?.entries || !Array.isArray(instruction.entries))
+ throw new Error("error parsing tw lists res 2");
+ for (const entry of instruction.entries) {
+ console.log("entry", entry.content.__typename);
+ // if (entry.content.__typename === "TimelineTimelineModule")
+ if (entry.content.__typename === "TimelineTimelineCursor") {
+ console.dir(entry, { depth: null });
+ continue;
+ }
+ // entry.content.entryType can be TimelineTimelineModule, TimelineTimelineCursor,
+ // entry.entryId can be list-to-follow-<bignumber> which si the recommended lists that show on top
+ // or owned-subscribed-list-module-<smolnum> which is what we want
+ const listList = entry?.content?.items;
+ if (!listList || !Array.isArray(listList))
+ throw new Error("error parsing tw lists res 3");
+ for (const list of listList) {
+ lists.push(this.parseListResponse(list.item.itemContent.list));
+ }
+ }
+ }
+ }
+ return lists;
+ }
+
+ private static parseListResponse(res: APITwitterList): TwitterList {
+ const { name, id_str, member_count, subscriber_count } = res;
+ const creator = res.user_results.result.core.name;
+ return { name, id: id_str, member_count, subscriber_count, creator };
+ }
+ private static parseNotificationsResponse(
+ response: TwitterNotificationsTimelineResponse,
+ ): TwitterNotification[] {
+ const notifications: TwitterNotification[] = [];
+ const timelineNotifs =
+ response.timeline.instructions[0]?.addEntries?.entries;
+ if (!timelineNotifs || !Array.isArray(timelineNotifs))
+ throw new Error("error parsing notifs");
+ for (const entry of timelineNotifs) {
+ const notificationId = entry.content.notification.id;
+ const notification = response.globalObjects.notifications[notificationId];
+ if (notification) {
+ notifications.push(notification);
+ }
+ }
+ return notifications;
+ }
+ private static extractUserData(userResults: UserResult): TwitterUser {
+ return {
+ id: userResults.rest_id,
+ avatar:
+ userResults.avatar?.image_url ||
+ userResults.legacy?.profile_image_url_https!,
+ name: userResults.legacy?.name || userResults.core?.name!,
+ username:
+ userResults.legacy?.screen_name || userResults.core?.screen_name!,
+ };
+ }
+
+ private static extractTweetData(
+ tweetRes: TweetResult | TweetWithVisibilityResult,
+ rter: RTMetadata | null = null,
+ ): Tweet {
+ const tweetData =
+ tweetRes.__typename === "Tweet" ? tweetRes : tweetRes.tweet;
+
+ console.log({ tweetData });
+ let quoting: Tweet | null = null;
+ let retweeted_by = rter;
+ const legacy = tweetData?.legacy;
+ const userResults = tweetData?.core?.user_results?.result;
+ // if (!legacy || !userResults) throw new Error("no legacy??");
+ if (!legacy) throw new Error("no legacy??");
+ if (!userResults) throw new Error("no userResults??");
+
+ const author = this.extractUserData(userResults);
+ const time = new Date(legacy.created_at).getTime();
+
+ // is_rt
+ if (legacy.retweeted_status_result) {
+ const d = legacy.retweeted_status_result.result;
+ if (!d) console.log("bad rt", tweetData);
+ return this.extractTweetData(legacy.retweeted_status_result.result, {
+ author,
+ time,
+ });
+ }
+ //
+
+ // quotes
+ if (
+ tweetData.quoted_status_result &&
+ tweetData.quoted_status_result.result
+ ) {
+ // const d = tweetData.quoted_status_result.result;
+ // if (!d) console.log("bad quote", tweetData);
+ quoting = this.extractTweetData(tweetData.quoted_status_result.result);
+ }
+ //
+ const mediaEntities = legacy.entities.media;
+ // if (!mediaEntities) {
+ // console.log("no media");
+ // console.dir(legacy.entities, { depth: null });
+ // }
+ const media = (mediaEntities || []).reduce(
+ (
+ acc: { pics: string[]; video: { thumb: string; url: string } },
+ item,
+ ) => {
+ if (item.type === "photo" && item.media_url_https) {
+ return {
+ pics: [...acc.pics, item.media_url_https],
+ video: acc.video,
+ };
+ }
+ if (item.type === "video" && item.video_info?.variants) {
+ const video = item.video_info.variants.reduce(
+ (
+ acc: { bitrate?: number; url: string },
+ vid: { bitrate?: number; url: string },
+ ) => {
+ if (!vid.bitrate) return acc;
+ if (!acc.bitrate || vid.bitrate > acc.bitrate) return vid;
+ return acc;
+ },
+ { url: "" },
+ );
+ return {
+ pics: acc.pics,
+ video: {
+ url: video.url!,
+ thumb: item.media_url_https!,
+ },
+ };
+ }
+ return acc;
+ },
+ { pics: [] as string[], video: { thumb: "", url: "" } },
+ );
+ if (legacy.full_text.includes("your computers"))
+ console.dir(tweetRes, { depth: null });
+ const replyingTo = legacy.entities?.user_mentions
+ ? legacy.entities.user_mentions.map((m) => ({
+ name: m.name,
+ username: m.screen_name,
+ id: m.id_str,
+ }))
+ : [];
+
+ return {
+ id: tweetData.rest_id,
+ text: legacy.display_text_range
+ ? legacy.full_text.slice(
+ legacy.display_text_range[0],
+ legacy.display_text_range[1] + 1,
+ )
+ : legacy.full_text!,
+ language: legacy.lang || "en",
+ author,
+ time,
+ urls:
+ legacy.entities?.urls?.map(
+ (url: { expanded_url: string; display_url: string }) => ({
+ expandedUrl: url.expanded_url,
+ displayUrl: url.display_url,
+ }),
+ ) || [],
+ media,
+ hashtags:
+ legacy.entities?.hashtags?.map((tag: { text: string }) => tag.text) ||
+ [],
+ quoting,
+ retweeted_by,
+ liked: legacy.favorited,
+ bookmarked: legacy.bookmarked,
+ rted: legacy.retweeted,
+ replyingTo,
+ };
+ }
+
+ async fetchAllBookmarks(): Promise<Tweet[]> {
+ const allBookmarks: Tweet[] = [];
+ let cursor: string | undefined;
+ let hasMore = true;
+
+ while (hasMore) {
+ try {
+ const result = await this.fetchBookmarks(cursor);
+ allBookmarks.push(...result.tweets);
+ cursor = result.cursorBottom;
+
+ // Rate limiting - be nice to Twitter's API
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ } catch (error) {
+ console.error("Error fetching bookmarks batch:", error);
+ break;
+ }
+ }
+
+ return allBookmarks;
+ }
+ // WRITE ENDPOINTS
+ // TODO Grok stuff
+ //
+ // TODO add images, polls etc.
+ // quote is the URL https;//x.com/{user}/status/{id} of the quoted tweet;
+ async createTweet(text: string, topts: { quote?: string; reply?: string }) {
+ const queryId = `-fU2A9SG7hdlzUdOh04POw`;
+ const url = `https://x.com/i/api/graphql/${queryId}/createTweet`;
+ let variables: any = {
+ tweet_text: text,
+ dark_request: false,
+ media: {
+ media_entities: [],
+ possibly_sensitive: false,
+ },
+ semantic_annotation_ids: [],
+ disallowed_reply_options: null,
+ };
+ const features = {
+ premium_content_api_read_enabled: false,
+ communities_web_enable_tweet_community_results_fetch: true,
+ c9s_tweet_anatomy_moderator_badge_enabled: true,
+ responsive_web_grok_analyze_button_fetch_trends_enabled: false,
+ responsive_web_grok_analyze_post_followups_enabled: true,
+ responsive_web_jetfuel_frame: true,
+ responsive_web_grok_share_attachment_enabled: true,
+ responsive_web_edit_tweet_api_enabled: true,
+ graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+ view_counts_everywhere_api_enabled: true,
+ longform_notetweets_consumption_enabled: true,
+ responsive_web_twitter_article_tweet_consumption_enabled: true,
+ tweet_awards_web_tipping_enabled: false,
+ responsive_web_grok_show_grok_translated_post: false,
+ responsive_web_grok_analysis_button_from_backend: true,
+ creator_subscriptions_quote_tweet_preview_enabled: false,
+ longform_notetweets_rich_text_read_enabled: true,
+ longform_notetweets_inline_media_enabled: true,
+ payments_enabled: false,
+ profile_label_improvements_pcf_label_in_post_enabled: true,
+ responsive_web_profile_redirect_enabled: false,
+ rweb_tipjar_consumption_enabled: true,
+ verified_phone_label_enabled: false,
+ articles_preview_enabled: true,
+ responsive_web_grok_community_note_auto_translation_is_enabled: false,
+ responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+ freedom_of_speech_not_reach_fetch_enabled: true,
+ standardized_nudges_misinfo: true,
+ tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
+ responsive_web_grok_image_annotation_enabled: true,
+ responsive_web_grok_imagine_annotation_enabled: true,
+ responsive_web_graphql_timeline_navigation_enabled: true,
+ responsive_web_enhance_cards_enabled: false,
+ };
+ if (topts.reply)
+ variables = {
+ ...variables,
+ reply: {
+ in_reply_to_tweet_id: topts.reply,
+ exclude_reply_user_ids: [],
+ },
+ };
+ if (topts.quote) variables = { ...variables, attachment_url: topts.quote };
+
+ const params = { ...features, variables, queryId };
+ const body = JSON.stringify(params);
+ const nheaders = { "Content-type": "application/json" };
+ const headers = {
+ ...TwitterApiService.HEADERS,
+ Authorization: TWITTER_INTERNAL_API_KEY,
+ Cookie: this.cookie,
+ ...nheaders,
+ };
+ const opts = {
+ method: "POST",
+ headers,
+ body,
+ };
+ const res = await fetch(url, opts);
+ console.log("like added", res);
+ }
+ async addLike(tweet_id: string) {
+ const queryId = `lI07N6Otwv1PhnEgXILM7A`;
+ const url = new URL(`https://x.com/i/api/graphql/${queryId}/FavoriteTweet`);
+ const body = { variables: { tweet_id }, queryId };
+ return await this.postCall(url, body);
+ }
+ async removeLike(tweet_id: string) {
+ const queryId = `ZYKSe-w7KEslx3JhSIk5LA`;
+ const url = new URL(
+ `https://x.com/i/api/graphql/${queryId}/UnfavoriteTweet`,
+ );
+ const body = { variables: { tweet_id }, queryId };
+ return await this.postCall(url, body);
+ }
+ async addRT(tweet_id: string) {
+ const queryId = `ZYKSe-w7KEslx3JhSIk5LA`;
+ const url = new URL(`https://x.com/i/api/graphql/${queryId}/CreateRetweet`);
+ // TODO wtf is dark_request bruh
+ const body = {
+ variables: { tweet_id, dark_request: false },
+ queryId,
+ };
+ return await this.postCall(url, body);
+ }
+ async removeRT(tweet_id: string) {
+ const queryId = `iQtK4dl5hBmXewYZuEOKVw`;
+ const url = new URL(`https://x.com/i/api/graphql/${queryId}/DeleteRetweet`);
+ const body = {
+ variables: { tweet_id, dark_request: false },
+ queryId,
+ };
+ return await this.postCall(url, body);
+ }
+ async removeTweet(tweet_id: string) {
+ const queryId = `VaenaVgh5q5ih7kvyVjgtg`;
+ const url = new URL(`https://x.com/i/api/graphql/${queryId}/DeleteTweet`);
+ const body = {
+ variables: { tweet_id, dark_request: false },
+ queryId,
+ };
+ return await this.postCall(url, body);
+ }
+
+ async addBookmark(tweet_id: string) {
+ const queryId = `aoDbu3RHznuiSkQ9aNM67Q`;
+ const url = new URL(
+ `https://x.com/i/api/graphql/${queryId}/CreateBookmark`,
+ );
+ const body = { variables: { tweet_id }, queryId };
+ try {
+ const res = await this.postCall(url, body);
+ return res?.data?.tweet_bookmark_put === "Done";
+ } catch (e) {
+ console.log("wtf man", e);
+ // return this.removeBookmark(tweet_id);
+ }
+ }
+ async removeBookmark(tweet_id: string) {
+ const queryId = `Wlmlj2-xzyS1GN3a6cj-mQ`;
+ const url = new URL(
+ `https://x.com/i/api/graphql/${queryId}/DeleteBookmark`,
+ );
+ const body = { variables: { tweet_id }, queryId };
+ const res = await this.postCall(url, body);
+ return res?.data?.tweet_bookmark_delete === "Done";
+ }
+}
diff --git a/packages/tweetdeck/src/lib/fetching/types.ts b/packages/tweetdeck/src/lib/fetching/types.ts
new file mode 100644
index 0000000..deb5418
--- /dev/null
+++ b/packages/tweetdeck/src/lib/fetching/types.ts
@@ -0,0 +1,596 @@
+export type TweetList = {
+ tweets: Tweet[];
+ cursorTop: string;
+ cursorBottom: string;
+};
+export interface UserResult {
+ __typename: "User";
+ id: string; // hash
+ rest_id: string; // number
+ affiliates_highlighted_label: {};
+ avatar: {
+ image_url: string;
+ };
+ core: {
+ created_at: string; // date string
+ name: string;
+ screen_name: string;
+ };
+ dm_permissions: {
+ can_dm: boolean;
+ };
+ has_graduated_access: boolean;
+ is_blue_verified: boolean;
+ legacy: {
+ profile_image_url_https?: string;
+ name?: string;
+ screen_name?: string;
+ default_profile: boolean;
+ default_profile_image: boolean;
+ description: string;
+ entities: {
+ description: {
+ urls: APITwitterURLEntity[];
+ };
+ url: {
+ urls: APITwitterURLEntity[];
+ };
+ };
+ fast_followers_count: number;
+ favourites_count: number;
+ followers_count: number;
+ friends_count: number;
+ has_custom_timelines: boolean;
+ is_translator: boolean;
+ listed_count: number;
+ media_count: number;
+ needs_phone_verification: boolean;
+ normal_followers_count: number;
+ pinned_tweet_ids_str: string[];
+ possibly_sensitive: boolean;
+ profile_interstitial_type: string;
+ statuses_count: number;
+ translator_type: string; // "none"
+ url: string;
+ want_retweets: boolean;
+ withheld_in_countries: string[];
+ };
+ location: {
+ location: string;
+ };
+ media_permissions: {
+ can_media_tag: boolean;
+ };
+ parody_commentary_fan_label: string;
+ profile_image_shape: string;
+ privacy: {
+ protected: boolean;
+ };
+ relationship_perspectives: {
+ following: boolean;
+ };
+ tipjar_settings:
+ | {}
+ | {
+ is_enabled: true;
+ bitcoin_handle: string;
+ ethereum_handle: string;
+ patreon_handle: string;
+ }; // TODO
+ super_follow_eligible?: boolean;
+ verification: {
+ verified: boolean;
+ };
+ quick_promote_eligibility?: {
+ eligibility: "IneligibleNotProfessional"; // TODO
+ };
+}
+export interface TweetWithVisibilityResult {
+ __typename: "TweetWithVisibilityResults";
+ tweet: TweetResult;
+ limitedActionResults: {
+ limited_actions: Array<{
+ action: "Reply"; // and?
+ prompts: {
+ __typename: "CtaLimitedActionPrompt"; // ?
+ cta_type: "SeeConversation";
+ headline: { text: string; entities: [] };
+ subtext: { text: string; entities: [] };
+ };
+ }>;
+ };
+}
+export interface TweetResult {
+ __typename: "Tweet";
+ rest_id: string;
+ post_video_description?: string;
+ has_birdwatch_notes?: boolean;
+ unmention_data: {};
+ edit_control: {
+ edit_tweet_ids: string[];
+ editable_until_msecs: string;
+ is_edit_eligible: boolean;
+ edits_remaining: number;
+ };
+ is_translatable: boolean;
+ views: {
+ count: string;
+ state: "EnabledWithCount"; // TODO
+ };
+ source: string; // "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ grok_analysis_button: boolean;
+ quoted_status_result?: { result: TweetResult };
+ is_quote_status: boolean;
+ legacy: {
+ retweeted_status_result?: { result: TweetResult };
+ quoted_status_id_str?: string;
+ quoted_status_permalink?: {
+ uri: string;
+ expanded: string;
+ display: string;
+ };
+ id_str: string;
+ user_id_str: string;
+ bookmark_count: number;
+ bookmarked: boolean;
+ favorite_count: number;
+ favorited: boolean;
+ quote_count: number;
+ reply_count: number;
+ retweet_count: number;
+ retweeted: boolean;
+ conversation_control: {
+ policy: "ByInvitation"; // TODO
+ conversation_owner_results: {
+ result: {
+ __typename: "User";
+ core: {
+ screen_name: string;
+ };
+ };
+ };
+ };
+ conversation_id_str: string;
+ display_text_range?: [number, number];
+ full_text: string;
+ lang: string;
+ created_at: string;
+ possibly_sensitive: boolean;
+ possibly_sensitive_Editable: boolean;
+ entities: {
+ hashtags?: Array<{ text: string }>;
+ media?: APITwitterMediaEntity[];
+ symbols: string[];
+ timestamps: string[];
+ urls: APITwitterURLEntity[]; // TODO
+ user_mentions: Array<{
+ id_str: string;
+ name: string;
+ screen_name: string;
+ indices: [number, number];
+ }>;
+ };
+ extended_entities: {
+ media: APITwitterMediaExtendedEntity[];
+ };
+ limitedActionResults: {
+ limited_actions: Array<{
+ actions: "Reply"; // TODO;
+ prompts: {
+ cta_type: string;
+ headline: {
+ text: string;
+ entities: APITwitterMediaEntity[]; // ?
+ };
+ subtext: {
+ text: string;
+ entities: APITwitterMediaEntity[];
+ };
+ };
+ }>;
+ };
+ };
+ core: {
+ user_results?: {
+ result: UserResult;
+ };
+ };
+}
+interface APITwitterURLEntity {
+ display_url: string;
+ expanded_url: string;
+ url: string; // minified
+ indices: [number, number];
+}
+type APITwitterMediaEntity = APITwitterPhotoEntity | APITwitterVideoEntity;
+interface APITwitterMediaBase {
+ additional_media_info?: {
+ monetizable: boolean;
+ };
+ display_url: string;
+ expanded_url: string;
+ id_str: string;
+ indices: [number, number];
+ media_key: string;
+ media_url_https: string;
+ url: string; // minified
+ ext_media_availability: {
+ status: "Available" | "Unavailable"; // ?
+ };
+ features: {
+ large: {
+ faces: [];
+ };
+ medium: {
+ faces: [];
+ };
+ small: {
+ faces: [];
+ };
+ orig: {
+ faces: [];
+ };
+ };
+ sizes: {
+ large: {
+ h: number;
+ w: number;
+ resize: "fit" | "crop";
+ };
+ medium: {
+ h: number;
+ w: number;
+ resize: "fit" | "crop";
+ };
+ small: {
+ h: number;
+ w: number;
+ resize: "fit" | "crop";
+ };
+ thumb: {
+ h: number;
+ w: number;
+ resize: "fit" | "crop";
+ };
+ };
+ original_info: {
+ height: number;
+ width: number;
+ focus_rects: [
+ {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ ];
+ };
+ media_results: {
+ result: {
+ media_key: string;
+ };
+ };
+}
+interface APITwitterPhotoEntity extends APITwitterMediaBase {
+ type: "photo";
+}
+interface APITwitterVideoEntity extends APITwitterMediaBase {
+ type: "video";
+
+ video_info: {
+ aspect_ratio: [number, number];
+ duration_millis: number;
+ variants: Array<
+ | {
+ content_type: "application/x-mpegURL";
+ url: string;
+ }
+ | {
+ content_type: "video/mp4";
+ bitrate: number;
+ url: string;
+ }
+ >;
+ };
+}
+
+type APITwitterMediaExtendedEntity = APITwitterMediaEntity;
+
+export interface TimelineEntry {
+ entryId: string;
+ sortIndex: string;
+ content: {
+ entryType: string;
+ __typename: string;
+ itemContent?: {
+ itemType: string;
+ __typename: string;
+ tweet_results?: {
+ result?: TweetResult | TweetWithVisibilityResult;
+ };
+ user_results?: {
+ result?: {
+ __typename: string;
+ id: string;
+ rest_id: string;
+ legacy: {
+ name?: string;
+ screen_name?: string;
+ profile_image_url_https?: string;
+ };
+ };
+ };
+ };
+ cursorType?: string;
+ value?: string;
+ stopOnEmptyResponse?: boolean;
+ };
+}
+
+export interface TimelineInstruction {
+ type: string;
+ entries?: TimelineEntry[];
+}
+
+export interface TwitterProfilesResponse {
+ data: { users: Array<{ result: UserResult }> };
+}
+export interface TwitterUserTweetsResponse {
+ data: {
+ user: {
+ result: {
+ __typename: "User";
+ timeline: {
+ timeline: {
+ instructions: TimelineInstruction[];
+ };
+ };
+ };
+ };
+ };
+}
+export interface TwitterTweetDetailResponse {
+ data: {
+ threaded_conversation_with_injections_v2: {
+ instructions: TimelineInstruction[];
+ };
+ };
+}
+export interface TwitterBookmarkResponse {
+ data: {
+ bookmark_timeline_v2: {
+ timeline: {
+ instructions: TimelineInstruction[];
+ };
+ };
+ };
+}
+
+export interface TwitterTimelineResponse {
+ data: {
+ home?: {
+ home_timeline_urt: {
+ instructions: TimelineInstruction[];
+ };
+ };
+ home_timeline_urt?: {
+ instructions: TimelineInstruction[];
+ };
+ };
+}
+
+export interface TwitterListTimelineResponse {
+ data: {
+ list: {
+ tweets_timeline: {
+ timeline: {
+ instructions: TimelineInstruction[];
+ };
+ };
+ };
+ };
+}
+
+export interface TwitterList {
+ id: string;
+ name: string;
+ member_count: number;
+ subscriber_count: number;
+ creator: string;
+}
+export interface APITwitterMediaInfo {
+ original_img_url: string;
+ original_img_width: number;
+ original_img_height: number;
+ salient_rect: {
+ left: number;
+ top: number;
+ width: number;
+ height: number;
+ };
+}
+export interface APITwitterList {
+ created_at: number;
+ default_banner_media: {
+ media_info: APITwitterMediaInfo;
+ };
+ default_banner_media_results: {
+ result: {
+ id: string;
+ media_key: string;
+ media_id: string;
+ media_info: APITwitterMediaInfo;
+ __typename: "ApiMedia";
+ };
+ };
+ description: string;
+ facepile_urls: string[];
+ following: boolean;
+ id: string; //hash
+ id_str: string; // timestamp
+ is_member: boolean;
+ member_count: number;
+ members_context: string;
+ mode: string; // "private or public"
+ muting: boolean;
+ name: string;
+ pinning: boolean;
+ subscriber_count: number;
+ user_results: {
+ result: UserResult;
+ };
+}
+
+export interface TwitterListsManagementResponse {
+ data: {
+ viewer: {
+ list_management_timeline: {
+ timeline: {
+ instructions: Array<{
+ type: string;
+ entries: Array<{
+ content: {
+ __typename: string;
+ items: Array<{
+ entryId: string;
+ item: {
+ clientEventInfo: any;
+ itemContent: {
+ itemType: "TimelineTwitterList";
+ displayType: "ListWithPin"; // ?
+ list: APITwitterList;
+ };
+ };
+ }>;
+ };
+ }>;
+ }>;
+ };
+ };
+ };
+ };
+}
+
+export interface TwitterNotification {
+ id: string;
+ timestampMs: string;
+ message: {
+ text: string;
+ entities: Array<{
+ fromIndex: number;
+ toIndex: number;
+ ref: {
+ type: string;
+ screenName?: string;
+ mentionResults?: {
+ result?: {
+ legacy?: {
+ name?: string;
+ screen_name?: string;
+ };
+ };
+ };
+ };
+ }>;
+ rtl: boolean;
+ };
+ icon: {
+ id: string;
+ };
+ users: {
+ [key: string]: {
+ id: string;
+ screen_name: string;
+ name: string;
+ profile_image_url_https: string;
+ };
+ };
+}
+
+export interface TwitterNotificationsTimelineResponse {
+ globalObjects: {
+ notifications: { [id: string]: TwitterNotification };
+ users: {
+ [id: string]: {
+ id: string;
+ screen_name: string;
+ name: string;
+ profile_image_url_https: string;
+ };
+ };
+ tweets: { [id: string]: Tweet };
+ };
+ timeline: {
+ id: string;
+ instructions: Array<{
+ addEntries?: {
+ entries: Array<{
+ entryId: string;
+ sortIndex: string;
+ content: {
+ notification: {
+ id: string;
+ urls: Array<{
+ url: string;
+ expandedUrl: string;
+ displayUrl: string;
+ }>;
+ };
+ };
+ }>;
+ };
+ }>;
+ };
+}
+
+export type TwitterUser = {
+ id: string;
+ avatar: string;
+ name: string;
+ username: string;
+};
+export interface Tweet {
+ id: string;
+ text: string;
+ language: string;
+ author: TwitterUser;
+ time: number;
+ urls: Array<{
+ expandedUrl: string;
+ displayUrl: string;
+ }>;
+ media: { pics: string[]; video: { thumb: string; url: string } };
+ hashtags: string[];
+ quoting: Tweet | null;
+ liked: boolean;
+ bookmarked: boolean;
+ retweeted_by: RTMetadata | null;
+ rted: boolean;
+ replyingTo: Array<{ username: string }>;
+}
+export type RTMetadata = { author: TwitterUser; time: number };
+export type TwitterBookmark = Tweet;
diff --git a/packages/tweetdeck/src/lib/utils/id.ts b/packages/tweetdeck/src/lib/utils/id.ts
new file mode 100644
index 0000000..3008587
--- /dev/null
+++ b/packages/tweetdeck/src/lib/utils/id.ts
@@ -0,0 +1,4 @@
+export const generateId = () =>
+ typeof crypto !== "undefined" && "randomUUID" in crypto
+ ? crypto.randomUUID()
+ : Math.random().toString(36).slice(2, 11);
diff --git a/packages/tweetdeck/src/lib/utils/time.ts b/packages/tweetdeck/src/lib/utils/time.ts
new file mode 100644
index 0000000..f2802bf
--- /dev/null
+++ b/packages/tweetdeck/src/lib/utils/time.ts
@@ -0,0 +1,18 @@
+export function timeAgo(date: string | number | Date) {
+ const ts = typeof date === "string" || typeof date === "number" ? new Date(date).getTime() : date.getTime();
+ const diff = Date.now() - ts;
+ const seconds = Math.floor(diff / 1000);
+ if (seconds < 60) return `${seconds}s`;
+ const minutes = Math.floor(seconds / 60);
+ if (minutes < 60) return `${minutes}m`;
+ const hours = Math.floor(minutes / 60);
+ if (hours < 24) return `${hours}h`;
+ const days = Math.floor(hours / 24);
+ if (days < 7) return `${days}d`;
+ const weeks = Math.floor(days / 7);
+ if (weeks < 4) return `${weeks}w`;
+ const months = Math.floor(days / 30);
+ if (months < 12) return `${months}mo`;
+ const years = Math.floor(days / 365);
+ return `${years}y`;
+}
diff --git a/packages/tweetdeck/src/logo.svg b/packages/tweetdeck/src/logo.svg
new file mode 100644
index 0000000..7ef1500
--- /dev/null
+++ b/packages/tweetdeck/src/logo.svg
@@ -0,0 +1 @@
+<svg id="Bun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 70"><title>Bun Logo</title><path id="Shadow" d="M71.09,20.74c-.16-.17-.33-.34-.5-.5s-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5A26.46,26.46,0,0,1,75.5,35.7c0,16.57-16.82,30.05-37.5,30.05-11.58,0-21.94-4.23-28.83-10.86l.5.5.5.5.5.5.5.5.5.5.5.5.5.5C19.55,65.3,30.14,69.75,42,69.75c20.68,0,37.5-13.48,37.5-30C79.5,32.69,76.46,26,71.09,20.74Z"/><g id="Body"><path id="Background" d="M73,35.7c0,15.21-15.67,27.54-35,27.54S3,50.91,3,35.7C3,26.27,9,17.94,18.22,13S33.18,3,38,3s8.94,4.13,19.78,10C67,17.94,73,26.27,73,35.7Z" style="fill:#fbf0df"/><path id="Bottom_Shadow" data-name="Bottom Shadow" d="M73,35.7a21.67,21.67,0,0,0-.8-5.78c-2.73,33.3-43.35,34.9-59.32,24.94A40,40,0,0,0,38,63.24C57.3,63.24,73,50.89,73,35.7Z" style="fill:#f6dece"/><path id="Light_Shine" data-name="Light Shine" d="M24.53,11.17C29,8.49,34.94,3.46,40.78,3.45A9.29,9.29,0,0,0,38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7c0,.4,0,.8,0,1.19C9.06,15.48,20.07,13.85,24.53,11.17Z" style="fill:#fffefc"/><path id="Top" d="M35.12,5.53A16.41,16.41,0,0,1,29.49,18c-.28.25-.06.73.3.59,3.37-1.31,7.92-5.23,6-13.14C35.71,5,35.12,5.12,35.12,5.53Zm2.27,0A16.24,16.24,0,0,1,39,19c-.12.35.31.65.55.36C41.74,16.56,43.65,11,37.93,5,37.64,4.74,37.19,5.14,37.39,5.49Zm2.76-.17A16.42,16.42,0,0,1,47,17.12a.33.33,0,0,0,.65.11c.92-3.49.4-9.44-7.17-12.53C40.08,4.54,39.82,5.08,40.15,5.32ZM21.69,15.76a16.94,16.94,0,0,0,10.47-9c.18-.36.75-.22.66.18-1.73,8-7.52,9.67-11.12,9.45C21.32,16.4,21.33,15.87,21.69,15.76Z" style="fill:#ccbea7;fill-rule:evenodd"/><path id="Outline" d="M38,65.75C17.32,65.75.5,52.27.5,35.7c0-10,6.18-19.33,16.53-24.92,3-1.6,5.57-3.21,7.86-4.62,1.26-.78,2.45-1.51,3.6-2.19C32,1.89,35,.5,38,.5s5.62,1.2,8.9,3.14c1,.57,2,1.19,3.07,1.87,2.49,1.54,5.3,3.28,9,5.27C69.32,16.37,75.5,25.69,75.5,35.7,75.5,52.27,58.68,65.75,38,65.75ZM38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7,3,50.89,18.7,63.25,38,63.25S73,50.89,73,35.7C73,26.62,67.31,18.13,57.78,13,54,11,51.05,9.12,48.66,7.64c-1.09-.67-2.09-1.29-3-1.84C42.63,4,40.42,3,38,3Z"/></g><g id="Mouth"><g id="Background-2" data-name="Background"><path d="M45.05,43a8.93,8.93,0,0,1-2.92,4.71,6.81,6.81,0,0,1-4,1.88A6.84,6.84,0,0,1,34,47.71,8.93,8.93,0,0,1,31.12,43a.72.72,0,0,1,.8-.81H44.26A.72.72,0,0,1,45.05,43Z" style="fill:#b71422"/></g><g id="Tongue"><path id="Background-3" data-name="Background" d="M34,47.79a6.91,6.91,0,0,0,4.12,1.9,6.91,6.91,0,0,0,4.11-1.9,10.63,10.63,0,0,0,1-1.07,6.83,6.83,0,0,0-4.9-2.31,6.15,6.15,0,0,0-5,2.78C33.56,47.4,33.76,47.6,34,47.79Z" style="fill:#ff6164"/><path id="Outline-2" data-name="Outline" d="M34.16,47a5.36,5.36,0,0,1,4.19-2.08,6,6,0,0,1,4,1.69c.23-.25.45-.51.66-.77a7,7,0,0,0-4.71-1.93,6.36,6.36,0,0,0-4.89,2.36A9.53,9.53,0,0,0,34.16,47Z"/></g><path id="Outline-3" data-name="Outline" d="M38.09,50.19a7.42,7.42,0,0,1-4.45-2,9.52,9.52,0,0,1-3.11-5.05,1.2,1.2,0,0,1,.26-1,1.41,1.41,0,0,1,1.13-.51H44.26a1.44,1.44,0,0,1,1.13.51,1.19,1.19,0,0,1,.25,1h0a9.52,9.52,0,0,1-3.11,5.05A7.42,7.42,0,0,1,38.09,50.19Zm-6.17-7.4c-.16,0-.2.07-.21.09a8.29,8.29,0,0,0,2.73,4.37A6.23,6.23,0,0,0,38.09,49a6.28,6.28,0,0,0,3.65-1.73,8.3,8.3,0,0,0,2.72-4.37.21.21,0,0,0-.2-.09Z"/></g><g id="Face"><ellipse id="Right_Blush" data-name="Right Blush" cx="53.22" cy="40.18" rx="5.85" ry="3.44" style="fill:#febbd0"/><ellipse id="Left_Bluch" data-name="Left Bluch" cx="22.95" cy="40.18" rx="5.85" ry="3.44" style="fill:#febbd0"/><path id="Eyes" d="M25.7,38.8a5.51,5.51,0,1,0-5.5-5.51A5.51,5.51,0,0,0,25.7,38.8Zm24.77,0A5.51,5.51,0,1,0,45,33.29,5.5,5.5,0,0,0,50.47,38.8Z" style="fill-rule:evenodd"/><path id="Iris" d="M24,33.64a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,24,33.64Zm24.77,0a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,48.75,33.64Z" style="fill:#fff;fill-rule:evenodd"/></g></svg> \ No newline at end of file
diff --git a/packages/tweetdeck/src/react.svg b/packages/tweetdeck/src/react.svg
new file mode 100644
index 0000000..1ab815a
--- /dev/null
+++ b/packages/tweetdeck/src/react.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.23174 23 20.46348">
+ <circle cx="0" cy="0" r="2.05" fill="#61dafb"/>
+ <g stroke="#61dafb" stroke-width="1" fill="none">
+ <ellipse rx="11" ry="4.2"/>
+ <ellipse rx="11" ry="4.2" transform="rotate(60)"/>
+ <ellipse rx="11" ry="4.2" transform="rotate(120)"/>
+ </g>
+</svg>
diff --git a/packages/tweetdeck/src/styles/index.css b/packages/tweetdeck/src/styles/index.css
new file mode 100644
index 0000000..e9a500f
--- /dev/null
+++ b/packages/tweetdeck/src/styles/index.css
@@ -0,0 +1,835 @@
+@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&family=Inter:wght@400;500;600&display=swap");
+
+:root {
+ color-scheme: dark;
+ --bg: radial-gradient(circle at top, #15234b 0%, #050914 55%);
+ --panel: rgba(9, 14, 28, 0.9);
+ --panel-border: rgba(255, 255, 255, 0.08);
+ --soft-border: rgba(255, 255, 255, 0.15);
+ --muted: rgba(255, 255, 255, 0.6);
+ --accent: #7f5af0;
+ font-family: "Inter", "Space Grotesk", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
+ background-color: #050914;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ background: var(--bg);
+ color: #f5f6fb;
+ min-height: 100vh;
+}
+
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+}
+
+.app-shell {
+ min-height: 100vh;
+ display: grid;
+ grid-template-columns: 320px 1fr;
+ color: inherit;
+}
+
+.sidebar {
+ position: sticky;
+ top: 0;
+ align-self: start;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 2rem;
+ background: var(--panel);
+ border-right: 1px solid var(--panel-border);
+ gap: 2rem;
+}
+
+.brand {
+ display: flex;
+ gap: 1rem;
+ position: relative;
+ padding-bottom: 1.5rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.brand-glow {
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background: linear-gradient(120deg, #7f5af0, #2cb67d);
+ box-shadow: 0 0 24px #7f5af0;
+ margin-top: 6px;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ font-family: "Space Grotesk", "Inter", sans-serif;
+ margin: 0.2rem 0;
+}
+
+.eyebrow {
+ text-transform: uppercase;
+ letter-spacing: 0.2em;
+ font-size: 0.7rem;
+ color: var(--muted);
+ margin: 0;
+}
+
+.tagline {
+ margin: 0.2rem 0 0;
+ color: var(--muted);
+}
+
+.sidebar-section {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.sidebar-section header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.account-chip {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem 0.9rem;
+ border-radius: 12px;
+ border: 1px solid var(--panel-border);
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.account-chip.active {
+ border-color: currentColor;
+ background: rgba(127, 90, 240, 0.2);
+}
+
+.account-chip strong {
+ display: block;
+}
+
+.account-chip small {
+ color: var(--muted);
+}
+
+.chip-accent {
+ width: 6px;
+ height: 40px;
+ border-radius: 999px;
+}
+
+.chip-actions button {
+ border: none;
+ background: transparent;
+ color: var(--muted);
+ font-size: 1rem;
+}
+
+.account-form input,
+.account-form textarea,
+.account-form select {
+ width: 100%;
+ margin-top: 0.35rem;
+ padding: 0.65rem 0.75rem;
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ background: rgba(5, 9, 20, 0.7);
+ color: inherit;
+ resize: vertical;
+}
+
+.account-form textarea.masked {
+ filter: blur(6px);
+}
+
+.checkbox {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ font-size: 0.85rem;
+}
+
+.sidebar-footer {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.button-row,
+.sidebar-footer button,
+.account-form button,
+.modal button.primary,
+.primary {
+ border: none;
+ border-radius: 999px;
+ padding: 0.75rem 1.5rem;
+ font-weight: 600;
+ background: linear-gradient(120deg, #7f5af0, #2cb67d);
+ color: #050914;
+ cursor: pointer;
+}
+
+.primary:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.ghost {
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 999px;
+ padding: 0.35rem 0.85rem;
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+}
+
+button.ghost:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.muted {
+ color: var(--muted);
+ margin: 0;
+}
+
+.muted.tiny {
+ font-size: 0.8rem;
+}
+
+.sidebar-footer .tiny {
+ font-size: 0.7rem;
+}
+
+main {
+ padding: 2.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ overflow-x: auto;
+}
+
+.column-board {
+ display: flex;
+ gap: 1rem;
+ overflow-x: auto;
+ padding-bottom: 1rem;
+}
+
+.column {
+ flex: 0 0 360px;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ padding: 1.25rem;
+ border-radius: 18px;
+ border: 1px solid var(--panel-border);
+ background: rgba(8, 13, 26, 0.9);
+ max-height: calc(100vh - 120px);
+ width: 100%;
+}
+
+.column.missing {
+ justify-content: center;
+ text-align: center;
+}
+
+.column header {
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+}
+
+.column-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.column .tweet-stack,
+.column .chat-stack {
+ flex: 1;
+ overflow-y: auto;
+ padding-right: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+}
+
+.column .tweet-stack::-webkit-scrollbar,
+.column .chat-stack::-webkit-scrollbar {
+ display: none;
+}
+
+.load-more-row {
+ display: flex;
+ justify-content: center;
+ margin: 0.5rem 0 1rem;
+}
+
+.load-more-row button {
+ min-width: 140px;
+}
+
+.load-more-row p {
+ margin: 0;
+ text-align: center;
+}
+
+.fullscreen-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(3, 5, 12, 0.95);
+ z-index: 100;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2rem 3rem;
+}
+
+.fullscreen-content {
+ width: min(900px, 100%);
+ height: min(95vh, 100%);
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.fullscreen-card {
+ flex: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.fullscreen-card .tweet-card {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 70vh;
+ font-size: 1.1rem;
+
+ .tweet-body {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+
+ .media-grid {
+ flex-grow: 1;
+
+ }
+ }
+
+ footer {
+ button {
+ svg {
+ width: 3rem;
+ height: 3rem;
+ }
+ }
+ }
+}
+
+.fullscreen-card header {
+ font-size: 1.8rem;
+}
+
+.fullscreen-card .tweet-text {
+ font-size: 1.5rem;
+ line-height: 1.8;
+}
+
+.fullscreen-empty {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.75rem;
+ text-align: center;
+ font-size: 1.3rem;
+}
+
+.fullscreen-controls {
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+}
+
+.fullscreen-column-controls {
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+}
+
+.fullscreen-column-controls .ghost {
+ min-width: 180px;
+}
+
+.fullscreen-close {
+ position: absolute;
+ top: 1.5rem;
+ right: 1.5rem;
+ font-size: 1.5rem;
+}
+
+.column-content {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+}
+
+.column-content.slide-forward {
+ animation: columnForward 0.35s ease both;
+}
+
+.column-content.slide-backward {
+ animation: columnBackward 0.35s ease both;
+}
+
+@keyframes columnForward {
+ from {
+ opacity: 0;
+ transform: translateX(24px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes columnBackward {
+ from {
+ opacity: 0;
+ transform: translateX(-24px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+.column footer {
+ text-align: center;
+}
+
+.tweet-card,
+.chat-card {
+ border-radius: 18px;
+ padding: 1rem;
+ border: 1px solid var(--soft-border);
+ background: rgba(4, 8, 18, 0.8);
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.retweet-banner {
+ margin: 0;
+ text-transform: none;
+}
+
+.tweet-replying-to {
+ opacity: 0.5;
+
+ span {
+ margin: 0 0.25ch;
+ }
+}
+
+.tweet-actions {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
+ padding-top: 0.75rem;
+ margin-top: 0.25rem;
+}
+
+.tweet-actions .action {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.3rem;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 999px;
+ background: transparent;
+ color: inherit;
+ font-size: 0.8rem;
+ padding: 0.35rem 0.6rem;
+ cursor: pointer;
+ position: relative;
+ overflow: hidden;
+ transition: border-color 0.2s ease, background 0.2s ease, transform 0.15s ease;
+}
+
+.tweet-actions .action.active {
+ border-color: rgba(255, 255, 255, 0.35);
+ transform: scale(1.08);
+}
+
+.tweet-actions .action.like.active svg {
+ color: #f25f4c;
+}
+
+.tweet-actions .action.retweet.active svg {
+ color: #2cb67d;
+}
+
+.tweet-actions .action.bookmark.active svg {
+ color: #f0a500;
+}
+
+.tweet-actions .action::after {
+ content: "";
+ position: absolute;
+ inset: 50%;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: currentColor;
+ opacity: 0;
+ transform: translate(-50%, -50%) scale(1);
+ pointer-events: none;
+}
+
+.tweet-actions .action:active::after {
+ animation: ripple 0.45s ease-out;
+}
+
+@keyframes ripple {
+ 0% {
+ opacity: 0.25;
+ transform: translate(-50%, -50%) scale(0.2);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: translate(-50%, -50%) scale(8);
+ }
+}
+
+.tweet-actions .action.in-flight {
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+.tweet-actions .action.copied svg {
+ color: var(--accent);
+}
+
+.tweet-actions .action:hover {
+ border-color: rgba(255, 255, 255, 0.35);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+.tweet-actions svg {
+ width: 16px;
+ height: 16px;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.link-button {
+ border: none;
+ background: none;
+ color: inherit;
+ font: inherit;
+ padding: 0;
+ cursor: pointer;
+}
+
+.link-button:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+.link-button:disabled {
+ opacity: 0.6;
+ cursor: default;
+}
+
+.tweet-card header,
+.chat-card header {
+ display: flex;
+ justify-content: space-between;
+ gap: 0.5rem;
+}
+
+.tweet-card img {
+ border-radius: 12px;
+}
+
+.author {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.author img {
+ width: 44px;
+ height: 44px;
+ border-radius: 50%;
+}
+
+.author-meta {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ text-align: left;
+}
+
+.tweet-text {
+ white-space: pre-wrap;
+ line-height: 1.5;
+}
+
+.mention {
+ color: #2cb67d;
+}
+
+.hashtag {
+ color: #7f5af0;
+}
+
+.media-grid {
+ display: grid;
+ gap: 0.5rem;
+}
+
+.media-grid.pics-1 {
+ grid-template-columns: 1fr;
+}
+
+.media-grid.pics-2 {
+ grid-template-columns: repeat(2, 1fr);
+}
+
+.media-grid.pics-3,
+.media-grid.pics-4 {
+ grid-template-columns: repeat(2, 1fr);
+}
+
+.media-grid img {
+ width: 100%;
+}
+
+.video-wrapper video {
+ width: 100%;
+ border-radius: 14px;
+ background: #000;
+}
+
+.link-chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.link-chips a {
+ border: 1px solid var(--soft-border);
+ border-radius: 999px;
+ padding: 0.35rem 0.9rem;
+ text-decoration: none;
+ color: inherit;
+ font-size: 0.85rem;
+}
+
+.chat-card {
+ flex-direction: row;
+}
+
+.chat-avatar img,
+.chat-avatar span {
+ width: 42px;
+ height: 42px;
+ border-radius: 12px;
+ background: rgba(255, 255, 255, 0.08);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.chat-avatar img {
+ border-radius: 999px;
+}
+
+.chat-body header {
+ align-items: baseline;
+ gap: 0.4rem;
+}
+
+.chat-body p {
+ margin: 0.25rem 0 0;
+}
+
+.chat-body .dot {
+ color: var(--muted);
+}
+
+.modal-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.65);
+ display: grid;
+ place-items: center;
+ padding: 1rem;
+ z-index: 1000;
+}
+
+.modal {
+ width: min(520px, 100%);
+ background: rgba(5, 9, 20, 0.95);
+ border-radius: 24px;
+ border: 1px solid var(--panel-border);
+ padding: 1.75rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.modal header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.modal-body {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.modal select,
+.modal input {
+ border-radius: 12px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ background: rgba(8, 13, 26, 0.9);
+ color: inherit;
+ padding: 0.65rem 0.75rem;
+}
+
+.option-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+ gap: 0.75rem;
+}
+
+.option {
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 14px;
+ padding: 0.75rem;
+ text-align: left;
+ background: rgba(3, 6, 15, 0.9);
+ cursor: pointer;
+}
+
+.option.selected {
+ border-color: var(--accent);
+ background: rgba(127, 90, 240, 0.15);
+}
+
+.option p {
+ margin: 0.2rem 0 0;
+ color: var(--muted);
+}
+
+.error {
+ color: #f25f4c;
+}
+
+.column-loading {
+ text-align: center;
+ padding: 2rem 0;
+ color: var(--muted);
+}
+
+.empty-board {
+ border: 1px dashed rgba(255, 255, 255, 0.2);
+ border-radius: 24px;
+ padding: 2.5rem;
+ text-align: center;
+}
+
+.toast {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ padding: 0.85rem 1.25rem;
+ border-radius: 999px;
+ background: rgba(15, 25, 50, 0.9);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ animation: fadeOut 4s forwards;
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ 80% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ transform: translateY(12px);
+ }
+}
+
+@media (max-width: 1024px) {
+ .app-shell {
+ grid-template-columns: 1fr;
+ }
+
+ .sidebar {
+ position: relative;
+ min-height: unset;
+ }
+
+ main {
+ padding: 1.5rem;
+ }
+
+ .column {
+ flex-basis: 80vw;
+ }
+}
+
+
+/* language stuff */
+*[lang="th"],
+*[lang="tha"] {
+ font-size: 3rem;
+}
+
+/* .font-Thai-0 { */ \ No newline at end of file
diff --git a/packages/tweetdeck/src/styles/normalize.css b/packages/tweetdeck/src/styles/normalize.css
new file mode 100644
index 0000000..fdec4bd
--- /dev/null
+++ b/packages/tweetdeck/src/styles/normalize.css
@@ -0,0 +1,379 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+ ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+ line-height: 1.15;
+ /* 1 */
+ -webkit-text-size-adjust: 100%;
+ /* 2 */
+}
+
+/* Sections
+ ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+ margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+ display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+ box-sizing: content-box;
+ /* 1 */
+ height: 0;
+ /* 1 */
+ overflow: visible;
+ /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+ font-family: monospace, monospace;
+ /* 1 */
+ font-size: 1em;
+ /* 2 */
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+ background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+ border-bottom: none;
+ /* 1 */
+ text-decoration: underline;
+ /* 2 */
+ text-decoration: underline dotted;
+ /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+ font-family: monospace, monospace;
+ /* 1 */
+ font-size: 1em;
+ /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+ border-style: none;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ /* 1 */
+ font-size: 100%;
+ /* 1 */
+ line-height: 1.15;
+ /* 1 */
+ margin: 0;
+ /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input {
+ /* 1 */
+ overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select {
+ /* 1 */
+ text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+ padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ * `fieldset` elements in all browsers.
+ */
+
+legend {
+ box-sizing: border-box;
+ /* 1 */
+ color: inherit;
+ /* 2 */
+ display: table;
+ /* 1 */
+ max-width: 100%;
+ /* 1 */
+ padding: 0;
+ /* 3 */
+ white-space: normal;
+ /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+ vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+ box-sizing: border-box;
+ /* 1 */
+ padding: 0;
+ /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+ -webkit-appearance: textfield;
+ /* 1 */
+ outline-offset: -2px;
+ /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ /* 1 */
+ font: inherit;
+ /* 2 */
+}
+
+/* Interactive
+ ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+ display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+ display: list-item;
+}
+
+/* Misc
+ ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+ display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+ display: none;
+} \ No newline at end of file
diff --git a/packages/tweetdeck/src/types/app.ts b/packages/tweetdeck/src/types/app.ts
new file mode 100644
index 0000000..c8c9e80
--- /dev/null
+++ b/packages/tweetdeck/src/types/app.ts
@@ -0,0 +1,92 @@
+import type {
+ Tweet,
+ TwitterList,
+ TwitterNotification,
+} from "../lib/fetching/types";
+
+export type TimelineMode =
+ | "foryou"
+ | "following"
+ | "bookmarks"
+ | "list"
+ | "chat";
+
+export interface DeckAccount {
+ id: string;
+ label: string;
+ handle?: string;
+ username?: string;
+ avatar?: string;
+ accent: string;
+ cookie: string;
+ createdAt: number;
+}
+
+export type ColumnView =
+ | {
+ type: "timeline";
+ mode: Exclude<TimelineMode, "chat">;
+ title?: string;
+ listId?: string;
+ listName?: string;
+ }
+ | {
+ type: "user";
+ userId: string;
+ username: string;
+ title?: string;
+ }
+ | {
+ type: "thread";
+ tweetId: string;
+ title?: string;
+ };
+
+export interface ColumnState {
+ stack: ColumnView[];
+}
+
+export interface ColumnSnapshot {
+ tweets: Tweet[];
+ label: string;
+}
+
+export interface DeckColumn {
+ id: string;
+ kind: TimelineMode;
+ accountId: string; // wtf is this
+ account: string; // TODO ensure this gets populated
+ title: string;
+ listId?: string;
+ listName?: string;
+ state?: ColumnState;
+}
+
+export interface TimelineState {
+ tweets: Tweet[];
+ cursorTop: string;
+ cursorBottom: string;
+ isLoading: boolean;
+ isAppending: boolean;
+ error?: string;
+}
+
+export interface FullscreenState {
+ column: DeckColumn;
+ columnLabel: string;
+ accent: string;
+ tweets: Tweet[];
+ index: number;
+ columnIndex: number;
+}
+
+export interface ChatState {
+ entries: TwitterNotification[];
+ cursor?: string;
+ isLoading: boolean;
+ error?: string;
+}
+
+export interface DeckListsCache {
+ [accountId: string]: TwitterList[];
+}
diff --git a/packages/tweetdeck/test.sh b/packages/tweetdeck/test.sh
new file mode 100644
index 0000000..a208049
--- /dev/null
+++ b/packages/tweetdeck/test.sh
@@ -0,0 +1 @@
+bun --eval "import { createRequire } from 'module'; const r=createRequire('/home/y/code/bun/libs/prosody-ui/src/fonts/FontChanger.tsx'); console.log('react ->', r.resolve('react')); console.log('glotscript->', r.resolve('glotscript'));"
diff --git a/packages/tweetdeck/tests/js.js b/packages/tweetdeck/tests/js.js
new file mode 100644
index 0000000..947d9a7
--- /dev/null
+++ b/packages/tweetdeck/tests/js.js
@@ -0,0 +1,185 @@
+let solverIframe;
+let solveId = 0;
+let solveCallbacks = {};
+let solveQueue = []
+let solverReady = false;
+let solverErrored = false;
+let sentData = false;
+
+let sandboxUrl = fetch(chrome.runtime.getURL(`sandbox.html`))
+ .then(resp => resp.blob())
+ .then(blob => URL.createObjectURL(blob))
+ .catch(console.error);
+
+function createSolverFrame() {
+ if (solverIframe) solverIframe.remove();
+ solverIframe = document.createElement('iframe');
+ solverIframe.style.display = 'none';
+ sandboxUrl.then(url => solverIframe.src = url);
+ let injectedBody = document.getElementById('injected-body');
+ if(injectedBody) {
+ injectedBody.appendChild(solverIframe);
+ } else {
+ let int = setInterval(() => {
+ let injectedBody = document.getElementById('injected-body');
+ if(injectedBody) {
+ injectedBody.appendChild(solverIframe);
+ clearInterval(int);
+ }
+ }, 10);
+ }
+}
+createSolverFrame();
+
+function solveChallenge(path, method) {
+ return new Promise((resolve, reject) => {
+ if(solverErrored) {
+ reject('Solver errored during initialization');
+ return;
+ }
+ let id = solveId++;
+ solveCallbacks[id] = { resolve, reject, time: Date.now() };
+ if(!solverReady || !solverIframe || !solverIframe.contentWindow) {
+ solveQueue.push({ id, path, method })
+ } else {
+ try {
+ solverIframe.contentWindow.postMessage({ action: 'solve', id, path, method }, '*');
+ } catch(e) {
+ console.error(`Error sending challenge to solver:`, e);
+ reject(e);
+ }
+ // setTimeout(() => {
+ // if(solveCallbacks[id]) {
+ // solveCallbacks[id].reject('Solver timed out');
+ // delete solveCallbacks[id];
+ // }
+ // }, 1750);
+ }
+ });
+}
+
+setInterval(() => {
+ if(!document.getElementById('loading-box').hidden && sentData && solveQueue.length) {
+ console.log("Something's wrong with the challenge solver, reloading", solveQueue);
+ createSolverFrame();
+ initChallenge();
+ }
+}, 2000);
+
+window.addEventListener('message', e => {
+ if(e.source !== solverIframe.contentWindow) return;
+ let data = e.data;
+ if(data.action === 'solved' && typeof data.id === 'number') {
+ let { id, result } = data;
+ if(solveCallbacks[id]) {
+ solveCallbacks[id].resolve(result);
+ delete solveCallbacks[id];
+ }
+ } else if(data.action === 'error' && typeof data.id === 'number') {
+ let { id, error } = data;
+ if(solveCallbacks[id]) {
+ solveCallbacks[id].reject(error);
+ delete solveCallbacks[id];
+ }
+ } else if(data.action === 'initError') {
+ solverErrored = true;
+ for(let id in solveCallbacks) {
+ solveCallbacks[id].reject('Solver errored during initialization');
+ delete solveCallbacks[id];
+ }
+ alert(`There was an error in initializing security header generator:\n${data.error}\nUser Agent: ${navigator.userAgent}\nOldTwitter doesn't allow unsigned requests anymore for your account security.`);
+ console.error('Error initializing solver:');
+ console.error(data.error);
+ } else if(data.action === 'ready') {
+ solverReady = true;
+ for (let task of solveQueue) {
+ solverIframe.contentWindow.postMessage({ action: 'solve', id: task.id, path: task.path, method: task.method }, '*')
+ }
+ }
+});
+
+window._fetch = window.fetch;
+fetch = async function(url, options) {
+ if(!url.startsWith('/i/api') && !url.startsWith('https://api.twitter.com') && !url.startsWith('https://api.x.com')) return _fetch(url, options);
+ if(!options) options = {};
+ if(!options.headers) options.headers = {};
+ if(!options.headers['x-twitter-auth-type']) {
+ options.headers['x-twitter-auth-type'] = 'OAuth2Session';
+ }
+ if(!options.headers['x-twitter-active-user']) {
+ options.headers['x-twitter-active-user'] = 'yes';
+ }
+ if(!options.headers['X-Client-UUID']) {
+ options.headers['X-Client-UUID'] = OLDTWITTER_CONFIG.deviceId;
+ }
+ if(!url.startsWith('http:') && !url.startsWith('https:')) {
+ let host = location.hostname;
+ if(!['x.com', 'twitter.com'].includes(host)) host = 'x.com';
+ if(!url.startsWith('/')) url = '/' + url;
+ url = `https://${host}${url}`;
+ }
+ let parsedUrl = new URL(url);
+ // try {
+ let solved = await solveChallenge(parsedUrl.pathname, options.method ? options.method.toUpperCase() : 'GET');
+ console.log({solved})
+ options.headers['x-client-transaction-id'] = solved;
+ // } catch (e) {
+ // console.error(`Error solving challenge for ${url}:`);
+ // console.error(e);
+ // }
+ if(options.method && options.method.toUpperCase() === 'POST' && typeof options.body === 'string') {
+ options.headers['Content-Length'] = options.body.length;
+ }
+
+ return _fetch(url, options);
+}
+
+async function initChallenge() {
+ try {
+ let homepageData;
+ let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
+ let host = location.hostname;
+ if(!['x.com', 'twitter.com'].includes(host)) host = 'x.com';
+ try {
+ homepageData = await _fetch(`https://${host}/`).then(res => res.text());
+ } catch(e) {
+ await sleep(500);
+ try {
+ homepageData = await _fetch(`https://${host}/`).then(res => res.text());
+ } catch(e) {
+ throw new Error('Failed to fetch homepage: ' + e);
+ }
+ }
+ let dom = new DOMParser().parseFromString(homepageData, 'text/html');
+ let verificationKey = dom.querySelector('meta[name="twitter-site-verification"]').content;
+ let anims = Array.from(dom.querySelectorAll('svg[id^="loading-x"]')).map(svg => svg.outerHTML);
+
+ let challengeCode = homepageData.match(/"ondemand.s":"(\w+)"/)[1];
+
+ OLDTWITTER_CONFIG.verificationKey = verificationKey;
+
+ function sendInit() {
+ sentData = true;
+ if(!solverIframe || !solverIframe.contentWindow) return setTimeout(sendInit, 50);
+ solverIframe.contentWindow.postMessage({
+ action: 'init',
+ challengeCode,
+ anims,
+ verificationCode: OLDTWITTER_CONFIG.verificationKey
+ }, '*');
+ }
+ setTimeout(sendInit, 50);
+ return true;
+ } catch (e) {
+ console.error(`Error during challenge init:`);
+ console.error(e);
+ if(location.hostname === 'twitter.com') {
+ alert(`There was an error in initializing security header generator: ${e}\nUser Agent: ${navigator.userAgent}\nOldTwitter doesn't allow unsigned requests anymore for your account security. Currently the main reason for this happening is social network tracker protection blocking the script. Try disabling such settings in your browser and extensions that do that and refresh the page. This also might be because you're either not logged in or using twitter.com instead of x.com.`);
+ } else {
+ alert(`There was an error in initializing security header generator: ${e}\nUser Agent: ${navigator.userAgent}\nOldTwitter doesn't allow unsigned requests anymore for your account security. Currently the main reason for this happening is social network tracker protection blocking the script. Try disabling such settings in your browser and extensions that do that and refresh the page. This can also happen if you're not logged in.`);
+ }
+ return false;
+ }
+};
+
+initChallenge();
diff --git a/packages/tweetdeck/tests/python.ts b/packages/tweetdeck/tests/python.ts
new file mode 100644
index 0000000..73a174b
--- /dev/null
+++ b/packages/tweetdeck/tests/python.ts
@@ -0,0 +1,150 @@
+import { TransactionIdGenerator as TransactionIdGenerator } from "../src/lib/fetching/python";
+
+export const cookie = `auth_token=651d779cb99b54e7ce81bf8f8050e597c452b50b;gt=1868678280205029867;guest_id=v1%3A176150648682600359;__cf_bm=mfj12NkVbU0uPRz84om3BecRhAwv.8CCo.w0B4Gw45o-1761513085.2856057-1.0.1.1-gHPgX0w_GRneFL_GLZ5kPSXd7VCe4EuZnxN9FeKlpRg5PR5CCFrHazEjrE_EM53YxTbd1hDJoaWNXSU.Abr7FmBF5aCso.Jqn6M1ycwSXLhvQ3nTdZXRfiGpDrQugR6q;twid=u%3D1710606417324015616;g_state={"i_p":1733335699025,"i_l":1};external_referer=8e8t2xd8A2w%3D|0|F8C7rVpldvH1T6C9tW%2FBRRUSYlUEkKIshjiEn%2Br%2F70sqqRYvQQ%2FYUQ%3D%3D;auth_multi="1761426801275097088:f10fe04281e0bdf50e9dba703e2cba86adb84847";lang=en;night_mode=2;__cf_bm=koa1LKRskRZYwhHNG6YcIcSFN87JPc24Tb3umQqxxxU-1761507653-1.0.1.1-9OQutUp3hCAW8o5Sr8S0h.uE0eTWkxGRYiiESZu6WW9Q_Y_out9M6G9FqS5n9BVn1svlAUT97_FyLZosboQ45Q0M_HZ4F_rYtd3ojsAm6n8;__cuid=b4c86e91126647b5b7e07dadeac8ec2b;ct0=678e86afdb647f32835e25d1204c99af50b95d0e9ebcaf9db2d837fb9c4e48759a0e218c4845337f429c10e8d95f8a97cb9a7cee84c1bc5a7b522f440d5b6824466b6f4b62c328cc32d980a22fb00745;dnt=1;guest_id_ads=v1%3A176150648682600359;guest_id_marketing=v1%3A176150648682600359;kdt=camHTtNmw0pQUK0ZHaYd6jJnJRRYihbuZ0Ii7yN2;personalization_id="v1_KKOJ4dZoYTZuKLY9eadfIg=="`;
+
+async function initTransactionIdGenerator(): Promise<TransactionIdGenerator> {
+ const MAX_REDIRECTS = 5;
+ let currentUrl = "https://x.com";
+ let html = "";
+ const headers: Record<string, string> = {
+ "User-Agent":
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
+ "Accept-Language": "en-US,en;q=0.9",
+ Cookie: cookie,
+ };
+
+ try {
+ for (
+ let redirectCount = 0;
+ redirectCount < MAX_REDIRECTS;
+ redirectCount++
+ ) {
+ const response = await fetch(currentUrl, { headers });
+ headers["Cookie"] = cookie;
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed fetching ${currentUrl}: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ html = await response.text();
+
+ // Check for meta refresh redirect
+ const metaRefreshMatch = html.match(
+ /<meta\s+http-equiv=["']refresh["']\s+content=["'][^;]+;\s*url=([^"']+)/i,
+ );
+ const migrationRedirectionRegex =
+ /(http(?:s)?:\/\/(?:www\.)?(?:twitter|x)\.com(?:\/x)?\/migrate(?:[\/?])?tok=[a-zA-Z0-9%\-_]+)/i;
+ const bodyMatch = html.match(migrationRedirectionRegex);
+ let migrationUrl = metaRefreshMatch?.[1] || bodyMatch?.[0];
+
+ if (migrationUrl) {
+ currentUrl = migrationUrl;
+ continue;
+ }
+
+ // Handle migration form
+ const formMatch = html.match(
+ /<form[^>]*?(?:name=["']f["']|action=["']https:\/\/x\.com\/x\/migrate["'])[^>]*>([\s\S]*?)<\/form>/i,
+ );
+ if (formMatch) {
+ const formContent = formMatch[1];
+ const actionMatch = formMatch[0].match(/action=["']([^"']+)["']/i);
+ const methodMatch = formMatch[0].match(/method=["']([^"']+)["']/i);
+
+ const formAction = actionMatch?.[1] || "https://x.com/x/migrate";
+ const formMethod = methodMatch?.[1]?.toUpperCase() || "POST";
+ const formUrl = new URL(formAction, currentUrl);
+ if (!formUrl.searchParams.has("mx")) {
+ formUrl.searchParams.set("mx", "2");
+ }
+
+ const payload = new URLSearchParams();
+ const inputRegex =
+ /<input[^>]*?name=["']([^"']+)["'][^>]*?(?:value=["']([^"']*)["'])?[^>]*?>/gi;
+ let inputMatch;
+ while ((inputMatch = inputRegex.exec(formContent!)) !== null) {
+ payload.append(inputMatch[1]!, inputMatch[2] || "");
+ }
+
+ const formHeaders = {
+ ...headers,
+ "Content-Type": "application/x-www-form-urlencoded",
+ Referer: currentUrl,
+ };
+
+ const response = await fetch(formUrl.toString(), {
+ method: formMethod,
+ headers: formHeaders,
+ body: payload,
+ redirect: "manual",
+ });
+ headers["Cookie"] = cookie;
+
+ if (
+ response.status >= 300 &&
+ response.status < 400 &&
+ response.headers.has("location")
+ ) {
+ currentUrl = new URL(
+ response.headers.get("location")!,
+ currentUrl,
+ ).toString();
+ continue;
+ }
+
+ if (!response.ok) {
+ throw new Error(
+ `Migration form submission failed: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ html = await response.text();
+
+ const subsequentMetaRefresh = html.match(
+ /<meta\s+http-equiv=["']refresh["']\s+content=["'][^;]+;\s*url=([^"']+)/i,
+ );
+ const subsequentBodyMatch = html.match(migrationRedirectionRegex);
+
+ if (subsequentMetaRefresh?.[1] || subsequentBodyMatch?.[0]) {
+ currentUrl = subsequentMetaRefresh?.[1] || subsequentBodyMatch![0];
+ if (!currentUrl.startsWith("http")) {
+ currentUrl = new URL(currentUrl, response.url).toString();
+ }
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (!html) {
+ throw new Error("Failed to retrieve HTML after potential migrations.");
+ }
+
+ const transactionIdGenerator = new TransactionIdGenerator(html);
+ return transactionIdGenerator;
+ } catch (error: any) {
+ throw new Error(
+ `Transaction ID initialization failed: ${error.message || error}`,
+ );
+ }
+}
+
+async function generateTransactionId(
+ method: string,
+ path: string,
+): Promise<string> {
+ const gen = new TransactionIdGenerator("");
+ await gen.init();
+ // const gen = await initTransactionIdGenerator();
+
+ return gen.getTransactionId(method, path);
+}
+
+const res = await generateTransactionId(
+ "GET",
+ "i/api/graphql/2AtIgw7Kz26sV6sEBrQjSQ/UsersByRestIds",
+);
+console.log({ res });
diff --git a/packages/tweetdeck/tsconfig.json b/packages/tweetdeck/tsconfig.json
new file mode 100644
index 0000000..632a36f
--- /dev/null
+++ b/packages/tweetdeck/tsconfig.json
@@ -0,0 +1,36 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ },
+
+ "exclude": ["dist", "node_modules"]
+}
diff --git a/packages/tweetdeck/twatter-cookies.ts b/packages/tweetdeck/twatter-cookies.ts
new file mode 100644
index 0000000..f001a79
--- /dev/null
+++ b/packages/tweetdeck/twatter-cookies.ts
@@ -0,0 +1,4 @@
+export const first = `guest_id_marketing=v1:173815032724456303; guest_id_ads=v:173815032724456303; personalization_id="v1_KKOJ4dZoYTZuKLY9eadfIg=="; night_mode=2; kdt=camHTtNmw0pQUK0ZHaYd6jJnJRRYihbuZ0Ii7yN2; auth_token=f10fe04281e0bdf50e9dba703e2cba86adb84847; dnt=1; auth_multi="1710606417324015616:651d779cb99b54e7ce81bf8f8050e597c452b50b"; twid=u=1761426801275097088; ads_prefs="HBESAAA="; guest_id=v:173815032724456303; ct0=8e701469358130a26157f02b7836e3a17585153e07ff333ad6f79409f9f5db9cb544eee4c8c56e7b18670beb159e36ace3206471c8bab809602292d60975006c02331f898b8114dbaca1aa2a1c4b0719; cf_clearance=KzFqh7S1eP8KW3jTDxoqJSA7wHoUFaU8Pd.SnFK7vpI-1752517603-1.2.1.1-IJsXxsVSGoUIhnuYX.jp8Q7BI47TUmy90fjmkj2VV6fjCGREbY92GKSfXUzVTHpnZCIe2B5TYBBqQBFX4eeaCjFKXtXQNwG3HEO.qOxqFgdIvvOB03di0ZEHhhDoPGDZfny62hpWMTFZOBbh9bdr5RRA7I8eUdHHBt5thctu.bRV7._mev_T_6SE4nt_SGxpAQRW5NHzBJLfyoWG6W6SfYNl59LCYnlDIcSfcvGq6.U; lang=en; __cf_bm=_rT5xRA6wTXq8eU3g5GKsmTkRcis.Kio4eMiItRdf1o-1752577372-1.0.1.1-0GbYnb9cjm30LzBGKItZ2zXdUlOiGkKUR3A0hgzCX.t4xnOIdBqhNW4uGuTXmeGtBh7iCIWOKtbI1zwI09AhoTX3QKEHiMjxwLv9N4sE1SI`;
+export const second = `auth_token=fbfae81af3e0f4864b95ebd57bb64a2bd90c88f9;guest_id=v1%3A175666063354353443;__cf_bm=buNVVS7Cr7Kk3QGX4Gd7KI8jnwIWWh3k7l2bZkfHGnE-1761497629.346858-1.0.1.1-mNrW9iLykA6uImGT9OnSKHclCD71D_2qQSLpFxFT1FC5Z4Qn1I7VS1ji5pNfS6k4PhlYkK6BnHq0p5gU_BAUH7rW9Lb1U5NjRlVmy2m20ghy.xiv0_t4h4arbMWrkUSR;twid=u%3D1633158398555353096;external_referer=padhuUp37zgMUM44MkDWArGK1spFbbIK|0|8e8t2xd8A2w%3D;lang=en;__cuid=7d2dc54e7dc6494db2e0b5f6ba22fe42;ct0=c3df8a4a191d504bb88b5bc1c0364f29a3aec9779b8b3eb1bdac3d8c2db71ccc1be094abe5f6a3ac01dd90ec08d2198be4762bf9f9537007f22a1c93202e5a78d616ae725e6826df3e63e5d1c468bd6b;d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoyLHRleHRfdmVyc2lvbjoxMDAw;guest_id_ads=v1%3A175666063354353443;guest_id_marketing=v1%3A175666063354353443;kdt=Wb4iiFrzVHltVOgQLrHNSlrViq7Q8Zj6RGImz7dW;personalization_id="v1_kJCXAbmibH2kUGyOROCScg=="`;
+
+export const sa = `auth_token=651d779cb99b54e7ce81bf8f8050e597c452b50b;gt=1868678280205029867;guest_id=v1%3A176150648682600359;__cf_bm=mfj12NkVbU0uPRz84om3BecRhAwv.8CCo.w0B4Gw45o-1761513085.2856057-1.0.1.1-gHPgX0w_GRneFL_GLZ5kPSXd7VCe4EuZnxN9FeKlpRg5PR5CCFrHazEjrE_EM53YxTbd1hDJoaWNXSU.Abr7FmBF5aCso.Jqn6M1ycwSXLhvQ3nTdZXRfiGpDrQugR6q;twid=u%3D1710606417324015616;g_state={"i_p":1733335699025,"i_l":1};external_referer=8e8t2xd8A2w%3D|0|F8C7rVpldvH1T6C9tW%2FBRRUSYlUEkKIshjiEn%2Br%2F70sqqRYvQQ%2FYUQ%3D%3D;auth_multi="1761426801275097088:f10fe04281e0bdf50e9dba703e2cba86adb84847";lang=en;night_mode=2;__cf_bm=koa1LKRskRZYwhHNG6YcIcSFN87JPc24Tb3umQqxxxU-1761507653-1.0.1.1-9OQutUp3hCAW8o5Sr8S0h.uE0eTWkxGRYiiESZu6WW9Q_Y_out9M6G9FqS5n9BVn1svlAUT97_FyLZosboQ45Q0M_HZ4F_rYtd3ojsAm6n8;__cuid=b4c86e91126647b5b7e07dadeac8ec2b;ct0=678e86afdb647f32835e25d1204c99af50b95d0e9ebcaf9db2d837fb9c4e48759a0e218c4845337f429c10e8d95f8a97cb9a7cee84c1bc5a7b522f440d5b6824466b6f4b62c328cc32d980a22fb00745;dnt=1;guest_id_ads=v1%3A176150648682600359;guest_id_marketing=v1%3A176150648682600359;kdt=camHTtNmw0pQUK0ZHaYd6jJnJRRYihbuZ0Ii7yN2;personalization_id="v1_KKOJ4dZoYTZuKLY9eadfIg=="`;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}