22from lxml import etree
33import requests
44from requests .adapters import HTTPAdapter
5+ from concurrent .futures import ThreadPoolExecutor
56from urllib3 .util .retry import Retry
67import sys
78import os
9+ import shutil
10+ from pathlib import Path
811
912WEBLATE_URL = "https://hosted.weblate.org"
1013PROJECT_SLUG = "gramophone"
11- COMPONENT_SLUG = "strings-xml"
1214API_TOKEN = os .getenv ("WEBLATE_TOKEN" )
1315headers = {
1416 "Authorization" : f"Token { API_TOKEN } " ,
@@ -26,29 +28,47 @@ session.mount("http://", adapter)
2628session .mount ("https://" , adapter )
2729
2830
29- def get_languages () -> list :
30- url = f"{ WEBLATE_URL } /api/components/{ PROJECT_SLUG } /{ COMPONENT_SLUG } /translations/"
31+ def get_languages (component_slug : str ) -> list :
32+ url = f"{ WEBLATE_URL } /api/components/{ PROJECT_SLUG } /{ component_slug } /translations/"
3133 response = session .get (url , headers = headers , timeout = 5 )
3234 response .raise_for_status () # Raise an error for bad responses
3335 languages = response .json ().get ('results' )
3436 return languages
3537
3638
39+ def get_pages_units (page : int , language_code : str ) -> list :
40+ url = f"{
41+ WEBLATE_URL } /api/translations/{ PROJECT_SLUG } /strings-xml/{ language_code } /units/"
42+ response = session .get (url , headers = headers ,
43+ timeout = 5 , params = {'page' : page })
44+ response .raise_for_status ()
45+ data = response .json ()
46+ return data .get ("results" )
47+
48+
3749# Fetch all units
38- def get_units_list (language_code : str ) -> list :
50+ def get_units_list (language_code : str , component_slug : str ) -> list :
3951 units = []
40- page_url = f"{
41- WEBLATE_URL } /api/translations/{ PROJECT_SLUG } /{ COMPONENT_SLUG } /{ language_code } /units/"
52+ url = f"{
53+ WEBLATE_URL } /api/translations/{ PROJECT_SLUG } /{ component_slug } /{ language_code } /units/"
4254
4355 print (f'\n Getting units of language code "{ language_code } "... ' , end = "" )
44- while page_url :
45- response = session .get (page_url , headers = headers , timeout = 5 )
46- response .raise_for_status ()
47- data = response .json ()
48- units .extend (data ["results" ])
49- page_url = data .get ("next" )
50- print ("Done." )
56+ response = session .get (url , headers = headers , timeout = 5 )
57+ response .raise_for_status ()
58+ data = response .json ()
59+ units .extend (data .get ("results" ))
5160
61+ count = data .get ("count" )
62+ if count > 50 :
63+ page_params = [{'page' : i , 'language_code' : language_code }
64+ for i in range (2 , count // 50 + 2 )]
65+ with ThreadPoolExecutor (max_workers = 10 ) as executor :
66+ futures = [executor .submit (get_pages_units , ** params ) for params in page_params ]
67+
68+ for future in futures :
69+ units .extend (future .result ())
70+
71+ print ("Done." )
5272 return units
5373
5474
@@ -66,7 +86,8 @@ def delete_strings(xml_file, names_to_delete):
6686 resolve_entities = False
6787 )
6888
69- tree = etree .parse (sys .argv [1 ] + '/' + xml_file , parser )
89+ source_file_path = Path (sys .argv [1 ] + "/" + str (xml_file ))
90+ tree = etree .parse (source_file_path , parser )
7091 root = tree .getroot ()
7192
7293 # Find all string elements
@@ -91,19 +112,68 @@ def delete_strings(xml_file, names_to_delete):
91112 )
92113
93114
94- def filter_translations ( language : dict ) -> None :
95- units = get_units_list (language . get ( 'language' ). get ( 'code' ) )
115+ def filter_editings ( language_code : dict , component_slug : str ) -> list :
116+ units = get_units_list (language_code , component_slug )
96117 string_names = [unit .get ('context' )
97118 for unit in units if unit .get ('state' ) == 10 ]
98119 print (" Strings to delete: " + str (string_names ))
120+ return string_names
99121
100- delete_strings (language .get ("filename" ), string_names )
101122
123+ def manage_fastlane_files (files_path : Path , editings : list ) -> None :
124+ source_files_path = Path (sys .argv [1 ] + "/" + files_path )
125+ if not source_files_path .exists ():
126+ print (f" Directory does not exist: { source_files_path } " )
127+ return
102128
103- if __name__ == "__main__" :
104- languages = get_languages ()
129+ if sys .argv [1 ] != os .getcwd ():
130+ shutil .rmtree (files_path )
131+ shutil .copytree (source_files_path , files_path )
105132
133+ files = ["short_description.txt" , "full_description.txt" , "title.txt" ]
134+ delete_files = any ("description" in s for s in editings )
135+ # true if a file has "description" in its name
136+ if delete_files :
137+ print (f" Deleting files in { files_path } " )
138+ for file in files :
139+ file_path = Path (files_path + "/" + file )
140+ if file_path .exists ():
141+ os .remove (file_path )
142+
143+ # if delete_changelogs:
144+ # changelogs_dir = Path(files_path + "/changelogs/")
145+ # if changelogs_dir.exists() and changelogs_dir.is_dir():
146+ # print(f" Deleting changelog {changelogs_dir}")
147+ # shutil.rmtree(changelogs_dir)
148+
149+ if not delete_files :
150+ print (" Skipped deleting" )
151+
152+
153+ def filter_strings_xml ():
154+ languages = get_languages ("strings-xml" )
106155 for language in languages :
107- if not language .get ('is_source' ):
108- filter_translations (language )
156+ if language .get ('is_source' ):
157+ continue
158+ language_code = language .get ('language' ).get ('code' )
159+ files_path = Path (language .get ('filename' ))
160+ editings = filter_editings (language_code , "strings-xml" )
161+ delete_strings (files_path , editings )
109162
163+
164+ def filter_fastlane ():
165+ fastlane_languages = get_languages ('fastlane' )
166+ for language in fastlane_languages :
167+ if language .get ("is_source" ):
168+ continue
169+ language_code = language .get ('language' ).get ('code' )
170+ files_path = language .get ('filename' )
171+ editings = filter_editings (language_code , "fastlane" )
172+ manage_fastlane_files (files_path , editings )
173+
174+
175+ if __name__ == "__main__" :
176+ print ("Filtering strings-xml..." )
177+ filter_strings_xml ()
178+ print ("Filtering fastlane..." )
179+ filter_fastlane ()
0 commit comments