mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-09 17:48:19 +00:00
Merge pull request #8804 from furo321/validate-languages-ci
Validate language files for `…` and non-matching formatters in CI
This commit is contained in:
commit
cd3e85f532
2
.github/workflows/style.yml
vendored
2
.github/workflows/style.yml
vendored
|
@ -40,6 +40,8 @@ jobs:
|
||||||
run: scripts/fix_style.py --dry-run
|
run: scripts/fix_style.py --dry-run
|
||||||
- name: Check header guards
|
- name: Check header guards
|
||||||
run: scripts/check_header_guards.py
|
run: scripts/check_header_guards.py
|
||||||
|
- name: Validate Languages
|
||||||
|
run: scripts/languages/validate.py
|
||||||
- name: Check languages
|
- name: Check languages
|
||||||
run: scripts/languages/update_all.py
|
run: scripts/languages/update_all.py
|
||||||
- name: Check dilated images
|
- name: Check dilated images
|
||||||
|
|
|
@ -1027,7 +1027,7 @@ Existing Player
|
||||||
== ﺩﻮﺟﻮﻣ ﺐﻋﻼﻟﺍ
|
== ﺩﻮﺟﻮﻣ ﺐﻋﻼﻟﺍ
|
||||||
|
|
||||||
Your nickname '%s' is already used (%d points). Do you still want to use it?
|
Your nickname '%s' is already used (%d points). Do you still want to use it?
|
||||||
== ﻪﻣﺍﺪﺨﺘﺳﺍ ﺪﻳﺮﺗ ﺖﻟﺯﺎﻣ ﻞﻫ '%s' ﻞﻤﻌﺘﺴﻣ ﻚﻤﺳﺍ
|
==
|
||||||
|
|
||||||
Checking for existing player with your name
|
Checking for existing player with your name
|
||||||
== ﻚﻤﺳﺎﺑ ﺐﻋﻻ ﺩﻮﺟﻭ ﻦﻣ ﻖﻘﺤﺘﻟﺍ
|
== ﻚﻤﺳﺎﺑ ﺐﻋﻻ ﺩﻮﺟﻭ ﻦﻣ ﻖﻘﺤﺘﻟﺍ
|
||||||
|
|
|
@ -1622,7 +1622,7 @@ Go back the specified duration
|
||||||
|
|
||||||
[Demo player duration]
|
[Demo player duration]
|
||||||
%d sec.
|
%d sec.
|
||||||
== % сек.
|
== %d сек.
|
||||||
|
|
||||||
Change the skip duration
|
Change the skip duration
|
||||||
== Змяніць працягласць пропуску
|
== Змяніць працягласць пропуску
|
||||||
|
@ -1765,14 +1765,14 @@ Following
|
||||||
== Бягучыя
|
== Бягучыя
|
||||||
|
|
||||||
Loading commands…
|
Loading commands…
|
||||||
== Загрузка каманд...
|
== Загрузка каманд…
|
||||||
|
|
||||||
[Spectating]
|
[Spectating]
|
||||||
Following %s
|
Following %s
|
||||||
== Назіранне за %s
|
== Назіранне за %s
|
||||||
|
|
||||||
Press a key…
|
Press a key…
|
||||||
== Націсніце клавішу...
|
== Націсніце клавішу…
|
||||||
|
|
||||||
Main menu
|
Main menu
|
||||||
== Галоўнае меню
|
== Галоўнае меню
|
||||||
|
@ -1805,7 +1805,7 @@ Friends
|
||||||
== Сябры
|
== Сябры
|
||||||
|
|
||||||
Loading…
|
Loading…
|
||||||
== Загрузка...
|
== Загрузка…
|
||||||
|
|
||||||
Player info change cooldown
|
Player info change cooldown
|
||||||
== Затрымка абнаўлення інфармацыі пра гульца
|
== Затрымка абнаўлення інфармацыі пра гульца
|
||||||
|
@ -1863,7 +1863,7 @@ Round %d/%d
|
||||||
|
|
||||||
[Spectators]
|
[Spectators]
|
||||||
%d others…
|
%d others…
|
||||||
== %d іншых...
|
== %d іншых…
|
||||||
|
|
||||||
[Team and size]
|
[Team and size]
|
||||||
%d\n(%d/%d)
|
%d\n(%d/%d)
|
||||||
|
|
|
@ -1061,7 +1061,7 @@ Replay
|
||||||
== Visszajátszás
|
== Visszajátszás
|
||||||
|
|
||||||
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
|
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
|
||||||
== Ennek a Textúrának szélessége, vagy magassága (%s) nem megfelelően osztható el ezzel a számmal: (%d), ami vizuális hibákhoz vezethet. (Méretek máshogyan fognak kinézni és a teljesítményt is ronthatja.)
|
==
|
||||||
|
|
||||||
Getting server list from master server
|
Getting server list from master server
|
||||||
== Szerverlista lekérése a fő szerverekről
|
== Szerverlista lekérése a fő szerverekről
|
||||||
|
|
|
@ -1180,7 +1180,7 @@ No server selected
|
||||||
== Nessun server selezionato
|
== Nessun server selezionato
|
||||||
|
|
||||||
Online clanmates (%d)
|
Online clanmates (%d)
|
||||||
== Compagni di clan online
|
== Compagni di clan online (%d)
|
||||||
|
|
||||||
Click to select server. Double click to join your friend.
|
Click to select server. Double click to join your friend.
|
||||||
== Fare click per selezionare il server. Fai doppio click per unirti al tuo amico.
|
== Fare click per selezionare il server. Fai doppio click per unirti al tuo amico.
|
||||||
|
|
|
@ -535,7 +535,7 @@ Replay feature is disabled!
|
||||||
== 리플레이 기능을 비활성화했습니다!
|
== 리플레이 기능을 비활성화했습니다!
|
||||||
|
|
||||||
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
|
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
|
||||||
== 텍스처 %s 의 너비 또는 높이를 %d 으로 나눌 수 없습니다. 이는 시각적 오류를 발생시킬 수 있습니다.
|
==
|
||||||
|
|
||||||
Warning
|
Warning
|
||||||
== 주의
|
== 주의
|
||||||
|
|
|
@ -7,22 +7,6 @@ class LanguageDecodeError(Exception):
|
||||||
error = f"File \"{filename}\", line {line+1}: {message}"
|
error = f"File \"{filename}\", line {line+1}: {message}"
|
||||||
super().__init__(error)
|
super().__init__(error)
|
||||||
|
|
||||||
|
|
||||||
# Taken from https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
|
|
||||||
cfmt = r'''\
|
|
||||||
( # start of capture group 1
|
|
||||||
% # literal "%"
|
|
||||||
(?: # first option
|
|
||||||
(?:[-+0 #]{0,5}) # optional flags
|
|
||||||
(?:\d+|\*)? # width
|
|
||||||
(?:\.(?:\d+|\*))? # precision
|
|
||||||
(?:h|l|ll|w|I|I32|I64)? # size
|
|
||||||
[cCdiouxXeEfgGaAnpsSZ] # type
|
|
||||||
) | # OR
|
|
||||||
%%) # literal "%%"
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def decode(fileobj, elements_per_key):
|
def decode(fileobj, elements_per_key):
|
||||||
data = {}
|
data = {}
|
||||||
current_context = ""
|
current_context = ""
|
||||||
|
@ -45,10 +29,7 @@ def decode(fileobj, elements_per_key):
|
||||||
if len(data[current_key]) >= 1+elements_per_key:
|
if len(data[current_key]) >= 1+elements_per_key:
|
||||||
raise LanguageDecodeError("Wrong number of elements per key", fileobj.name, index)
|
raise LanguageDecodeError("Wrong number of elements per key", fileobj.name, index)
|
||||||
if current_key:
|
if current_key:
|
||||||
original = current_key[0] # pylint: disable=unsubscriptable-object
|
|
||||||
translation = line[3:]
|
translation = line[3:]
|
||||||
if translation and [m.group(1) for m in re.finditer(cfmt, original, flags=re.X)] != [m.group(1) for m in re.finditer(cfmt, translation, flags=re.X)]:
|
|
||||||
raise LanguageDecodeError("Non-matching formatting string", fileobj.name, index)
|
|
||||||
data[current_key].extend([translation])
|
data[current_key].extend([translation])
|
||||||
else:
|
else:
|
||||||
raise LanguageDecodeError("Element before key given", fileobj.name, index)
|
raise LanguageDecodeError("Element before key given", fileobj.name, index)
|
||||||
|
|
54
scripts/languages/validate.py
Executable file
54
scripts/languages/validate.py
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import twlang
|
||||||
|
|
||||||
|
os.chdir(os.path.dirname(__file__) + "/../..")
|
||||||
|
|
||||||
|
# Taken from https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
|
||||||
|
cfmt = '''
|
||||||
|
( # start of capture group 1
|
||||||
|
% # literal "%"
|
||||||
|
(?: # first option
|
||||||
|
(?:[-+0 #]{0,5}) # optional flags
|
||||||
|
(?:\\d+|\\*)? # width
|
||||||
|
(?:\\.(?:\\d+|\\*))? # precision
|
||||||
|
(?:h|l|ll|w|I|I32|I64)? # size
|
||||||
|
[cCdiouxXeEfgGaAnpsSZ] # type
|
||||||
|
) | # OR
|
||||||
|
%%) # literal "%%"
|
||||||
|
'''
|
||||||
|
|
||||||
|
total_errors = 0
|
||||||
|
|
||||||
|
def print_validation_error(error, filename, error_line):
|
||||||
|
print(f"Invalid: {translated}")
|
||||||
|
print(f"- {error} in {filename}:{error_line + 1}\n")
|
||||||
|
global total_errors
|
||||||
|
total_errors += 1
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
languages = sys.argv[1:]
|
||||||
|
else:
|
||||||
|
languages = twlang.languages()
|
||||||
|
local = twlang.localizes()
|
||||||
|
|
||||||
|
for language in languages:
|
||||||
|
translations = twlang.translations(language)
|
||||||
|
|
||||||
|
for (english, _), (line, translated, _) in translations.items():
|
||||||
|
if not translated:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Validate c format strings. Strings that move the formatters are not validated.
|
||||||
|
if re.findall(cfmt, english, flags=re.X) != re.findall(cfmt, translated, flags=re.X) and not "1$" in translated:
|
||||||
|
print_validation_error("Non-matching formatting", language, line)
|
||||||
|
|
||||||
|
# Check for elipisis
|
||||||
|
if "…" in english and "..." in translated:
|
||||||
|
print_validation_error("Usage of ... instead of the … character", language, line)
|
||||||
|
|
||||||
|
if total_errors:
|
||||||
|
print(f"Found {total_errors} {'error' if total_errors == 1 else 'errors'} ")
|
||||||
|
sys.exit(1)
|
Loading…
Reference in a new issue