Daryl Lim Claude Sonnet 4.6 commited on
Commit
bfb1fb7
·
1 Parent(s): 647408b

feat: expand language mapping to 418 languages with regions

Browse files

Run generate_langmap.py against the MADLAD-400 paper PDF to produce
langid_to_language with 418 entries (up from 22), each having "name"
and "region" keys across 9 geographic regions. Updated parse_table9()
to use word-based PDF extraction (pdfplumber extract_table() returns
None for this PDF) with column x-boundaries and 5px y-tolerance for
multi-line name cells. Added comprehensive LANGUAGE_OVERRIDES covering
all Latin-script languages plus Syrc to SCRIPT_TO_REGION.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. langmap/langid_mapping.py +424 -25
  2. scripts/generate_langmap.py +345 -46
langmap/langid_mapping.py CHANGED
@@ -1,27 +1,426 @@
1
- # Tier 1 production-ready languages: BLEU 35+ in both translation directions
2
- # (en→xx and xx→en) from Section A.10 of the MADLAD-400 paper.
3
- # See: https://arxiv.org/pdf/2309.04662
 
 
 
4
  langid_to_language = {
5
- "<2af>": "Afrikaans",
6
- "<2bg>": "Bulgarian",
7
- "<2bs>": "Bosnian",
8
- "<2ca>": "Catalan",
9
- "<2cs>": "Czech",
10
- "<2da>": "Danish",
11
- "<2de>": "German",
12
- "<2es>": "Spanish",
13
- "<2fil>": "Filipino",
14
- "<2fr>": "French",
15
- "<2hr>": "Croatian",
16
- "<2id>": "Indonesian",
17
- "<2it>": "Italian",
18
- "<2mk>": "Macedonian",
19
- "<2ms>": "Malay",
20
- "<2mt>": "Maltese",
21
- "<2nn>": "Norwegian Nynorsk",
22
- "<2no>": "Norwegian",
23
- "<2pt>": "Portuguese",
24
- "<2ro>": "Romanian",
25
- "<2sk>": "Slovak",
26
- "<2sv>": "Swedish",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
 
1
+ # Auto-generated by scripts/generate_langmap.py from MADLAD-400 paper Table 9 (Section A.1)
2
+ # Source: https://arxiv.org/pdf/2309.04662
3
+ # 418 languages with training data (excludes 79 self-audit omissions)
4
+ #
5
+ # To regenerate:
6
+ # python scripts/generate_langmap.py <path-to-paper.pdf>
7
  langid_to_language = {
8
+ "<2ach>": {"name": "Acoli", "region": "Africa"},
9
+ "<2ada>": {"name": "Adangme", "region": "Africa"},
10
+ "<2adh>": {"name": "Adhola", "region": "Africa"},
11
+ "<2aa>": {"name": "Afar", "region": "Africa"},
12
+ "<2af>": {"name": "Afrikaans", "region": "Africa"},
13
+ "<2alz>": {"name": "Alur", "region": "Africa"},
14
+ "<2am>": {"name": "Amharic", "region": "Africa"},
15
+ "<2bm>": {"name": "Bambara", "region": "Africa"},
16
+ "<2bci>": {"name": "Baoulé", "region": "Africa"},
17
+ "<2bas>": {"name": "Basa(Cameroon)", "region": "Africa"},
18
+ "<2bim>": {"name": "Bimoba", "region": "Africa"},
19
+ "<2bqc>": {"name": "Boko(Benin)", "region": "Africa"},
20
+ "<2bus>": {"name": "Bokobaru", "region": "Africa"},
21
+ "<2bum>": {"name": "Bulu", "region": "Africa"},
22
+ "<2ny>": {"name": "Chichewa", "region": "Africa"},
23
+ "<2cjk>": {"name": "Chokwe", "region": "Africa"},
24
+ "<2cce>": {"name": "Chopi", "region": "Africa"},
25
+ "<2dwr>": {"name": "Dawro", "region": "Africa"},
26
+ "<2din>": {"name": "Dinka", "region": "Africa"},
27
+ "<2tbz>": {"name": "Ditammari", "region": "Africa"},
28
+ "<2dov>": {"name": "Dombe", "region": "Africa"},
29
+ "<2dyu>": {"name": "Dyula", "region": "Africa"},
30
+ "<2ee>": {"name": "Ewe", "region": "Africa"},
31
+ "<2fip>": {"name": "Fipa", "region": "Africa"},
32
+ "<2fon>": {"name": "Fon", "region": "Africa"},
33
+ "<2ff>": {"name": "Fulfulde", "region": "Africa"},
34
+ "<2gof>": {"name": "Gofa", "region": "Africa"},
35
+ "<2gub>": {"name": "Guajajára", "region": "Africa"},
36
+ "<2gvl>": {"name": "Gulay", "region": "Africa"},
37
+ "<2ha>": {"name": "Hausa", "region": "Africa"},
38
+ "<2ibb>": {"name": "Ibibio", "region": "Africa"},
39
+ "<2ig>": {"name": "Igbo", "region": "Africa"},
40
+ "<2iso>": {"name": "Isoko", "region": "Africa"},
41
+ "<2izz>": {"name": "Izii", "region": "Africa"},
42
+ "<2kbp>": {"name": "Kabiyè", "region": "Africa"},
43
+ "<2kmb>": {"name": "Kimbundu", "region": "Africa"},
44
+ "<2rw>": {"name": "Kinyarwanda", "region": "Africa"},
45
+ "<2ktu>": {"name": "Kituba(DRC)", "region": "Africa"},
46
+ "<2kg>": {"name": "Kongo", "region": "Africa"},
47
+ "<2kri>": {"name": "Krio", "region": "Africa"},
48
+ "<2kj>": {"name": "Kuanyama", "region": "Africa"},
49
+ "<2laj>": {"name": "Lango(Uganda)", "region": "Africa"},
50
+ "<2ln>": {"name": "Lingala", "region": "Africa"},
51
+ "<2lu>": {"name": "Luba-Katanga", "region": "Africa"},
52
+ "<2lg>": {"name": "Luganda", "region": "Africa"},
53
+ "<2ffm>": {"name": "MaasinaFulfulde", "region": "Africa"},
54
+ "<2mgh>": {"name": "Makhuwa-Meetto", "region": "Africa"},
55
+ "<2mg>": {"name": "Malagasy", "region": "Africa"},
56
+ "<2mas>": {"name": "Masai", "region": "Africa"},
57
+ "<2mfe>": {"name": "Morisien", "region": "Africa"},
58
+ "<2nnb>": {"name": "Nande", "region": "Africa"},
59
+ "<2niq>": {"name": "Nandi", "region": "Africa"},
60
+ "<2nyu>": {"name": "Nyungwe", "region": "Africa"},
61
+ "<2nzi>": {"name": "Nzima", "region": "Africa"},
62
+ "<2ann>": {"name": "Obolo", "region": "Africa"},
63
+ "<2om>": {"name": "Oromo", "region": "Africa"},
64
+ "<2rn>": {"name": "Rundi", "region": "Africa"},
65
+ "<2sg>": {"name": "Sango", "region": "Africa"},
66
+ "<2seh>": {"name": "Sena", "region": "Africa"},
67
+ "<2nso>": {"name": "Sepedi", "region": "Africa"},
68
+ "<2srr>": {"name": "Serer", "region": "Africa"},
69
+ "<2st>": {"name": "Sesotho", "region": "Africa"},
70
+ "<2sn>": {"name": "Shona", "region": "Africa"},
71
+ "<2so>": {"name": "Somali", "region": "Africa"},
72
+ "<2nr>": {"name": "SouthNdebele", "region": "Africa"},
73
+ "<2spp>": {"name": "SupyireSenoufo", "region": "Africa"},
74
+ "<2sus>": {"name": "Susu", "region": "Africa"},
75
+ "<2sw>": {"name": "Swahili", "region": "Africa"},
76
+ "<2ss>": {"name": "Swati", "region": "Africa"},
77
+ "<2ber>": {"name": "Tamazight(Tfng)", "region": "Africa"},
78
+ "<2tdx>": {"name": "Tandroy-MahafalyMalagasy", "region": "Africa"},
79
+ "<2teo>": {"name": "Teso", "region": "Africa"},
80
+ "<2tll>": {"name": "Tetela", "region": "Africa"},
81
+ "<2ti>": {"name": "Tigrinya", "region": "Africa"},
82
+ "<2tiv>": {"name": "Tiv", "region": "Africa"},
83
+ "<2ts>": {"name": "Tsonga", "region": "Africa"},
84
+ "<2tsc>": {"name": "Tswa", "region": "Africa"},
85
+ "<2tn>": {"name": "Tswana", "region": "Africa"},
86
+ "<2ak>": {"name": "Twi", "region": "Africa"},
87
+ "<2ve>": {"name": "Venda", "region": "Africa"},
88
+ "<2wal>": {"name": "Wolaytta", "region": "Africa"},
89
+ "<2wo>": {"name": "Wolof", "region": "Africa"},
90
+ "<2xh>": {"name": "Xhosa", "region": "Africa"},
91
+ "<2yo>": {"name": "Yoruba", "region": "Africa"},
92
+ "<2zne>": {"name": "Zande", "region": "Africa"},
93
+ "<2dje>": {"name": "Zarma", "region": "Africa"},
94
+ "<2zu>": {"name": "Zulu", "region": "Africa"},
95
+ "<2agr>": {"name": "Aguaruna", "region": "Americas"},
96
+ "<2cni>": {"name": "Asháninka", "region": "Americas"},
97
+ "<2kwi>": {"name": "Awa-Cuaiquer", "region": "Americas"},
98
+ "<2quy>": {"name": "AyacuchoQuechua", "region": "Americas"},
99
+ "<2ay>": {"name": "Aymara", "region": "Americas"},
100
+ "<2bzj>": {"name": "BelizeKriolEnglish", "region": "Americas"},
101
+ "<2qvc>": {"name": "CajamarcaQuechua", "region": "Americas"},
102
+ "<2qxr>": {"name": "CañarH.Quichua", "region": "Americas"},
103
+ "<2maz>": {"name": "CentralMazahua", "region": "Americas"},
104
+ "<2chr>": {"name": "Cherokee", "region": "Americas"},
105
+ "<2ctu>": {"name": "Chol", "region": "Americas"},
106
+ "<2cac>": {"name": "Chuj", "region": "Americas"},
107
+ "<2gui>": {"name": "E.BolivianGuaraní", "region": "Americas"},
108
+ "<2nhe>": {"name": "E.HuastecaNahuatl", "region": "Americas"},
109
+ "<2djk>": {"name": "E.MaroonCreole", "region": "Americas"},
110
+ "<2sja>": {"name": "Epena", "region": "Americas"},
111
+ "<2cab>": {"name": "Garifuna", "region": "Americas"},
112
+ "<2guh>": {"name": "Guahibo", "region": "Americas"},
113
+ "<2gn>": {"name": "Guarani", "region": "Americas"},
114
+ "<2amu>": {"name": "GuerreroAmuzgo", "region": "Americas"},
115
+ "<2ngu>": {"name": "GuerreroNahuatl", "region": "Americas"},
116
+ "<2gyn>": {"name": "GuyaneseCreoleEnglish", "region": "Americas"},
117
+ "<2ht>": {"name": "HaitianCreole", "region": "Americas"},
118
+ "<2haw>": {"name": "Hawaiian", "region": "Americas"},
119
+ "<2qub>": {"name": "HuallagaHuánucoQuechua", "region": "Americas"},
120
+ "<2hus>": {"name": "Huastec", "region": "Americas"},
121
+ "<2qvi>": {"name": "ImbaburaH.Quichua", "region": "Americas"},
122
+ "<2inb>": {"name": "Inga", "region": "Americas"},
123
+ "<2iu>": {"name": "Inuktitut", "region": "Americas"},
124
+ "<2jam>": {"name": "JamaicanCreoleEnglish", "region": "Americas"},
125
+ "<2kl>": {"name": "Kalaallisut", "region": "Americas"},
126
+ "<2cak>": {"name": "Kaqchikel", "region": "Americas"},
127
+ "<2kek>": {"name": "Kekchí", "region": "Americas"},
128
+ "<2tlh>": {"name": "Klingon", "region": "Americas"},
129
+ "<2quc>": {"name": "K’iche’", "region": "Americas"},
130
+ "<2quf>": {"name": "LambayequeQuechua", "region": "Americas"},
131
+ "<2mam>": {"name": "Mam", "region": "Americas"},
132
+ "<2arn>": {"name": "Mapudungun", "region": "Americas"},
133
+ "<2miq>": {"name": "Mískito", "region": "Americas"},
134
+ "<2emp>": {"name": "N.Emberá", "region": "Americas"},
135
+ "<2qvz>": {"name": "N.PastazaQuichua", "region": "Americas"},
136
+ "<2nv>": {"name": "Navajo", "region": "Americas"},
137
+ "<2gym>": {"name": "Ngäbere", "region": "Americas"},
138
+ "<2zxx>": {"name": "Noise", "region": "Americas"},
139
+ "<2oj>": {"name": "Ojibwa", "region": "Americas"},
140
+ "<2pap>": {"name": "Papiamento", "region": "Americas"},
141
+ "<2jac>": {"name": "Popti’", "region": "Americas"},
142
+ "<2qu>": {"name": "Quechua", "region": "Americas"},
143
+ "<2otq>": {"name": "QuerétaroOtomi", "region": "Americas"},
144
+ "<2rcf>": {"name": "RéunionCreoleFrench", "region": "Americas"},
145
+ "<2quh>": {"name": "S.BolivianQuechua", "region": "Americas"},
146
+ "<2qup>": {"name": "S.PastazaQuechua", "region": "Americas"},
147
+ "<2cuk>": {"name": "SanBlasKuna", "region": "Americas"},
148
+ "<2srm>": {"name": "Saramaccan", "region": "Americas"},
149
+ "<2crs>": {"name": "SeselwaCreoleFrench", "region": "Americas"},
150
+ "<2shp>": {"name": "Shipibo-Conibo", "region": "Americas"},
151
+ "<2jiv>": {"name": "Shuar", "region": "Americas"},
152
+ "<2srn>": {"name": "SrananTongo", "region": "Americas"},
153
+ "<2acf>": {"name": "StLucianCreoleFrench", "region": "Americas"},
154
+ "<2tca>": {"name": "Ticuna", "region": "Americas"},
155
+ "<2toj>": {"name": "Tojolabal", "region": "Americas"},
156
+ "<2tzh>": {"name": "Tzeltal", "region": "Americas"},
157
+ "<2tzo>": {"name": "Tzotzil", "region": "Americas"},
158
+ "<2tzj>": {"name": "Tz’utujil", "region": "Americas"},
159
+ "<2knj>": {"name": "W.Kanjobal", "region": "Americas"},
160
+ "<2guc>": {"name": "Wayuu", "region": "Americas"},
161
+ "<2noa>": {"name": "WounMeu", "region": "Americas"},
162
+ "<2yua>": {"name": "Yucateco", "region": "Americas"},
163
+ "<2zap>": {"name": "Zapotec", "region": "Americas"},
164
+ "<2az>": {"name": "Azerbaijani", "region": "Central Asia"},
165
+ "<2ba>": {"name": "Bashkir", "region": "Central Asia"},
166
+ "<2kaa-Latn>": {"name": "Kara-Kalpak(Latn)", "region": "Central Asia"},
167
+ "<2kk>": {"name": "Kazakh", "region": "Central Asia"},
168
+ "<2ky>": {"name": "Kyrghyz", "region": "Central Asia"},
169
+ "<2tg>": {"name": "Tajik", "region": "Central Asia"},
170
+ "<2tk>": {"name": "Turkmen", "region": "Central Asia"},
171
+ "<2uz>": {"name": "Uzbek", "region": "Central Asia"},
172
+ "<2yue>": {"name": "Cantonese", "region": "East Asia"},
173
+ "<2dz>": {"name": "Dzongkha", "region": "East Asia"},
174
+ "<2ja>": {"name": "Japanese", "region": "East Asia"},
175
+ "<2ko>": {"name": "Korean", "region": "East Asia"},
176
+ "<2zh>": {"name": "MandarinChinese", "region": "East Asia"},
177
+ "<2mn>": {"name": "Mongolian", "region": "East Asia"},
178
+ "<2bo>": {"name": "Tibetan", "region": "East Asia"},
179
+ "<2sah>": {"name": "Yakut", "region": "East Asia"},
180
+ "<2za>": {"name": "Zhuang", "region": "East Asia"},
181
+ "<2ady>": {"name": "Adyghe", "region": "Europe"},
182
+ "<2sq>": {"name": "Albanian", "region": "Europe"},
183
+ "<2grc>": {"name": "AncientGreek", "region": "Europe"},
184
+ "<2hy>": {"name": "Armenian", "region": "Europe"},
185
+ "<2frp>": {"name": "Arpitan", "region": "Europe"},
186
+ "<2av>": {"name": "Avar", "region": "Europe"},
187
+ "<2eu>": {"name": "Basque", "region": "Europe"},
188
+ "<2be>": {"name": "Belarusian", "region": "Europe"},
189
+ "<2bs>": {"name": "Bosnian", "region": "Europe"},
190
+ "<2br>": {"name": "Breton", "region": "Europe"},
191
+ "<2bg>": {"name": "Bulgarian", "region": "Europe"},
192
+ "<2bua>": {"name": "Buryat", "region": "Europe"},
193
+ "<2rmc>": {"name": "CarpathianRomani", "region": "Europe"},
194
+ "<2ca>": {"name": "Catalan", "region": "Europe"},
195
+ "<2ce>": {"name": "Chechen", "region": "Europe"},
196
+ "<2cv>": {"name": "Chuvash", "region": "Europe"},
197
+ "<2kw>": {"name": "Cornish", "region": "Europe"},
198
+ "<2co>": {"name": "Corsican", "region": "Europe"},
199
+ "<2crh>": {"name": "CrimeanTatar", "region": "Europe"},
200
+ "<2hr>": {"name": "Croatian", "region": "Europe"},
201
+ "<2cs>": {"name": "Czech", "region": "Europe"},
202
+ "<2da>": {"name": "Danish", "region": "Europe"},
203
+ "<2nl>": {"name": "Dutch", "region": "Europe"},
204
+ "<2en>": {"name": "English", "region": "Europe"},
205
+ "<2myv>": {"name": "Erzya", "region": "Europe"},
206
+ "<2eo>": {"name": "Esperanto", "region": "Europe"},
207
+ "<2et>": {"name": "Estonian", "region": "Europe"},
208
+ "<2fo>": {"name": "Faroese", "region": "Europe"},
209
+ "<2fi>": {"name": "Finnish", "region": "Europe"},
210
+ "<2fr>": {"name": "French", "region": "Europe"},
211
+ "<2gag>": {"name": "Gagauz", "region": "Europe"},
212
+ "<2gl>": {"name": "Galician", "region": "Europe"},
213
+ "<2ka>": {"name": "Georgian", "region": "Europe"},
214
+ "<2de>": {"name": "German", "region": "Europe"},
215
+ "<2el>": {"name": "Greek", "region": "Europe"},
216
+ "<2mrj>": {"name": "HillMari", "region": "Europe"},
217
+ "<2hu>": {"name": "Hungarian", "region": "Europe"},
218
+ "<2is>": {"name": "Icelandic", "region": "Europe"},
219
+ "<2ga>": {"name": "Irish", "region": "Europe"},
220
+ "<2it>": {"name": "Italian", "region": "Europe"},
221
+ "<2kbd>": {"name": "Kabardian", "region": "Europe"},
222
+ "<2xal>": {"name": "Kalmyk", "region": "Europe"},
223
+ "<2kaa>": {"name": "Kara-Kalpak", "region": "Europe"},
224
+ "<2krc>": {"name": "Karachay-Balkar", "region": "Europe"},
225
+ "<2kjh>": {"name": "Khakas", "region": "Europe"},
226
+ "<2kv>": {"name": "Komi", "region": "Europe"},
227
+ "<2koi>": {"name": "Komi-Permyak", "region": "Europe"},
228
+ "<2kum>": {"name": "Kumyk", "region": "Europe"},
229
+ "<2ltg>": {"name": "Latgalian", "region": "Europe"},
230
+ "<2la>": {"name": "Latin", "region": "Europe"},
231
+ "<2lv>": {"name": "Latvian", "region": "Europe"},
232
+ "<2lt>": {"name": "Lithuanian", "region": "Europe"},
233
+ "<2lb>": {"name": "Luxembourgish", "region": "Europe"},
234
+ "<2mk>": {"name": "Macedonian", "region": "Europe"},
235
+ "<2mt>": {"name": "Maltese", "region": "Europe"},
236
+ "<2gv>": {"name": "Manx", "region": "Europe"},
237
+ "<2chm>": {"name": "MeadowMari", "region": "Europe"},
238
+ "<2mdf>": {"name": "Moksha", "region": "Europe"},
239
+ "<2se>": {"name": "N.Sami", "region": "Europe"},
240
+ "<2nog>": {"name": "Nogai", "region": "Europe"},
241
+ "<2no>": {"name": "Norwegian", "region": "Europe"},
242
+ "<2oc>": {"name": "Occitan", "region": "Europe"},
243
+ "<2ang>": {"name": "OldEnglish", "region": "Europe"},
244
+ "<2os>": {"name": "Ossetian", "region": "Europe"},
245
+ "<2pl>": {"name": "Polish", "region": "Europe"},
246
+ "<2pt>": {"name": "Portuguese", "region": "Europe"},
247
+ "<2rom>": {"name": "Romani", "region": "Europe"},
248
+ "<2ro>": {"name": "Romanian", "region": "Europe"},
249
+ "<2rm>": {"name": "Romansh", "region": "Europe"},
250
+ "<2ru>": {"name": "Russian", "region": "Europe"},
251
+ "<2alt>": {"name": "S.Altai", "region": "Europe"},
252
+ "<2stq>": {"name": "Saterfriesisch", "region": "Europe"},
253
+ "<2gd>": {"name": "ScottishGaelic", "region": "Europe"},
254
+ "<2sr>": {"name": "Serbian", "region": "Europe"},
255
+ "<2sk>": {"name": "Slovak", "region": "Europe"},
256
+ "<2sl>": {"name": "Slovenian", "region": "Europe"},
257
+ "<2es>": {"name": "Spanish", "region": "Europe"},
258
+ "<2sv>": {"name": "Swedish", "region": "Europe"},
259
+ "<2gsw>": {"name": "SwissGerman", "region": "Europe"},
260
+ "<2tab>": {"name": "Tabassaran", "region": "Europe"},
261
+ "<2tt>": {"name": "Tatar", "region": "Europe"},
262
+ "<2tyv>": {"name": "Tuvinian", "region": "Europe"},
263
+ "<2udm>": {"name": "Udmurt", "region": "Europe"},
264
+ "<2uk>": {"name": "Ukrainian", "region": "Europe"},
265
+ "<2vec>": {"name": "Venetian", "region": "Europe"},
266
+ "<2fy>": {"name": "W.Frisian", "region": "Europe"},
267
+ "<2wa>": {"name": "Walloon", "region": "Europe"},
268
+ "<2cy>": {"name": "Welsh", "region": "Europe"},
269
+ "<2ar>": {"name": "Arabic", "region": "Middle East & North Africa"},
270
+ "<2bgp>": {"name": "E.Baluchi", "region": "Middle East & North Africa"},
271
+ "<2mey>": {"name": "Hassaniyya", "region": "Middle East & North Africa"},
272
+ "<2he>": {"name": "Hebrew", "region": "Middle East & North Africa"},
273
+ "<2ks>": {"name": "Kashmiri", "region": "Middle East & North Africa"},
274
+ "<2ku>": {"name": "Kurdish(Kurmanji)", "region": "Middle East & North Africa"},
275
+ "<2ckb>": {"name": "Kurdish(Sorani)", "region": "Middle East & North Africa"},
276
+ "<2lrc>": {"name": "N.Luri", "region": "Middle East & North Africa"},
277
+ "<2ps>": {"name": "Pashto", "region": "Middle East & North Africa"},
278
+ "<2fa>": {"name": "Persian", "region": "Middle East & North Africa"},
279
+ "<2luz>": {"name": "S.Luri", "region": "Middle East & North Africa"},
280
+ "<2skr>": {"name": "Saraiki", "region": "Middle East & North Africa"},
281
+ "<2sd>": {"name": "Sindhi", "region": "Middle East & North Africa"},
282
+ "<2syr>": {"name": "Syriac", "region": "Middle East & North Africa"},
283
+ "<2tks>": {"name": "Takestani", "region": "Middle East & North Africa"},
284
+ "<2tr>": {"name": "Turkish", "region": "Middle East & North Africa"},
285
+ "<2ur>": {"name": "Urdu", "region": "Middle East & North Africa"},
286
+ "<2ug>": {"name": "Uyghur", "region": "Middle East & North Africa"},
287
+ "<2yi>": {"name": "Yiddish", "region": "Middle East & North Africa"},
288
+ "<2zza>": {"name": "Zaza", "region": "Middle East & North Africa"},
289
+ "<2abt>": {"name": "Ambulas", "region": "Oceania"},
290
+ "<2bi>": {"name": "Bislama", "region": "Oceania"},
291
+ "<2ape>": {"name": "Bukiyip", "region": "Oceania"},
292
+ "<2ch>": {"name": "Chamorro", "region": "Oceania"},
293
+ "<2chk>": {"name": "Chuukese", "region": "Oceania"},
294
+ "<2mps>": {"name": "Dadibi", "region": "Oceania"},
295
+ "<2enq>": {"name": "Enga", "region": "Oceania"},
296
+ "<2hif>": {"name": "FijiHindi", "region": "Oceania"},
297
+ "<2fj>": {"name": "Fijian", "region": "Oceania"},
298
+ "<2gil>": {"name": "Gilbertese", "region": "Oceania"},
299
+ "<2ho>": {"name": "HiriMotu", "region": "Oceania"},
300
+ "<2hui>": {"name": "Huli", "region": "Oceania"},
301
+ "<2kos>": {"name": "Kosraean", "region": "Oceania"},
302
+ "<2ksd>": {"name": "Kuanua", "region": "Oceania"},
303
+ "<2mi>": {"name": "Maori", "region": "Oceania"},
304
+ "<2mh>": {"name": "Marshallese", "region": "Oceania"},
305
+ "<2meu>": {"name": "Motu", "region": "Oceania"},
306
+ "<2tuc>": {"name": "Mutu", "region": "Oceania"},
307
+ "<2pau>": {"name": "Palauan", "region": "Oceania"},
308
+ "<2pis>": {"name": "Pijin", "region": "Oceania"},
309
+ "<2pon>": {"name": "Pohnpeian", "region": "Oceania"},
310
+ "<2rwo>": {"name": "Rawa", "region": "Oceania"},
311
+ "<2hvn>": {"name": "Sabu", "region": "Oceania"},
312
+ "<2sm>": {"name": "Samoan", "region": "Oceania"},
313
+ "<2twu>": {"name": "Termanu", "region": "Oceania"},
314
+ "<2to>": {"name": "Tonga(TongaIslands)", "region": "Oceania"},
315
+ "<2tvl>": {"name": "Tuvalu", "region": "Oceania"},
316
+ "<2ubu>": {"name": "Umbu-Ungu", "region": "Oceania"},
317
+ "<2yap>": {"name": "Yapese", "region": "Oceania"},
318
+ "<2as>": {"name": "Assamese", "region": "South Asia"},
319
+ "<2awa>": {"name": "Awadhi", "region": "South Asia"},
320
+ "<2bn>": {"name": "Bengali", "region": "South Asia"},
321
+ "<2bho>": {"name": "Bhojpuri", "region": "South Asia"},
322
+ "<2brx>": {"name": "Bodo(India)", "region": "South Asia"},
323
+ "<2hne>": {"name": "Chhattisgarhi", "region": "South Asia"},
324
+ "<2dv>": {"name": "Dhivehi", "region": "South Asia"},
325
+ "<2taj>": {"name": "E.Tamang", "region": "South Asia"},
326
+ "<2gbm>": {"name": "Garhwali", "region": "South Asia"},
327
+ "<2gom>": {"name": "GoanKonkani", "region": "South Asia"},
328
+ "<2gu>": {"name": "Gujarati", "region": "South Asia"},
329
+ "<2hi>": {"name": "Hindi", "region": "South Asia"},
330
+ "<2bjj>": {"name": "Kanauji", "region": "South Asia"},
331
+ "<2kn>": {"name": "Kannada", "region": "South Asia"},
332
+ "<2kha>": {"name": "Khasi", "region": "South Asia"},
333
+ "<2trp>": {"name": "KokBorok", "region": "South Asia"},
334
+ "<2mag>": {"name": "Magahi", "region": "South Asia"},
335
+ "<2mai>": {"name": "Maithili", "region": "South Asia"},
336
+ "<2ml>": {"name": "Malayalam", "region": "South Asia"},
337
+ "<2mr>": {"name": "Marathi", "region": "South Asia"},
338
+ "<2mni>": {"name": "Meiteilon(Manipuri)", "region": "South Asia"},
339
+ "<2mtr>": {"name": "Mewari", "region": "South Asia"},
340
+ "<2lus>": {"name": "Mizo", "region": "South Asia"},
341
+ "<2ne>": {"name": "Nepali", "region": "South Asia"},
342
+ "<2new>": {"name": "Newari", "region": "South Asia"},
343
+ "<2noe>": {"name": "Nimadi", "region": "South Asia"},
344
+ "<2or>": {"name": "Odia(Oriya)", "region": "South Asia"},
345
+ "<2pa>": {"name": "Punjabi", "region": "South Asia"},
346
+ "<2raj>": {"name": "Rajasthani", "region": "South Asia"},
347
+ "<2sa>": {"name": "Sanskrit", "region": "South Asia"},
348
+ "<2smt>": {"name": "Simte", "region": "South Asia"},
349
+ "<2si>": {"name": "Sinhala", "region": "South Asia"},
350
+ "<2suz>": {"name": "Sunwar", "region": "South Asia"},
351
+ "<2sjp>": {"name": "Surjapuri", "region": "South Asia"},
352
+ "<2syl>": {"name": "Sylheti", "region": "South Asia"},
353
+ "<2ta>": {"name": "Tamil", "region": "South Asia"},
354
+ "<2te>": {"name": "Telugu", "region": "South Asia"},
355
+ "<2tcy>": {"name": "Tulu", "region": "South Asia"},
356
+ "<2ace>": {"name": "Achinese", "region": "Southeast Asia"},
357
+ "<2msm>": {"name": "AgusanManobo", "region": "Southeast Asia"},
358
+ "<2ahk>": {"name": "Akha", "region": "Southeast Asia"},
359
+ "<2ban>": {"name": "Balinese", "region": "Southeast Asia"},
360
+ "<2bgz>": {"name": "Banggai", "region": "Southeast Asia"},
361
+ "<2akb>": {"name": "BatakAngkola", "region": "Southeast Asia"},
362
+ "<2btx>": {"name": "BatakKaro", "region": "Southeast Asia"},
363
+ "<2bts>": {"name": "BatakSimalungun", "region": "Southeast Asia"},
364
+ "<2bbc>": {"name": "BatakToba", "region": "Southeast Asia"},
365
+ "<2bew>": {"name": "Betawi", "region": "Southeast Asia"},
366
+ "<2jvn>": {"name": "CaribbeanJavanese", "region": "Southeast Asia"},
367
+ "<2ceb>": {"name": "Cebuano", "region": "Southeast Asia"},
368
+ "<2bik>": {"name": "CentralBikol", "region": "Southeast Asia"},
369
+ "<2mel>": {"name": "CentralMelanau", "region": "Southeast Asia"},
370
+ "<2cbk>": {"name": "Chavacano", "region": "Southeast Asia"},
371
+ "<2bru>": {"name": "E.Bru", "region": "Southeast Asia"},
372
+ "<2cfm>": {"name": "FalamChin", "region": "Southeast Asia"},
373
+ "<2fil>": {"name": "Filipino", "region": "Southeast Asia"},
374
+ "<2gor>": {"name": "Gorontalo", "region": "Southeast Asia"},
375
+ "<2cnh>": {"name": "HakhaChin", "region": "Southeast Asia"},
376
+ "<2hil>": {"name": "Hiligaynon", "region": "Southeast Asia"},
377
+ "<2hmn>": {"name": "Hmong", "region": "Southeast Asia"},
378
+ "<2iba>": {"name": "Iban", "region": "Southeast Asia"},
379
+ "<2ilo>": {"name": "Ilocano", "region": "Southeast Asia"},
380
+ "<2id>": {"name": "Indonesian", "region": "Southeast Asia"},
381
+ "<2ium>": {"name": "IuMien", "region": "Southeast Asia"},
382
+ "<2jax>": {"name": "JambiMalay", "region": "Southeast Asia"},
383
+ "<2jv>": {"name": "Javanese", "region": "Southeast Asia"},
384
+ "<2kac>": {"name": "Kachin", "region": "Southeast Asia"},
385
+ "<2dtp>": {"name": "KadazanDusun", "region": "Southeast Asia"},
386
+ "<2meo>": {"name": "KedahMalay", "region": "Southeast Asia"},
387
+ "<2ify>": {"name": "Keley-IKallahan", "region": "Southeast Asia"},
388
+ "<2km>": {"name": "Khmer", "region": "Southeast Asia"},
389
+ "<2kjg>": {"name": "Khmu", "region": "Southeast Asia"},
390
+ "<2krj>": {"name": "Kinaray-A", "region": "Southeast Asia"},
391
+ "<2mkn>": {"name": "KupangMalay", "region": "Southeast Asia"},
392
+ "<2lhu>": {"name": "Lahu", "region": "Southeast Asia"},
393
+ "<2lo>": {"name": "Lao", "region": "Southeast Asia"},
394
+ "<2mad>": {"name": "Madurese", "region": "Southeast Asia"},
395
+ "<2mak>": {"name": "Makasar", "region": "Southeast Asia"},
396
+ "<2ms>": {"name": "Malay", "region": "Southeast Asia"},
397
+ "<2xmm>": {"name": "ManadoMalay", "region": "Southeast Asia"},
398
+ "<2mqy>": {"name": "Manggarai", "region": "Southeast Asia"},
399
+ "<2mrw>": {"name": "Maranao", "region": "Southeast Asia"},
400
+ "<2msb>": {"name": "Masbatenyo", "region": "Southeast Asia"},
401
+ "<2mbt>": {"name": "MatigsalugManobo", "region": "Southeast Asia"},
402
+ "<2min>": {"name": "Minangkabau", "region": "Southeast Asia"},
403
+ "<2mnw>": {"name": "Mon", "region": "Southeast Asia"},
404
+ "<2my>": {"name": "Myanmar(Burmese)", "region": "Southeast Asia"},
405
+ "<2nij>": {"name": "Ngaju", "region": "Southeast Asia"},
406
+ "<2nia>": {"name": "Nias", "region": "Southeast Asia"},
407
+ "<2nut>": {"name": "Nung(VietNam)", "region": "Southeast Asia"},
408
+ "<2pck>": {"name": "PaiteChin", "region": "Southeast Asia"},
409
+ "<2pam>": {"name": "Pampanga", "region": "Southeast Asia"},
410
+ "<2pag>": {"name": "Pangasinan", "region": "Southeast Asia"},
411
+ "<2prk>": {"name": "Parauk", "region": "Southeast Asia"},
412
+ "<2rki>": {"name": "Rakhine", "region": "Southeast Asia"},
413
+ "<2msi>": {"name": "SabahMalay", "region": "Southeast Asia"},
414
+ "<2sxn>": {"name": "Sangir", "region": "Southeast Asia"},
415
+ "<2shn>": {"name": "Shan", "region": "Southeast Asia"},
416
+ "<2su>": {"name": "Sundanese", "region": "Southeast Asia"},
417
+ "<2ksw>": {"name": "S’gawKaren", "region": "Southeast Asia"},
418
+ "<2tsg>": {"name": "Tausug", "region": "Southeast Asia"},
419
+ "<2tet>": {"name": "Tetum", "region": "Southeast Asia"},
420
+ "<2th>": {"name": "Thai", "region": "Southeast Asia"},
421
+ "<2sda>": {"name": "Toraja-Sa’dan", "region": "Southeast Asia"},
422
+ "<2tyz>": {"name": "Tày", "region": "Southeast Asia"},
423
+ "<2ppk>": {"name": "Uma", "region": "Southeast Asia"},
424
+ "<2vi>": {"name": "Vietnamese", "region": "Southeast Asia"},
425
+ "<2war>": {"name": "Waray(Philippines)", "region": "Southeast Asia"},
426
  }
scripts/generate_langmap.py CHANGED
@@ -13,6 +13,7 @@ Dev dependencies (requirements-dev.txt):
13
  pdfplumber, pycountry
14
  """
15
 
 
16
  import sys
17
  from pathlib import Path
18
 
@@ -21,6 +22,26 @@ import pdfplumber
21
  # Pages 16-22 of the paper contain Table 9 (0-indexed: 15-21)
22
  TABLE_PAGES = range(15, 22)
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # Default region based on writing script
25
  SCRIPT_TO_REGION: dict[str, str] = {
26
  "Arab": "Middle East & North Africa",
@@ -50,6 +71,7 @@ SCRIPT_TO_REGION: dict[str, str] = {
50
  "Olck": "South Asia",
51
  "Orya": "South Asia",
52
  "Sinh": "South Asia",
 
53
  "Taml": "South Asia",
54
  "Telu": "South Asia",
55
  "Tfng": "Africa",
@@ -60,81 +82,326 @@ SCRIPT_TO_REGION: dict[str, str] = {
60
 
61
  # Language-specific overrides (primarily for Latin-script languages and corrections)
62
  LANGUAGE_OVERRIDES: dict[str, str] = {
63
- # Europe
64
- "af": "Africa",
65
- "az": "Central Asia",
66
  "bs": "Europe",
67
  "ca": "Europe",
68
- "ceb": "Southeast Asia",
69
  "co": "Europe",
70
  "cs": "Europe",
71
  "cy": "Europe",
72
  "da": "Europe",
73
  "de": "Europe",
74
  "en": "Europe",
 
75
  "es": "Europe",
76
  "et": "Europe",
77
  "eu": "Europe",
78
  "fi": "Europe",
79
- "fil": "Southeast Asia",
80
  "fr": "Europe",
 
81
  "fy": "Europe",
82
  "ga": "Europe",
 
83
  "gd": "Europe",
84
  "gl": "Europe",
 
 
85
  "hr": "Europe",
86
- "ht": "Americas",
87
  "hu": "Europe",
88
- "id": "Southeast Asia",
89
- "ig": "Africa",
90
  "is": "Europe",
91
  "it": "Europe",
92
- "jv": "Southeast Asia",
93
- "kk": "Central Asia",
94
- "ku": "Middle East & North Africa",
95
- "ky": "Central Asia",
96
  "la": "Europe",
97
  "lb": "Europe",
98
  "lt": "Europe",
 
99
  "lv": "Europe",
100
- "mg": "Africa",
101
- "mi": "Oceania",
102
  "mk": "Europe",
103
- "mn": "East Asia",
104
- "ms": "Southeast Asia",
105
  "mt": "Europe",
106
  "nl": "Europe",
107
  "nn": "Europe",
108
  "no": "Europe",
109
- "ny": "Africa",
110
  "pl": "Europe",
111
  "pt": "Europe",
112
- "qu": "Americas",
 
113
  "ro": "Europe",
114
- "rw": "Africa",
 
115
  "sk": "Europe",
116
  "sl": "Europe",
117
- "sm": "Oceania",
118
- "sn": "Africa",
119
- "so": "Africa",
120
  "sq": "Europe",
121
  "sr": "Europe",
122
- "st": "Africa",
123
- "su": "Southeast Asia",
124
  "sv": "Europe",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  "sw": "Africa",
126
- "tg": "Central Asia",
127
- "tk": "Central Asia",
128
- "tl": "Southeast Asia",
129
- "to": "Oceania",
130
- "tr": "Middle East & North Africa",
131
- "uz": "Central Asia",
132
- "vi": "Southeast Asia",
 
 
 
 
133
  "xh": "Africa",
134
  "yo": "Africa",
 
135
  "zu": "Africa",
136
- # South Asia (non-Devanagari Latin-script entries)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  "cjm": "Southeast Asia",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  # Corrections for Cyrillic languages not in Europe
139
  "ba": "Central Asia",
140
  "ce": "Europe",
@@ -143,32 +410,64 @@ LANGUAGE_OVERRIDES: dict[str, str] = {
143
  "os": "Europe",
144
  "sah": "East Asia",
145
  "tt": "Europe",
 
 
 
 
146
  }
147
 
148
 
149
  def parse_table9(pdf_path: str) -> list[dict[str, str]]:
150
- """Parse Table 9 from the MADLAD-400 paper."""
 
 
 
 
 
 
151
  entries: list[dict[str, str]] = []
152
  with pdfplumber.open(pdf_path) as pdf:
153
  for page_num in TABLE_PAGES:
154
  page = pdf.pages[page_num]
155
- table = page.extract_table()
156
- if not table:
157
- continue
158
- for row in table:
159
- if not row or not row[0]:
 
 
 
 
 
 
 
 
 
 
 
160
  continue
161
- bcp47 = row[0].strip()
162
- # Skip header rows
163
- if bcp47 == "BCP-47" or bcp47 == "":
 
 
 
 
 
 
 
 
 
 
 
164
  continue
165
- name = row[1].strip() if row[1] else ""
166
- script = row[2].strip() if row[2] else ""
167
- # Skip rows with "-" in all data columns (last 79 self-audit omissions)
168
- data_cols = [c for c in row[3:] if c is not None]
169
- if all(c.strip() == "-" for c in data_cols) or not data_cols:
170
  continue
 
171
  entries.append({"bcp47": bcp47, "name": name, "script": script})
 
172
  return entries
173
 
174
 
 
13
  pdfplumber, pycountry
14
  """
15
 
16
+ import re
17
  import sys
18
  from pathlib import Path
19
 
 
22
  # Pages 16-22 of the paper contain Table 9 (0-indexed: 15-21)
23
  TABLE_PAGES = range(15, 22)
24
 
25
+ # On the last page of the table (page 22, 0-indexed 21), the table ends around
26
+ # y=430 and is followed by Section A.2. Stop processing words below this threshold.
27
+ PAGE_22_MAX_TOP = 440
28
+
29
+ # Column x-coordinate boundaries for Table 9
30
+ COL_BCP47_MIN = 100
31
+ COL_BCP47_MAX = 140
32
+ COL_NAME_MIN = 140
33
+ COL_NAME_MAX = 220
34
+ COL_SCRIPT_MIN = 220
35
+ COL_SCRIPT_MAX = 260
36
+ COL_DATA_MIN = 260
37
+
38
+ # Tolerance in pixels for matching name/script cells to BCP-47 cells in the same row.
39
+ # Some multi-line name cells have a slightly different y-position than the BCP-47 cell.
40
+ ROW_TOLERANCE = 5
41
+
42
+ # BCP-47 code pattern: lowercase base subtag, optional uppercase subtags (e.g. tly-IR, nan-Latn-TW)
43
+ BCP47_RE = re.compile(r"^[a-z]{2,}(-[A-Za-z0-9]+)*$")
44
+
45
  # Default region based on writing script
46
  SCRIPT_TO_REGION: dict[str, str] = {
47
  "Arab": "Middle East & North Africa",
 
71
  "Olck": "South Asia",
72
  "Orya": "South Asia",
73
  "Sinh": "South Asia",
74
+ "Syrc": "Middle East & North Africa",
75
  "Taml": "South Asia",
76
  "Telu": "South Asia",
77
  "Tfng": "Africa",
 
82
 
83
  # Language-specific overrides (primarily for Latin-script languages and corrections)
84
  LANGUAGE_OVERRIDES: dict[str, str] = {
85
+ # Europe (Latin-script)
86
+ "ang": "Europe",
87
+ "br": "Europe",
88
  "bs": "Europe",
89
  "ca": "Europe",
 
90
  "co": "Europe",
91
  "cs": "Europe",
92
  "cy": "Europe",
93
  "da": "Europe",
94
  "de": "Europe",
95
  "en": "Europe",
96
+ "eo": "Europe",
97
  "es": "Europe",
98
  "et": "Europe",
99
  "eu": "Europe",
100
  "fi": "Europe",
101
+ "fo": "Europe",
102
  "fr": "Europe",
103
+ "frp": "Europe",
104
  "fy": "Europe",
105
  "ga": "Europe",
106
+ "gag": "Europe",
107
  "gd": "Europe",
108
  "gl": "Europe",
109
+ "gsw": "Europe",
110
+ "gv": "Europe",
111
  "hr": "Europe",
 
112
  "hu": "Europe",
 
 
113
  "is": "Europe",
114
  "it": "Europe",
115
+ "kw": "Europe",
 
 
 
116
  "la": "Europe",
117
  "lb": "Europe",
118
  "lt": "Europe",
119
+ "ltg": "Europe",
120
  "lv": "Europe",
 
 
121
  "mk": "Europe",
 
 
122
  "mt": "Europe",
123
  "nl": "Europe",
124
  "nn": "Europe",
125
  "no": "Europe",
126
+ "oc": "Europe",
127
  "pl": "Europe",
128
  "pt": "Europe",
129
+ "rm": "Europe",
130
+ "rmc": "Europe",
131
  "ro": "Europe",
132
+ "rom": "Europe",
133
+ "se": "Europe",
134
  "sk": "Europe",
135
  "sl": "Europe",
 
 
 
136
  "sq": "Europe",
137
  "sr": "Europe",
138
+ "stq": "Europe",
 
139
  "sv": "Europe",
140
+ "vec": "Europe",
141
+ "wa": "Europe",
142
+ # Africa (Latin-script)
143
+ "aa": "Africa",
144
+ "ach": "Africa",
145
+ "ada": "Africa",
146
+ "adh": "Africa",
147
+ "af": "Africa",
148
+ "ak": "Africa",
149
+ "alz": "Africa",
150
+ "ann": "Africa",
151
+ "bas": "Africa",
152
+ "bci": "Africa",
153
+ "bim": "Africa",
154
+ "bm": "Africa",
155
+ "bqc": "Africa",
156
+ "bum": "Africa",
157
+ "bus": "Africa",
158
+ "cce": "Africa",
159
+ "cjk": "Africa",
160
+ "din": "Africa",
161
+ "dje": "Africa",
162
+ "dov": "Africa",
163
+ "dwr": "Africa",
164
+ "dyu": "Africa",
165
+ "ee": "Africa",
166
+ "ff": "Africa",
167
+ "ffm": "Africa",
168
+ "fip": "Africa",
169
+ "fon": "Africa",
170
+ "gub": "Africa",
171
+ "gvl": "Africa",
172
+ "ha": "Africa",
173
+ "ig": "Africa",
174
+ "ibb": "Africa",
175
+ "iso": "Africa",
176
+ "izz": "Africa",
177
+ "kbp": "Africa",
178
+ "kg": "Africa",
179
+ "kj": "Africa",
180
+ "kmb": "Africa",
181
+ "kri": "Africa",
182
+ "ktu": "Africa",
183
+ "laj": "Africa",
184
+ "lg": "Africa",
185
+ "ln": "Africa",
186
+ "lu": "Africa",
187
+ "mas": "Africa",
188
+ "mfe": "Africa",
189
+ "mg": "Africa",
190
+ "mgh": "Africa",
191
+ "niq": "Africa",
192
+ "nnb": "Africa",
193
+ "nso": "Africa",
194
+ "nr": "Africa",
195
+ "ny": "Africa",
196
+ "nyu": "Africa",
197
+ "nzi": "Africa",
198
+ "om": "Africa",
199
+ "rn": "Africa",
200
+ "rw": "Africa",
201
+ "seh": "Africa",
202
+ "sg": "Africa",
203
+ "sn": "Africa",
204
+ "so": "Africa",
205
+ "spp": "Africa",
206
+ "srr": "Africa",
207
+ "ss": "Africa",
208
+ "st": "Africa",
209
+ "sus": "Africa",
210
  "sw": "Africa",
211
+ "tbz": "Africa",
212
+ "tdx": "Africa",
213
+ "teo": "Africa",
214
+ "tiv": "Africa",
215
+ "tll": "Africa",
216
+ "tn": "Africa",
217
+ "ts": "Africa",
218
+ "tsc": "Africa",
219
+ "ve": "Africa",
220
+ "wal": "Africa",
221
+ "wo": "Africa",
222
  "xh": "Africa",
223
  "yo": "Africa",
224
+ "zne": "Africa",
225
  "zu": "Africa",
226
+ # Americas (Latin-script)
227
+ "acf": "Americas",
228
+ "agr": "Americas",
229
+ "cab": "Americas",
230
+ "amu": "Americas",
231
+ "arn": "Americas",
232
+ "ay": "Americas",
233
+ "bzj": "Americas",
234
+ "cac": "Americas",
235
+ "cak": "Americas",
236
+ "cni": "Americas",
237
+ "ctu": "Americas",
238
+ "cuk": "Americas",
239
+ "djk": "Americas",
240
+ "emp": "Americas",
241
+ "gn": "Americas",
242
+ "guc": "Americas",
243
+ "guh": "Americas",
244
+ "gui": "Americas",
245
+ "gym": "Americas",
246
+ "gyn": "Americas",
247
+ "haw": "Americas",
248
+ "ht": "Americas",
249
+ "hus": "Americas",
250
+ "inb": "Americas",
251
+ "jac": "Americas",
252
+ "jam": "Americas",
253
+ "jiv": "Americas",
254
+ "kek": "Americas",
255
+ "kl": "Americas",
256
+ "knj": "Americas",
257
+ "kwi": "Americas",
258
+ "mam": "Americas",
259
+ "maz": "Americas",
260
+ "miq": "Americas",
261
+ "ngu": "Americas",
262
+ "nhe": "Americas",
263
+ "noa": "Americas",
264
+ "nv": "Americas",
265
+ "otq": "Americas",
266
+ "pap": "Americas",
267
+ "qu": "Americas",
268
+ "qub": "Americas",
269
+ "quc": "Americas",
270
+ "quf": "Americas",
271
+ "quh": "Americas",
272
+ "qup": "Americas",
273
+ "quy": "Americas",
274
+ "qvc": "Americas",
275
+ "qvi": "Americas",
276
+ "qvz": "Americas",
277
+ "qxr": "Americas",
278
+ "rcf": "Americas",
279
+ "shp": "Americas",
280
+ "sja": "Americas",
281
+ "srm": "Americas",
282
+ "srn": "Americas",
283
+ "tca": "Americas",
284
+ "toj": "Americas",
285
+ "tzh": "Americas",
286
+ "tzj": "Americas",
287
+ "tzo": "Americas",
288
+ "yua": "Americas",
289
+ "zap": "Americas",
290
+ # Southeast Asia (Latin-script)
291
+ "ace": "Southeast Asia",
292
+ "ahk": "Southeast Asia",
293
+ "akb": "Southeast Asia",
294
+ "ban": "Southeast Asia",
295
+ "bbc": "Southeast Asia",
296
+ "bew": "Southeast Asia",
297
+ "bgz": "Southeast Asia",
298
+ "bik": "Southeast Asia",
299
+ "bru": "Southeast Asia",
300
+ "btx": "Southeast Asia",
301
+ "bts": "Southeast Asia",
302
+ "cbk": "Southeast Asia",
303
+ "ceb": "Southeast Asia",
304
+ "cfm": "Southeast Asia",
305
  "cjm": "Southeast Asia",
306
+ "cnh": "Southeast Asia",
307
+ "dtp": "Southeast Asia",
308
+ "fil": "Southeast Asia",
309
+ "gor": "Southeast Asia",
310
+ "hil": "Southeast Asia",
311
+ "hmn": "Southeast Asia",
312
+ "iba": "Southeast Asia",
313
+ "id": "Southeast Asia",
314
+ "ify": "Southeast Asia",
315
+ "ilo": "Southeast Asia",
316
+ "ium": "Southeast Asia",
317
+ "jax": "Southeast Asia",
318
+ "jv": "Southeast Asia",
319
+ "jvn": "Southeast Asia",
320
+ "kac": "Southeast Asia",
321
+ "krj": "Southeast Asia",
322
+ "lhu": "Southeast Asia",
323
+ "mad": "Southeast Asia",
324
+ "mak": "Southeast Asia",
325
+ "mbt": "Southeast Asia",
326
+ "mel": "Southeast Asia",
327
+ "meo": "Southeast Asia",
328
+ "min": "Southeast Asia",
329
+ "mkn": "Southeast Asia",
330
+ "mqy": "Southeast Asia",
331
+ "mrw": "Southeast Asia",
332
+ "ms": "Southeast Asia",
333
+ "msi": "Southeast Asia",
334
+ "msb": "Southeast Asia",
335
+ "msm": "Southeast Asia",
336
+ "nia": "Southeast Asia",
337
+ "nij": "Southeast Asia",
338
+ "nut": "Southeast Asia",
339
+ "pag": "Southeast Asia",
340
+ "pam": "Southeast Asia",
341
+ "pck": "Southeast Asia",
342
+ "ppk": "Southeast Asia",
343
+ "prk": "Southeast Asia",
344
+ "sda": "Southeast Asia",
345
+ "su": "Southeast Asia",
346
+ "sxn": "Southeast Asia",
347
+ "tet": "Southeast Asia",
348
+ "tl": "Southeast Asia",
349
+ "tsg": "Southeast Asia",
350
+ "tyz": "Southeast Asia",
351
+ "vi": "Southeast Asia",
352
+ "war": "Southeast Asia",
353
+ "xmm": "Southeast Asia",
354
+ # Oceania (Latin-script)
355
+ "abt": "Oceania",
356
+ "ape": "Oceania",
357
+ "bi": "Oceania",
358
+ "ch": "Oceania",
359
+ "chk": "Oceania",
360
+ "enq": "Oceania",
361
+ "fj": "Oceania",
362
+ "gil": "Oceania",
363
+ "hif": "Oceania",
364
+ "ho": "Oceania",
365
+ "hui": "Oceania",
366
+ "hvn": "Oceania",
367
+ "kos": "Oceania",
368
+ "ksd": "Oceania",
369
+ "meu": "Oceania",
370
+ "mh": "Oceania",
371
+ "mi": "Oceania",
372
+ "mps": "Oceania",
373
+ "pau": "Oceania",
374
+ "pis": "Oceania",
375
+ "pon": "Oceania",
376
+ "rwo": "Oceania",
377
+ "sm": "Oceania",
378
+ "to": "Oceania",
379
+ "tuc": "Oceania",
380
+ "tvl": "Oceania",
381
+ "twu": "Oceania",
382
+ "ubu": "Oceania",
383
+ "yap": "Oceania",
384
+ # South Asia (Latin-script)
385
+ "kha": "South Asia",
386
+ "lus": "South Asia",
387
+ "smt": "South Asia",
388
+ "trp": "South Asia",
389
+ # East Asia (Latin-script)
390
+ "mn": "East Asia",
391
+ "za": "East Asia",
392
+ # Middle East & North Africa (Latin-script)
393
+ "bgp": "Middle East & North Africa",
394
+ "ku": "Middle East & North Africa",
395
+ "tr": "Middle East & North Africa",
396
+ "zza": "Middle East & North Africa",
397
+ # Central Asia (Latin-script)
398
+ "az": "Central Asia",
399
+ "kaa-Latn": "Central Asia",
400
+ "kk": "Central Asia",
401
+ "ky": "Central Asia",
402
+ "tg": "Central Asia",
403
+ "tk": "Central Asia",
404
+ "uz": "Central Asia",
405
  # Corrections for Cyrillic languages not in Europe
406
  "ba": "Central Asia",
407
  "ce": "Europe",
 
410
  "os": "Europe",
411
  "sah": "East Asia",
412
  "tt": "Europe",
413
+ # Non-geographic / constructed languages: assign to Americas (creator's continent)
414
+ "crs": "Americas", # Seselwa Creole French (Indian Ocean) — grouped with Atlantic/Indian Ocean creoles
415
+ "tlh": "Americas", # Klingon — constructed language
416
+ "zxx": "Americas", # Noise/non-linguistic content — placeholder region
417
  }
418
 
419
 
420
  def parse_table9(pdf_path: str) -> list[dict[str, str]]:
421
+ """Parse Table 9 from the MADLAD-400 paper using word-based extraction.
422
+
423
+ pdfplumber's extract_table() does not work on this PDF because the table
424
+ lines are not detected as table borders. Instead we extract words and
425
+ assign them to columns by x-coordinate, then group by row using a
426
+ y-position tolerance to handle multi-line name cells.
427
+ """
428
  entries: list[dict[str, str]] = []
429
  with pdfplumber.open(pdf_path) as pdf:
430
  for page_num in TABLE_PAGES:
431
  page = pdf.pages[page_num]
432
+ words = page.extract_words()
433
+
434
+ # On the last table page, cut off below the table (avoids Section A.2 code block)
435
+ max_top = PAGE_22_MAX_TOP if page_num == 21 else float("inf")
436
+
437
+ # Partition words by column
438
+ bcp47_col = [w for w in words if COL_BCP47_MIN <= w["x0"] < COL_BCP47_MAX and w["top"] <= max_top]
439
+ name_col = [w for w in words if COL_NAME_MIN <= w["x0"] < COL_NAME_MAX and w["top"] <= max_top]
440
+ script_col = [w for w in words if COL_SCRIPT_MIN <= w["x0"] < COL_SCRIPT_MAX and w["top"] <= max_top]
441
+ data_col = [w for w in words if w["x0"] >= COL_DATA_MIN and w["top"] <= max_top]
442
+
443
+ for bcp_w in bcp47_col:
444
+ bcp47 = bcp_w["text"]
445
+
446
+ # Skip non-BCP-47 text (headers, prose, code)
447
+ if not BCP47_RE.match(bcp47) or bcp47 in ("total", "median"):
448
  continue
449
+
450
+ bcp_top = bcp_w["top"]
451
+
452
+ # Find cells in the same row (within ROW_TOLERANCE pixels)
453
+ name_ws = [w for w in name_col if abs(w["top"] - bcp_top) <= ROW_TOLERANCE]
454
+ script_ws = [w for w in script_col if abs(w["top"] - bcp_top) <= ROW_TOLERANCE]
455
+ data_ws = [w for w in data_col if abs(w["top"] - bcp_top) <= ROW_TOLERANCE]
456
+
457
+ name = " ".join(w["text"] for w in sorted(name_ws, key=lambda w: w["x0"]))
458
+ script = " ".join(w["text"] for w in sorted(script_ws, key=lambda w: w["x0"]))
459
+ data_texts = [w["text"] for w in sorted(data_ws, key=lambda w: w["x0"])]
460
+
461
+ # Require both name and script to be present
462
+ if not name or not script:
463
  continue
464
+
465
+ # Skip rows where all data columns are "-" (self-audit omissions)
466
+ if data_texts and all(d.strip() == "-" for d in data_texts):
 
 
467
  continue
468
+
469
  entries.append({"bcp47": bcp47, "name": name, "script": script})
470
+
471
  return entries
472
 
473