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

feat: add generation script for language mapping from MADLAD-400 paper

Browse files
Files changed (1) hide show
  1. scripts/generate_langmap.py +245 -0
scripts/generate_langmap.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate langid_mapping.py from MADLAD-400 paper Table 9.
4
+
5
+ Parses Table 9 (Section A.1, pages 16-22) from the MADLAD-400 paper PDF,
6
+ assigns regions using script-based defaults and language-specific overrides,
7
+ and writes the result to langmap/langid_mapping.py.
8
+
9
+ Usage:
10
+ python scripts/generate_langmap.py <path-to-paper.pdf>
11
+
12
+ Dev dependencies (requirements-dev.txt):
13
+ pdfplumber, pycountry
14
+ """
15
+
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ import pdfplumber
20
+
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",
27
+ "Armn": "Europe",
28
+ "Beng": "South Asia",
29
+ "Cans": "Americas",
30
+ "Cher": "Americas",
31
+ "Cyrl": "Europe",
32
+ "Deva": "South Asia",
33
+ "Ethi": "Africa",
34
+ "Geor": "Europe",
35
+ "Grek": "Europe",
36
+ "Gujr": "South Asia",
37
+ "Guru": "South Asia",
38
+ "Hang": "East Asia",
39
+ "Hans": "East Asia",
40
+ "Hant": "East Asia",
41
+ "Hebr": "Middle East & North Africa",
42
+ "Jpan": "East Asia",
43
+ "Khmr": "Southeast Asia",
44
+ "Knda": "South Asia",
45
+ "Kore": "East Asia",
46
+ "Laoo": "Southeast Asia",
47
+ "Mlym": "South Asia",
48
+ "Mong": "East Asia",
49
+ "Mymr": "Southeast Asia",
50
+ "Olck": "South Asia",
51
+ "Orya": "South Asia",
52
+ "Sinh": "South Asia",
53
+ "Taml": "South Asia",
54
+ "Telu": "South Asia",
55
+ "Tfng": "Africa",
56
+ "Thaa": "South Asia",
57
+ "Thai": "Southeast Asia",
58
+ "Tibt": "East Asia",
59
+ }
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",
141
+ "cv": "Europe",
142
+ "kv": "Europe",
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
+
175
+ def assign_region(bcp47: str, script: str) -> str:
176
+ """Assign a geographic region based on language code and script."""
177
+ if bcp47 in LANGUAGE_OVERRIDES:
178
+ return LANGUAGE_OVERRIDES[bcp47]
179
+ if script in SCRIPT_TO_REGION:
180
+ return SCRIPT_TO_REGION[script]
181
+ return "Other"
182
+
183
+
184
+ def write_mapping(entries: list[dict[str, str]], output_path: Path) -> None:
185
+ """Write the language mapping to a Python file."""
186
+ mapping: list[tuple[str, str, str]] = []
187
+ for entry in entries:
188
+ token = f"<2{entry['bcp47']}>"
189
+ region = assign_region(entry["bcp47"], entry["script"])
190
+ mapping.append((token, entry["name"], region))
191
+
192
+ # Sort by region, then by name within each region
193
+ mapping.sort(key=lambda x: (x[2], x[1]))
194
+
195
+ lines = [
196
+ "# Auto-generated by scripts/generate_langmap.py from MADLAD-400 paper Table 9 (Section A.1)",
197
+ "# Source: https://arxiv.org/pdf/2309.04662",
198
+ f"# {len(mapping)} languages with training data (excludes 79 self-audit omissions)",
199
+ "#",
200
+ "# To regenerate:",
201
+ "# python scripts/generate_langmap.py <path-to-paper.pdf>",
202
+ "langid_to_language = {",
203
+ ]
204
+ for token, name, region in mapping:
205
+ # Escape any quotes in names
206
+ escaped_name = name.replace('"', '\\"')
207
+ lines.append(f' "{token}": {{"name": "{escaped_name}", "region": "{region}"}},')
208
+ lines.append("}")
209
+ lines.append("")
210
+
211
+ output_path.write_text("\n".join(lines))
212
+
213
+
214
+ def main() -> None:
215
+ if len(sys.argv) != 2:
216
+ print("Usage: python scripts/generate_langmap.py <path-to-paper.pdf>")
217
+ sys.exit(1)
218
+
219
+ pdf_path = sys.argv[1]
220
+ entries = parse_table9(pdf_path)
221
+ print(f"Parsed {len(entries)} languages from Table 9")
222
+
223
+ # Report region assignments
224
+ other: list[tuple[str, str]] = []
225
+ for entry in entries:
226
+ region = assign_region(entry["bcp47"], entry["script"])
227
+ if region == "Other":
228
+ other.append((entry["bcp47"], entry["name"]))
229
+
230
+ if other:
231
+ print(f"\n{len(other)} languages assigned to 'Other' region (need manual override):")
232
+ for bcp47, name in other:
233
+ print(f" {bcp47}: {name}")
234
+ print("\nAdd overrides to LANGUAGE_OVERRIDES in the script and re-run.")
235
+
236
+ output_path = Path(__file__).parent.parent / "langmap" / "langid_mapping.py"
237
+ write_mapping(entries, output_path)
238
+ print(f"\nWrote {len(entries)} entries to {output_path}")
239
+
240
+ if other:
241
+ sys.exit(1)
242
+
243
+
244
+ if __name__ == "__main__":
245
+ main()