File size: 9,906 Bytes
544091e
 
 
 
9229f26
 
544091e
 
 
 
 
 
50128d8
 
 
 
 
 
 
 
 
 
 
544091e
d178010
 
544091e
 
 
9229f26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544091e
1983d1c
 
 
 
9229f26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544091e
86b8b3a
 
9229f26
 
b909c3a
6b6f157
 
5640508
6b6f157
 
b909c3a
 
 
9229f26
b909c3a
d178010
 
86b8b3a
5640508
b909c3a
6b6f157
b909c3a
86b8b3a
b909c3a
86b8b3a
d178010
 
6b6f157
b909c3a
6b6f157
d178010
86b8b3a
d178010
86b8b3a
d178010
 
6b6f157
d178010
86b8b3a
 
d178010
6b6f157
d178010
 
 
9aa08d7
5640508
d178010
b909c3a
 
 
9229f26
 
0e5691e
50128d8
 
6b6f157
 
 
50128d8
 
 
 
 
 
 
 
5640508
544091e
50128d8
 
 
544091e
 
50128d8
 
 
 
 
 
 
 
 
9229f26
544091e
6b6f157
544091e
0e5691e
 
 
 
 
5640508
6b6f157
5640508
 
 
 
 
86b8b3a
 
5640508
 
 
 
86b8b3a
5640508
 
 
9229f26
5640508
544091e
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<!DOCTYPE html>
<html>

<head>
    <title>Local Language Monitor</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        body {
            margin: 0 auto;
            padding: 20px;
            font-family: sans-serif;
        }

        .language-header {
            margin-bottom: 10px;
        }

        .speaker-count {
            font-size: 0.8em;
            color: #666;
            font-weight: normal;
            margin: 0;
        }
    </style>
    <link rel="icon"
        href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22 fill=%22black%22>🌍</text></svg>">
</head>

<body>
    <nav class="border-b border-gray-200 bg-white">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <!-- Mobile menu button -->
            <div class="sm:hidden absolute left-4 top-4">
                <button onclick="toggleMobileMenu()" class="text-gray-500 hover:text-gray-700 focus:outline-none">
                    <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
                    </svg>
                </button>
            </div>

            <!-- Mobile menu (hidden by default) -->
            <div id="mobileMenu" class="hidden sm:hidden absolute left-0 top-16 w-full bg-white border-b border-gray-200 py-2">
                <div class="flex flex-col space-y-2 px-4">
                    <a href="#" onclick="showSection('coverage'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700">
                        Language Coverage
                    </a>
                    <a href="#" onclick="showSection('comparison'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700">
                        LLM Comparison
                    </a>
                    <a href="#" onclick="showSection('results'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700">
                        Results by Language
                    </a>
                </div>
            </div>

            <!-- Desktop menu -->
            <div class="hidden sm:flex justify-center h-16">
                <div class="flex">
                    <div class="flex space-x-8">
                        <a href="#" onclick="showSection('coverage')" class="nav-link active inline-flex items-center px-1 pt-1 border-b-2 border-indigo-500 text-sm font-medium text-gray-900">
                            Language Coverage
                        </a>
                        <a href="#" onclick="showSection('comparison')" class="nav-link inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700">
                            LLM Comparison
                        </a>
                        <a href="#" onclick="showSection('results')" class="nav-link inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700">
                            Results by Language
                        </a>
                    </div>
                </div>
            </div>
        </div>
    </nav>

    <div class="p-6">
        <section id="coverage" class="section">
            <div id="summary-chart"></div>
        </section>

        <section id="comparison" class="section hidden">
            <p class="text-gray-600">Coming soon...</p>
        </section>

        <section id="results" class="section hidden">
            <div id="language-list"></div>
        </section>
    </div>

    <script type="module">
        // Import Plot using ESM
        import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm";

        function showSection(sectionId) {
            // Update nav links
            document.querySelectorAll('.nav-link').forEach(link => {
                link.classList.remove('border-indigo-500', 'text-gray-900');
                link.classList.add('border-transparent', 'text-gray-500');
            });
            const activeLink = document.querySelector(`[onclick="showSection('${sectionId}')"]`);
            activeLink.classList.remove('border-transparent', 'text-gray-500');
            activeLink.classList.add('border-indigo-500', 'text-gray-900');

            // Show/hide sections
            document.querySelectorAll('.section').forEach(section => {
                section.classList.add('hidden');
            });
            document.getElementById(sectionId).classList.remove('hidden');
        }
        window.showSection = showSection;

        function toggleMobileMenu() {
            const mobileMenu = document.getElementById('mobileMenu');
            mobileMenu.classList.toggle('hidden');
        }
        window.toggleMobileMenu = toggleMobileMenu;

        async function init() {
            const scoreKey = "bleu"
            const scoreName = "BLEU Score"
            const summaryChartDiv = document.getElementById('summary-chart');
            const languageListDiv = document.getElementById('language-list');

            const response = await fetch('results.json');
            const data = await response.json();
            // Format captions
            const formatScore = (score) => score > 0 ? score.toFixed(2) : "No benchmark available!"
            const formatTitle = d => (d.language_name + "\n" + parseInt(d.speakers / 1_000_00) / 10 + "M speakers\n" + scoreName + ": " + formatScore(d[scoreKey]))

            // Create summary plot
            const summaryPlot = Plot.plot({
                width: summaryChartDiv.clientWidth,
                height: 400,
                marginBottom: 100,
                x: { label: "Number of speakers", axis: null },
                y: { label: `${scoreName} (average across models)` },
                // color: { scheme: "BrBG" },
                marks: [
                    Plot.rectY(data, Plot.stackX({
                        x: "speakers",
                        order: scoreKey,
                        reverse: true,
                        y2: scoreKey, // y2 to avoid stacking by y
                        title: formatTitle,
                        tip: true,
                        fill: d => d[scoreKey] > 0 ? "black" : "pink"
                    })),
                    Plot.rectY(data, Plot.pointerX(Plot.stackX({
                        x: "speakers",
                        order: scoreKey,
                        reverse: true,
                        y2: scoreKey, // y2 to avoid stacking by y
                        fill: "grey",
                    }))),
                    Plot.text(data, Plot.stackX({
                        x: "speakers",
                        y2: scoreKey,
                        order: scoreKey,
                        reverse: true,
                        text: "language_name",
                        frameAnchor: "bottom",
                        textAnchor: "end",
                        dy: 10,
                        rotate: 270,
                        opacity: (d) => d.speakers > 50_000_000 ? 1 : 0,
                    }))
                ]
            });

            // Add summary plot to the coverage section
            summaryChartDiv.appendChild(summaryPlot);

            // Get unique languages with their speaker counts
            const languageMap = new Map();
            data.forEach(r => {
                if (!languageMap.has(r.language_name)) {
                    languageMap.set(r.language_name, r.speakers);
                }
            });

            // Sort languages by speaker count (descending)
            const languages = [...languageMap.entries()]
                .sort((a, b) => b[1] - a[1])
                .map(([lang]) => lang);

            // Section for each language
            languages.forEach(language => {
                const headerDiv = document.createElement('div');
                headerDiv.className = 'language-header';

                const h2 = document.createElement('h2');
                h2.textContent = language;
                h2.style.marginBottom = '5px';

                const speakerP = document.createElement('p');
                speakerP.className = 'speaker-count';
                const speakerCount = (languageMap.get(language) / 1_000_000).toFixed(1);
                speakerP.textContent = `${speakerCount}M speakers`;

                headerDiv.appendChild(h2);
                headerDiv.appendChild(speakerP);
                languageListDiv.appendChild(headerDiv);

                const languageData = data.filter(r => r.language_name === language)[0]["scores"];

                const descriptor = code => {
                    let [org, model] = code.split("/")
                    return model.split("-")[0]
                }

                // Plot for how well the models perform on this language
                if (languageData && languageData.length > 1) {
                    const plot = Plot.plot({
                        width: 400,
                        height: 200,
                        margin: 30,
                        y: {
                            domain: [0, 1],
                            label: scoreName
                        },
                        marks: [
                            Plot.barY(languageData, {
                                x: d => descriptor(d.model),
                                y: scoreKey
                            })
                        ]
                    });
                    languageListDiv.appendChild(plot);
                }
            });
        }

        init();
    </script>
</body>

</html>