Skip to content

Commit 1bd8989

Browse files
committed
initial commit
0 parents  commit 1bd8989

File tree

4 files changed

+245
-0
lines changed

4 files changed

+245
-0
lines changed

.github/update.py

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import urllib.request
2+
import re
3+
import os
4+
import json5
5+
import xml.etree.ElementTree as ET
6+
from mdutils.mdutils import MdUtils
7+
import pandas as pd
8+
9+
def get_languages():
10+
with urllib.request.urlopen('https://raw.githubusercontent.com/vcmi/vcmi/develop/lib/Languages.h') as f:
11+
src = f.read().decode('utf-8')
12+
languages = [x for x in re.findall(r"{ ?\"([\w]*)?\" ?,", src, re.IGNORECASE) if "other" not in x]
13+
return languages
14+
15+
def get_base_mod():
16+
return json5.loads(urllib.request.urlopen('https://raw.githubusercontent.com/vcmi/vcmi/develop/Mods/vcmi/mod.json').read())
17+
18+
def base_mod_existing(languages):
19+
vcmi_base_mod = get_base_mod()
20+
return {value:(value in vcmi_base_mod) for value in languages}
21+
22+
def base_mod_ratio(languages):
23+
base_mod = get_base_mod()
24+
translation_english = json5.loads(urllib.request.urlopen('https://raw.githubusercontent.com/vcmi/vcmi/develop/Mods/vcmi/' + base_mod["translations"][0]).read())
25+
26+
data = {}
27+
28+
for language in [key for key, value in base_mod_existing(languages).items() if value == True]:
29+
translation = json5.loads(urllib.request.urlopen('https://raw.githubusercontent.com/vcmi/vcmi/develop/Mods/vcmi/' + next(value for key, value in base_mod.items() if key == language)["translations"][0]).read())
30+
count_equal = 0
31+
count_difference = 0
32+
count_only_english = 0
33+
for key, value in translation_english.items():
34+
if key not in translation:
35+
count_only_english += 1
36+
continue
37+
if translation[key] == value:
38+
count_equal += 1
39+
else:
40+
count_difference += 1
41+
ratio = (count_difference + count_equal) / len(translation_english)
42+
data[language] = {"ratio": ratio, "count_equal": count_equal, "count_difference": count_difference, "count_only_english": count_only_english}
43+
return data
44+
45+
def get_mod_repo():
46+
settings_schema = json5.loads(urllib.request.urlopen("https://raw.githubusercontent.com/vcmi/vcmi/develop/config/schemas/settings.json").read())
47+
vcmi_mod_url = settings_schema["properties"]["launcher"]["properties"]["defaultRepositoryURL"]["default"]
48+
vcmi_mods = json5.loads(urllib.request.urlopen(vcmi_mod_url).read())
49+
return vcmi_mods
50+
51+
def get_translation_mods():
52+
vcmi_translation_mods = {}
53+
54+
vcmi_mods = get_mod_repo()
55+
56+
for key, value in vcmi_mods.items():
57+
url = value["mod"].replace(" ", "%20")
58+
mod = json5.loads(urllib.request.urlopen(url).read())
59+
if "language" in mod:
60+
vcmi_translation_mods[mod["language"]] = (url, mod)
61+
62+
return vcmi_translation_mods
63+
64+
def get_translation_mods_translation():
65+
translation_mods = get_translation_mods()
66+
data = {}
67+
for key, value in translation_mods.items():
68+
tmp = {}
69+
for item in value[1]["translations"]:
70+
base_url = value[0].rsplit('/', 1)[0] + "/content/"
71+
try:
72+
tmp_str = urllib.request.urlopen(base_url + item).read()
73+
except:
74+
tmp_str = urllib.request.urlopen((base_url + item).replace("content", "Content").replace("config", "Config")).read()
75+
tmp |= json5.loads(tmp_str)
76+
data[key] = tmp
77+
return data
78+
79+
def translation_mod_ratio(translation_mods_translation):
80+
translation_english = translation_mods_translation["english"]
81+
82+
data = {}
83+
84+
for language in [key for key, value in translation_mods_translation.items() if key != "english"]:
85+
data_ns = {}
86+
namespaces = [None, "map", "campaign"]
87+
for namespace in namespaces:
88+
translation = translation_mods_translation[language]
89+
count_equal = 0
90+
count_difference = 0
91+
count_only_english = 0
92+
for key, value in translation_english.items():
93+
if key.split(".", 1)[0] == namespace or (namespace == None and key.split(".", 1)[0] not in namespaces):
94+
if key not in translation:
95+
count_only_english += 1
96+
continue
97+
if translation[key] == value:
98+
count_equal += 1
99+
else:
100+
count_difference += 1
101+
ratio = (count_difference + count_equal) / (count_only_english + count_difference + count_equal)
102+
data_ns[namespace] = {"ratio": ratio, "count_equal": count_equal, "count_difference": count_difference, "count_only_english": count_only_english}
103+
data[language] = data_ns
104+
return data
105+
106+
def get_qt_translations(languages):
107+
data = {}
108+
109+
for language in [key for key, value in base_mod_existing(languages).items() if value == True]:
110+
data_type = {}
111+
for type in ["mapeditor", "launcher"]:
112+
count_translated = 0
113+
count_untranslated = 0
114+
tmp_str = urllib.request.urlopen("https://raw.githubusercontent.com/vcmi/vcmi/develop/" + type + "/translation/" + language + ".ts").read()
115+
root = ET.fromstring(tmp_str)
116+
for item_context in root.iter('context'):
117+
for item_message in item_context.iter('message'):
118+
if list(item_message.iter('translation'))[0].get("type") == None:
119+
count_translated += 1
120+
else:
121+
count_untranslated += 1
122+
ratio = (count_translated) / (count_translated + count_untranslated)
123+
data_type[type] = {"ratio": ratio, "count_translated": count_translated, "count_untranslated": count_untranslated}
124+
data[language] = data_type
125+
return data
126+
127+
def get_mod_translations(languages):
128+
vcmi_mods = get_mod_repo()
129+
data = {}
130+
for key, value in vcmi_mods.items():
131+
url = value["mod"].replace(" ", "%20")
132+
mod = json5.loads(urllib.request.urlopen(url).read())
133+
if "language" not in mod:
134+
found_languages = []
135+
for language in languages:
136+
if language in mod:
137+
found_languages.append(language)
138+
data[key] = found_languages
139+
return data
140+
141+
def create_md():
142+
languages = get_languages()
143+
languages_translate = [x for x in languages if x != "english"]
144+
145+
md = MdUtils(file_name='_')
146+
147+
def format_value(percent):
148+
if percent < 0.7:
149+
return "$\\color{red}{\\textsf{" + str(round(percent * 100, 1)) + " \\%" + "}}$"
150+
elif percent < 0.9:
151+
return "$\\color{yellow}{\\textsf{" + str(round(percent * 100, 1)) + " \\%" + "}}$"
152+
else:
153+
return "$\\color{green}{\\textsf{" + str(round(percent * 100, 1)) + " \\%" + "}}$"
154+
155+
md.new_header(level=1, title="VCMI translations")
156+
md.new_line("This tables shows the current translation progress of VCMI. Contains only the state of the translation strings, not for the assets.")
157+
158+
md.new_header(level=2, title="Main translation")
159+
tmp = base_mod_ratio(languages_translate)
160+
df = pd.DataFrame({"Area": "Main-Repo"} | {x:([format_value(tmp[x]["ratio"])] if x in tmp else [format_value(0)]) for x in languages_translate})
161+
tmp = translation_mod_ratio(get_translation_mods_translation())
162+
for area in list(tmp.values())[0].keys():
163+
df = pd.concat([df, pd.DataFrame({"Area": "Mod-Repo" + (' main' if area == None else ' ' + area)} | {x:([format_value(tmp[x][area]["ratio"])] if x in tmp else [format_value(0)]) for x in languages_translate})], ignore_index=True)
164+
df = df.T.reset_index().T
165+
md.new_table(columns=df.shape[1], rows=df.shape[0], text=df.to_numpy().flatten(), text_align='center')
166+
167+
md.new_header(level=2, title="QT tools translation")
168+
tmp = get_qt_translations(languages_translate)
169+
df = pd.DataFrame(columns=["Tool"] + languages_translate)
170+
for tool in list(tmp.values())[0].keys():
171+
df = pd.concat([df, pd.DataFrame({"Tool": tool} | {x:[format_value(tmp[x][tool]["ratio"])] if x in tmp else [format_value(0)] for x in languages_translate})], ignore_index=True)
172+
df = df.T.reset_index().T
173+
md.new_table(columns=df.shape[1], rows=df.shape[0], text=df.to_numpy().flatten(), text_align='center')
174+
175+
md.new_header(level=2, title="Mod translations")
176+
tmp = get_mod_translations(languages_translate)
177+
df = pd.DataFrame(columns=["Mod"] + languages_translate)
178+
for mod in tmp:
179+
df = pd.concat([df, pd.DataFrame({"Mod": mod} | {x:["x" if x in tmp[mod] else ""] for x in languages_translate})], ignore_index=True)
180+
df = df.T.reset_index().T
181+
md.new_table(columns=df.shape[1], rows=df.shape[0], text=df.to_numpy().flatten(), text_align='center')
182+
183+
return md.get_md_text()
184+
185+
if __name__ == "__main__":
186+
with open(os.path.join('.', 'README.md'), "w") as f:
187+
f.write(create_md())

.github/workflows/main.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: VCMI translations status
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
paths:
9+
- "**.py"
10+
schedule:
11+
- cron: '0 2 * * 0'
12+
workflow_dispatch:
13+
14+
jobs:
15+
update_status:
16+
runs-on: ubuntu-latest
17+
defaults:
18+
run:
19+
shell: bash
20+
steps:
21+
- uses: actions/checkout@v3
22+
- name: update status
23+
run: |
24+
python3 -m pip install -r requirements.txt
25+
python3 .github/update.py
26+
- name: Commit changes
27+
uses: EndBug/add-and-commit@v9
28+
with:
29+
default_author: github_actions
30+
message: Update status
31+
add: '*.md'

LICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <https://unlicense.org>

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
json5==0.9.11
2+
pandas==1.5.3
3+
mdutils==1.6.0

0 commit comments

Comments
 (0)