22import os
33import xml .etree .ElementTree as ET
44import polib
5+ import langid
56
7+ def normalize (s : str | None ) -> str :
8+ return " " .join ((s or "" ).strip ().lower ().split ())
69
7- def normalize (s : str ) -> str :
8- # Normalize strings for reliable comparison (trim, lowercase, collapse spaces)
9- return " " .join ((s or "" ).strip ().lower ().split ())
10+ # -----------------------------
1011
12+ # PO CHECK
1113
1214# -----------------------------
13- # PO FILE CHECK
14- # -----------------------------
15- def checkPo (path : str ) -> float :
16- # Parse PO file using polib
17- po = polib .pofile (path )
1815
19- translated = 0
20- total = 0
16+ def checkPo ( path : str ) -> float :
17+ po = polib . pofile ( path )
2118
22- for entry in po :
23- # Skip empty msgid entries
24- if not entry .msgid .strip ():
25- continue
19+ `` `
20+ translated = 0
21+ total = 0
2622
27- total += 1
23+ for entry in po :
24+ if not entry .msgid .strip ():
25+ continue
2826
29- # Consider entry translated only if msgstr differs from msgid
30- if entry .msgstr and normalize (entry .msgstr ) != normalize (entry .msgid ):
31- translated += 1
27+ total += 1
3228
33- return translated / total if total else 0.0
29+ if entry .msgstr and normalize (entry .msgstr ) != normalize (entry .msgid ):
30+ translated += 1
3431
32+ return translated / total if total else 0.0
33+ `` `
3534
3635# -----------------------------
37- # XLIFF CHECK (skeleton-safe generic parsing)
36+
37+ # XLIFF CHECK
38+
3839# -----------------------------
40+
3941def checkXliff (path : str ) -> float :
40- # Parse XML XLIFF file
41- tree = ET .parse (path )
42- root = tree .getroot ()
42+ tree = ET .parse (path )
43+ root = tree .getroot ()
44+
45+ `` `
46+ translated = 0
47+ total = 0
48+ source = None
49+
50+ for elem in root .iter ():
51+ if elem .tag .endswith ("source" ):
52+ source = normalize (elem .text )
53+
54+ elif elem .tag .endswith ("target" ):
55+ target = normalize (elem .text )
56+
57+ if source :
58+ total += 1
59+ if target and target != source :
60+ translated += 1
4361
44- translated = 0
45- total = 0
62+ return translated / total if total else 0. 0
63+ `` `
4664
47- source = None
65+ # -----------------------------
4866
49- for elem in root . iter ():
67+ # MD LANGUAGE SCORE (langid)
5068
51- # Capture source segments
52- if elem .tag .endswith ("source" ):
53- source = normalize (elem .text )
69+ # -----------------------------
5470
55- # Compare with target segments
56- elif elem .tag .endswith ("target" ):
57- target = normalize (elem .text )
71+ def scoreMd (path : str , expected_lang : str ) -> float :
72+ try :
73+ with open (path , "r" , encoding = "utf-8" ) as f :
74+ text = f .read ()
75+ except Exception :
76+ return 0.0
5877
59- if source :
60- total += 1
78+ `` `
79+ if not text .strip ():
80+ return 0.0
6181
62- # Count as translated only if target differs from source
63- if target and target != source :
64- translated += 1
82+ lang , score = langid .classify (text )
6583
66- return translated / total if total else 0.0
84+ # Normalize score into positive confidence
85+ confidence = 1 / (1 + abs (score ))
6786
87+ if lang == expected_lang :
88+ return confidence
89+ else :
90+ return 0.0
91+ `` `
6892
6993# -----------------------------
70- # MAIN ENTRY POINT
94+
95+ # COMPARE MULTIPLE MD FILES
96+
7197# -----------------------------
72- def main ():
73- if len (sys .argv ) < 2 :
74- print ("Usage: checkTranslation.py <file>" )
75- sys .exit (2 )
7698
77- path = sys .argv [1 ]
99+ def compareMd (files : list [str ], lang : str ):
100+ results = []
78101
79- if not os .path .exists (path ):
80- print (f"File not found: { path } " )
81- sys .exit (2 )
102+ `` `
103+ for f in files :
104+ if not os .path .exists (f ):
105+ continue
82106
83- ext = os .path .splitext (path )[1 ].lower ()
107+ score = scoreMd (f , lang )
108+ results .append ((f , score ))
84109
85- # Dispatch based on file type
86- if ext == ".po" :
87- ratio = checkPo ( path )
110+ if not results :
111+ print ( "winner=None" )
112+ sys . exit ( 1 )
88113
89- elif ext in [".xliff" , ".xlf" ]:
90- ratio = checkXliff (path )
114+ results .sort (key = lambda x : x [1 ], reverse = True )
91115
92- else :
93- print (f"Unsupported file type: { ext } " )
94- sys .exit (2 )
116+ winner = results [0 ]
95117
96- print (f"translation_ratio={ ratio } " )
118+ print ("comparison_results:" )
119+ for f , s in results :
120+ print (f"{ f } ={ s } " )
97121
98- # Threshold: consider file translated if above 5%
99- if ratio > 0.05 :
100- sys .exit (0 ) # translated
101- else :
102- sys .exit (1 ) # not translated
122+ print (f"winner={ winner [0 ]} " )
123+ print (f"winner_score={ winner [1 ]} " )
103124
125+ sys .exit (0 )
126+ `` `
104127
105- if __name__ == "__main__" :
106- main ()
128+ # -----------------------------
129+
130+ # MAIN
131+
132+ # -----------------------------
133+
134+ def main ():
135+ if len (sys .argv ) < 2 :
136+ print ("Usage:" )
137+ print (" checkTranslation.py <file>" )
138+ print (" checkTranslation.py <file> <lang>" )
139+ print (" checkTranslation.py <file1> <file2> [...] <lang>" )
140+ sys .exit (2 )
141+
142+ `` `
143+ args = sys .argv [1 :]
144+
145+ # -------------------------
146+ # MULTI FILE MODE
147+ # -------------------------
148+ if len (args ) >= 3 :
149+ * files , lang = args
150+ compareMd (files , lang )
151+ return
152+
153+ path = args [0 ]
154+
155+ if not os .path .exists (path ):
156+ print (f"File not found: { path } " )
157+ sys .exit (2 )
158+
159+ ext = os .path .splitext (path )[1 ].lower ()
160+
161+ # -------------------------
162+ # PO
163+ # -------------------------
164+ if ext == ".po" :
165+ ratio = checkPo (path )
166+ print (f"translation_ratio={ ratio } " )
167+ sys .exit (0 if ratio > 0.05 else 1 )
168+
169+ # -------------------------
170+ # XLIFF
171+ # -------------------------
172+ elif ext in [".xliff" , ".xlf" ]:
173+ ratio = checkXliff (path )
174+ print (f"translation_ratio={ ratio } " )
175+ sys .exit (0 if ratio > 0.05 else 1 )
176+
177+ # -------------------------
178+ # MD (LANG SCORE)
179+ # -------------------------
180+ elif ext == ".md" :
181+
182+ if len (args ) < 2 :
183+ print ("Missing language argument for MD scoring" )
184+ sys .exit (2 )
185+
186+ lang = args [1 ]
187+ score = scoreMd (path , lang )
188+
189+ print (f"md_score={ score } " )
190+
191+ sys .exit (0 )
192+
193+ else :
194+ print (f"Unsupported file type: { ext } " )
195+ sys .exit (2 )
196+ `` `
197+
198+ if * * name ** == "**main**" :
199+ main ()
0 commit comments