1
- from pathlib import Path
2
1
import json
3
- from typing import TYPE_CHECKING , TypeAlias , TypedDict , Annotated as A
2
+ from pathlib import Path
3
+ from typing import TYPE_CHECKING , TypeAlias , TypedDict
4
+ from typing import Annotated as A
4
5
6
+ import numpy as np
7
+ import plotly .graph_objects as go
5
8
from babel .messages import pofile
6
9
from docutils import nodes
7
10
from docutils .parsers .rst import Directive
8
- import plotly .graph_objects as go
9
11
from plotly .offline import plot
10
- import numpy as np
11
12
12
13
if TYPE_CHECKING :
13
14
from sphinx .application import Sphinx
17
18
LOCALES_DIR = BASE_DIR / "locales" # Locales directory
18
19
STATIC_DIR = BASE_DIR / "_static" # Static directory
19
20
21
+
20
22
class ModuleStats (TypedDict ):
21
23
total : int
22
24
translated : int
23
25
fuzzy : int
24
26
untranslated : int
25
27
percentage : float
26
28
27
- TranslationStats : TypeAlias = dict [A [str , "locale" ], dict [A [str , "module" ], ModuleStats ]]
29
+
30
+ TranslationStats : TypeAlias = dict [
31
+ A [str , "locale" ], dict [A [str , "module" ], ModuleStats ]
32
+ ]
28
33
29
34
30
35
class TranslationGraph (Directive ):
@@ -42,19 +47,39 @@ class TranslationGraph(Directive):
42
47
Total: %{customdata.total}<br>
43
48
Completed: %{customdata.percentage}%
44
49
"""
50
+
45
51
def run (self ):
46
52
data = get_translation_stats ()
47
53
48
54
# Sort data by locale and module
49
- data = {locale : dict (sorted (loc_stats .items ())) for locale , loc_stats in sorted (data .items ())}
55
+ data = {
56
+ locale : dict (sorted (loc_stats .items ()))
57
+ for locale , loc_stats in sorted (data .items ())
58
+ }
50
59
51
60
# prepend english, everything set to 100%
52
- en = {module : ModuleStats (total = stats ['total' ], translated = stats ['total' ], fuzzy = stats ['total' ], untranslated = 0 , percentage = 100 ) for module , stats in next (iter (data .values ())).items ()}
53
- data = {'en' : en } | data
61
+ en = {
62
+ module : ModuleStats (
63
+ total = stats ["total" ],
64
+ translated = stats ["total" ],
65
+ fuzzy = stats ["total" ],
66
+ untranslated = 0 ,
67
+ percentage = 100 ,
68
+ )
69
+ for module , stats in next (iter (data .values ())).items ()
70
+ }
71
+ data = {"en" : en } | data
54
72
55
73
# Calculate average completion percentage for each locale and sort locales
56
- locale_completion = {locale : np .mean ([stats ['percentage' ] for stats in loc_stats .values ()]) for locale , loc_stats in data .items ()}
57
- sorted_locales = sorted (locale_completion .keys (), key = lambda locale : locale_completion [locale ], reverse = True )
74
+ locale_completion = {
75
+ locale : np .mean ([stats ["percentage" ] for stats in loc_stats .values ()])
76
+ for locale , loc_stats in data .items ()
77
+ }
78
+ sorted_locales = sorted (
79
+ locale_completion .keys (),
80
+ key = lambda locale : locale_completion [locale ],
81
+ reverse = True ,
82
+ )
58
83
59
84
# Reorder data based on sorted locales
60
85
data = {locale : data [locale ] for locale in sorted_locales }
@@ -64,11 +89,20 @@ def run(self):
64
89
modules = list (next (iter (data .values ())).keys ())
65
90
66
91
# Extract data to plot
67
- values = [[stats ['percentage' ] for stats in loc_stats .values ()] for loc_stats in data .values ()]
68
- hoverdata = [[{'module' : module } | stats for module , stats in loc_stats .items ()] for loc_stats in data .values ()]
92
+ values = [
93
+ [stats ["percentage" ] for stats in loc_stats .values ()]
94
+ for loc_stats in data .values ()
95
+ ]
96
+ hoverdata = [
97
+ [{"module" : module } | stats for module , stats in loc_stats .items ()]
98
+ for loc_stats in data .values ()
99
+ ]
69
100
70
101
# Add text to display percentages directly in the heatmap boxes
71
- text = [[f"{ int (stats ['percentage' ])} %" for stats in loc_stats .values ()] for loc_stats in data .values ()]
102
+ text = [
103
+ [f"{ int (stats ['percentage' ])} %" for stats in loc_stats .values ()]
104
+ for loc_stats in data .values ()
105
+ ]
72
106
73
107
heatmap = go .Heatmap (
74
108
x = modules ,
@@ -83,23 +117,28 @@ def run(self):
83
117
hovertemplate = self .HOVER_TEMPLATE ,
84
118
name = "" , # Set the trace name to an empty string to remove "trace 0" from hoverbox
85
119
colorbar = {
86
- ' orientation' : 'h' ,
87
- 'y' : 0 ,
120
+ " orientation" : "h" ,
121
+ "y" : 0 ,
88
122
"yanchor" : "bottom" ,
89
123
"yref" : "container" ,
90
124
"title" : "Completion %" ,
91
125
"thickness" : 10 ,
92
126
"tickvals" : [12.5 , 50 , 87.5 , 100 ], # Midpoints for each category
93
- "ticktext" : ["0-25%" , "25-75%" , "75-<100%" , "100%" ], # Labels for categories
127
+ "ticktext" : [
128
+ "0-25%" ,
129
+ "25-75%" ,
130
+ "75-<100%" ,
131
+ "100%" ,
132
+ ], # Labels for categories
94
133
},
95
134
colorscale = [
96
- [0.0 , "rgb(254, 255, 231)" ], # 0-25%
135
+ [0.0 , "rgb(254, 255, 231)" ], # 0-25%
97
136
[0.25 , "rgb(254, 255, 231)" ],
98
- [0.25 , "rgb(187, 130, 176)" ], # 25-75%
137
+ [0.25 , "rgb(187, 130, 176)" ], # 25-75%
99
138
[0.75 , "rgb(187, 130, 176)" ],
100
- [0.75 , "rgb(129, 192, 170)" ], # 75-<100%
139
+ [0.75 , "rgb(129, 192, 170)" ], # 75-<100%
101
140
[0.99 , "rgb(129, 192, 170)" ],
102
- [1.0 , "rgb(78, 112, 100)" ], # 100%
141
+ [1.0 , "rgb(78, 112, 100)" ], # 100%
103
142
],
104
143
)
105
144
# Create figure
@@ -112,7 +151,7 @@ def run(self):
112
151
xaxis_showgrid = False ,
113
152
xaxis_side = "top" ,
114
153
xaxis_tickangle = - 45 ,
115
- xaxis_tickfont = {
154
+ xaxis_tickfont = {
116
155
"family" : "var(--bs-font-monospace)" ,
117
156
},
118
157
yaxis_showgrid = False ,
@@ -127,7 +166,8 @@ def run(self):
127
166
)
128
167
return [nodes .raw ("" , div , format = "html" )]
129
168
130
- def calculate_translation_percentage (po_path : Path , locale : str ) -> ModuleStats :
169
+
170
+ def calculate_translation_percentage (po_path : Path , locale : str ) -> ModuleStats :
131
171
"""
132
172
Calculate the translation percentage for a given .po file.
133
173
@@ -170,7 +210,7 @@ def calculate_translation_percentage(po_path : Path, locale : str) -> ModuleStat
170
210
"translated" : translated ,
171
211
"fuzzy" : fuzzy ,
172
212
"untranslated" : total - translated - fuzzy ,
173
- "percentage" : round (percentage , 2 )
213
+ "percentage" : round (percentage , 2 ),
174
214
}
175
215
176
216
@@ -218,10 +258,16 @@ def get_translation_stats() -> TranslationStats:
218
258
219
259
return results
220
260
261
+
221
262
def write_translation_stats (app : "Sphinx" , exception : Exception | None ) -> None :
222
263
from sphinx .util import logging
264
+
223
265
logger = logging .getLogger ("_ext.translation_graph" )
224
266
267
+ if app .builder .name != "html" :
268
+ logger .info ("Skipping translation stats for non-HTML build" )
269
+ return
270
+
225
271
stats = get_translation_stats ()
226
272
out_path = app .outdir / "_static" / "translation_stats.json"
227
273
with open (out_path , "w" ) as f :
0 commit comments