push_translations.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import os
  2. import json
  3. import re
  4. import subprocess
  5. import xml.etree.ElementTree as ET
  6. import requests
  7. # 可配置支持的语言
  8. SUPPORTED_LANGUAGES = [
  9. "zh", "en", "ar"
  10. ]
  11. TOLGEE_API_URL = "http://47.236.31.243:8090/api"
  12. TOLGEE_API_KEY = "tgpak_hbpxgyltnryguy3lonyxiyrtgruta4rroryhiolooe3q"
  13. PROJECT_ID = "8"
  14. def find_strings_files(root_dir="."):
  15. result = []
  16. for root, dirs, files in os.walk(root_dir):
  17. normalized_root = root.replace("\\", "/")
  18. if "lite" in normalized_root or "lite" in files:
  19. continue
  20. if "/build/" in normalized_root or normalized_root.endswith("/build"):
  21. continue
  22. if "strings.xml" in files and "/src/" in normalized_root:
  23. full_path = os.path.join(root, "strings.xml")
  24. rel_path = os.path.relpath(full_path, root_dir).replace("\\", "/")
  25. result.append({
  26. "path": rel_path
  27. })
  28. return result
  29. def extract_language_from_path(path):
  30. """
  31. 从 Android 的 values[-xx[-rYY]] 路径中提取语言代码。
  32. - 默认返回 'en'
  33. - 特殊处理:'in' → 'id'
  34. """
  35. match = re.search(r'values(?:-([a-zA-Z-]+))?/', path)
  36. if match:
  37. lang = match.group(1)
  38. if not lang:
  39. return "en"
  40. lang = lang.replace("-r", "-")
  41. if lang == "in":
  42. return "id"
  43. return lang
  44. return "en"
  45. def generate_tolgee_config():
  46. files = find_strings_files(".")
  47. push_files = []
  48. for f in files:
  49. lang = extract_language_from_path(f["path"])
  50. if lang in SUPPORTED_LANGUAGES:
  51. push_files.append({
  52. "path": f["path"],
  53. "language": lang
  54. })
  55. config = {
  56. "apiUrl": TOLGEE_API_URL,
  57. "apiKey": TOLGEE_API_KEY,
  58. "projectId": PROJECT_ID,
  59. "format": "ANDROID_XML",
  60. "convertPlaceholdersTo": "icu",
  61. "structureDelimiter": ".",
  62. "push": {
  63. "files": push_files
  64. }
  65. }
  66. with open(".tolgeerc", "w", encoding="utf-8") as f:
  67. json.dump(config, f, indent=2)
  68. print(f"✅ Tolgee 配置已生成,共 {len(push_files)} 个语言文件")
  69. def run_tolgee_push():
  70. print("🚀 正在执行 tolgee push ...")
  71. try:
  72. subprocess.run(["tolgee", "push", "--force-mode", "KEEP","--verbose"], check=True)
  73. print("✅ 推送成功")
  74. except subprocess.CalledProcessError as e:
  75. print("❌ 推送失败", e)
  76. HEADERS = {
  77. "X-Api-Key": TOLGEE_API_KEY,
  78. "Content-Type": "application/json"
  79. }
  80. def fetch_all_keys():
  81. dataList = [] #key是name,value是id
  82. page = 0
  83. page_size = 1000
  84. while True:
  85. url = f"{TOLGEE_API_URL}/v2/projects/{PROJECT_ID}/keys?page={page}&size={page_size}"
  86. r = requests.get(url, headers=HEADERS)
  87. if r.status_code != 200:
  88. print(f"❌ 拉取失败: {r.status_code} -> {r.text}")
  89. break
  90. data = r.json()
  91. # print(data)
  92. dataList.extend(data['_embedded']['keys'])
  93. if data['page']['totalPages'] == page +1 : # 最后一页
  94. break
  95. page += 1
  96. print(f"✅ 已拉取 {len(dataList)} 个 Tolgee keys")
  97. # print(dataList)
  98. return dataList
  99. def find_translatable_false_keys():
  100. result = []
  101. for root, dirs, files in os.walk("."):
  102. normalized_root = root.replace("\\", "/")
  103. if normalized_root.endswith("/res/values") and "strings.xml" in files:
  104. file_path = os.path.join(root, "strings.xml")
  105. try:
  106. tree = ET.parse(file_path)
  107. root_elem = tree.getroot()
  108. for elem in root_elem.findall("string"):
  109. if elem.attrib.get("translatable") == "false":
  110. result.append(elem.attrib["name"])
  111. except Exception as e:
  112. print(f"⚠️ 解析失败:{file_path} -> {e}")
  113. print(f"🔍 找到 {len(result)} 个 translatable=false 的 key")
  114. # print(result)
  115. return result
  116. def get_key_id_by_name(key_name):
  117. url = f"{TOLGEE_API_URL}/v2/projects/{PROJECT_ID}/keys?search={key_name}"
  118. try:
  119. resp = requests.get(url, headers=HEADERS)
  120. data = resp.json()
  121. print(f"🔍 查询 key ID:{key_name} -> {data}")
  122. if isinstance(data, list) and data:
  123. return data[0]["id"]
  124. except Exception as e:
  125. print(f"❌ 查询 key ID 失败:{key_name} -> {e}")
  126. return None
  127. def fetch_language_ids():
  128. print("📥 拉取 Tolgee 语言信息...")
  129. url = f"{TOLGEE_API_URL}/v2/projects/{PROJECT_ID}/languages"
  130. r = requests.get(url, headers=HEADERS)
  131. if not r.ok:
  132. print(f"❌ 获取 languages 失败:{r.status_code} -> {r.text}")
  133. return []
  134. languages = r.json()['_embedded']['languages']
  135. print(languages)
  136. # result = {}
  137. # for lang in languages:
  138. # result[lang["tag"]] = lang["id"]
  139. # print(f"✅ 拉取语言: {result}")
  140. return {lang["tag"]: lang["id"] for lang in languages}
  141. def mark_keys_as_disabled(keys: list):
  142. print(f"🔧 准备禁用 {len(keys)} 个不可翻译的 key")
  143. all_keys = fetch_all_keys()
  144. all_languages = fetch_language_ids()
  145. disabled_lang_ids = [
  146. lang_id for tag, lang_id in all_languages.items() if tag != 'en'
  147. ]
  148. print(f"🔍 禁用语言 ID: {disabled_lang_ids}")
  149. key_map = {item["name"]: item["id"] for item in all_keys}
  150. for key in keys:
  151. key_id = key_map.get(key)
  152. if not key_id:
  153. print(f"⚠️ 跳过:{key},未找到 keyId")
  154. continue
  155. url = f"{TOLGEE_API_URL}/v2/projects/{PROJECT_ID}/keys/{key_id}/disabled-languages"
  156. payload = json.dumps({
  157. "languageIds": disabled_lang_ids
  158. })
  159. try:
  160. resp = requests.request("PUT", url, headers=HEADERS, data=payload)
  161. if resp.status_code == 200:
  162. print(f"✅ 已禁用:{key}")
  163. else:
  164. print(f"❌ 禁用失败:{key} -> {resp.status_code}: {resp.text}")
  165. except Exception as e:
  166. print(f"❌ 异常:{key} -> {e}")
  167. def main():
  168. generate_tolgee_config()
  169. run_tolgee_push()
  170. # 处理 translatable=false 的 key
  171. false_keys = find_translatable_false_keys()
  172. if false_keys:
  173. mark_keys_as_disabled(false_keys)
  174. if __name__ == "__main__":
  175. main()