From 0dc631f5ec9638d49598b11753b15274d9c2e258 Mon Sep 17 00:00:00 2001 From: Julius Jacobitz <47418007+JuliusJacobitz@users.noreply.github.com> Date: Wed, 9 Jun 2021 16:36:30 +0200 Subject: [PATCH 01/26] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 27498f3a..1f88a213 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,7 @@ werden (von uns oder euch - feel free!) irgendwann hinzukommen: - [ ] Datum eingrenzen bei der Terminwahl - [ ] Github Pages +- [ ] Integrierter updater. - [ ] Macosx Build / Pipeline (Mac currently blocks the app: [Branch](https://github.com/iamnotturner/vaccipy/tree/mac-intel-build)) - [ ] Code Zertifikate für Windows (gegen Virusmeldung) From f56785d7e74bf66682224a783e93e75d1a361922 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Mon, 7 Jun 2021 15:24:40 +0200 Subject: [PATCH 02/26] Add translation files for en and de --- main.py | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/main.py b/main.py index 4e2fa184..bb0293c3 100755 --- a/main.py +++ b/main.py @@ -8,6 +8,8 @@ import string import sys +import i18n + from tools.its import ImpfterminService from tools.kontaktdaten import decode_wochentag, encode_wochentag, get_kontaktdaten, validate_kontaktdaten, validate_datum from tools.utils import create_missing_dirs, get_latest_version, remove_prefix, update_available, get_current_version @@ -371,31 +373,31 @@ def main(): base_subparser.add_argument( "-f", "--file", - help="Pfad zur JSON-Datei für Kontaktdaten") + help=i18n.t("i18n.PathContactData")) base_subparser.add_argument( "-c", "--configure-only", action='store_true', - help="Nur Kontaktdaten erfassen und in JSON-Datei abspeichern") + help=i18n.t("i18n.ConfigureOnlyDescription")) base_subparser.add_argument( "-r", "--read-only", action='store_true', - help="Es wird nicht nach fehlenden Kontaktdaten gefragt. Stattdessen wird ein Fehler angezeigt, falls benötigte Kontaktdaten in der JSON-Datei fehlen.") + help=i18n.t("i18n.ReadOnlyDescription")) parser_search = subparsers.add_parser( - "search", parents=[base_subparser], help="Termin suchen") + "search", parents=[base_subparser], help=i18n.t("i18n.SearchForAppointment")) parser_search.add_argument( "-s", "--retry-sec", type=int, default=60, - help="Wartezeit zwischen zwei Versuchen (in Sekunden)") + help=i18n.t("i18n.RetrySecDescription")) parser_code = subparsers.add_parser( "code", parents=[base_subparser], - help="Impf-Code generieren") + help=i18n.t("i18n.GenerateVacCode")) args = parser.parse_args() @@ -423,22 +425,22 @@ def main(): else: assert False except ValidationError as exc: - print(f"Fehler in {json.dumps(args.file)}:\n{str(exc)}") + print(i18n.t("i18n.ErrorIn") + f" {json.dumps(args.file)}:\n{str(exc)}") else: extended_settings = False while True: print( - "Was möchtest du tun?\n" - "[1] Termin suchen\n" - "[2] Impf-Code generieren\n" - f"[x] Erweiterte Einstellungen {'verbergen' if extended_settings else 'anzeigen'}\n") + i18n.t("Menu") + "?\n" + "[1] " + i18n.t("i18n.SearchForAppointment") + "\n" + "[2] " + i18n.t("i18n.GenerateVacCode") + "\n" + f"[x] {i18n.t('i18n.HideAdvancedSettings') if extended_settings else i18n.t('i18n.ShowAdvancedSettings')}\n") if extended_settings: print( - f"[c] --configure-only {'de' if args.configure_only else ''}aktivieren\n" - f"[r] --read-only {'de' if args.read_only else ''}aktivieren\n" + f"[c] --configure-only {'de' if args.configure_only else ''}" + i18n.t("i18n.activate") + "\n" + f"[r] --read-only {'de' if args.read_only else ''}" + i18n.t("i18n.activate") + "\n" "[s] --retry-sec setzen\n") option = input("> Option: ").lower() @@ -457,21 +459,21 @@ def main(): validate_args(new_args) args = new_args print( - f"--configure-only {'de' if not args.configure_only else ''}aktiviert.") + f"--configure-only {'de' if not args.configure_only else ''}" + i18n.t("i18n.activate") + ".") elif extended_settings and option == "r": new_args = copy.copy(args) new_args.read_only = not new_args.read_only validate_args(new_args) args = new_args print( - f"--read-only {'de' if not args.read_only else ''}aktiviert.") + f"--read-only {'de' if not args.read_only else ''}" + i18n.t("i18n.activate") + ".") elif extended_settings and option == "s": args.retry_sec = int(input("> --retry-sec=")) else: - print("Falscheingabe! Bitte erneut versuchen.") + print(i18n.t("i18n.InvalidInputPleaseTryAgain") + ".") print() except Exception as exc: - print(f"\nFehler:\n{str(exc)}\n") + print(f"\n" + i18n.t("Error") + ":\n{str(exc)}\n") if __name__ == "__main__": @@ -485,22 +487,26 @@ def main(): | | __/ | |_| |___/ """) + # Lade Sprachen + i18n.load_path.append(os.path.join(PATH, "i18n")) + i18n.set('fallback', 'de') + print(i18n.t("i18n.test")) # Auf aktuelle Version prüfen try: if not update_available(): - print('Du verwendest die aktuellste Version von vaccipy: ' + get_current_version()) + print(i18n.t("i18n.YouAreUsingTheLatestVersionOfVaccipy") + ': ' + get_current_version()) else: - print("Du verwendest eine alte Version von vaccipy.\n" - "Bitte installiere die aktuellste Version. Link zum Download:\n" + print(i18n.t("YouAreUsingAnOldVersionOfVaccipy") + "\n" + + i18n.t("PleaseInstallTheLastestVersionLink") + ":\n" + "https://github.com/iamnotturner/vaccipy/releases/tag/" + get_latest_version()) except: - print("vaccipy konnte nicht auf die neuste Version geprüft werden.") + print(i18n.t("i18n.CannotVerifyIfVaccipyIsRunningInItsLatestVersion")) print() - print("Automatische Terminbuchung für den Corona Impfterminservice\n") + print(i18n.t("i18n.AutomaticVaccinationAppointments") + "\n") - print("Vor der Ausführung des Programms ist die Berechtigung zur Impfung zu prüfen.\n" - "Ob Anspruch auf eine Impfung besteht, kann hier nachgelesen werden:\n" + print(i18n.t("i18n.CheckIfYouAreAllowedForVaccination") + ".\n" + + i18n.t("i18n.PleaseFindThisInformationHere") + ":\n" + "https://www.impfterminservice.de/terminservice/faq\n") main() From 1994c57b42622dc40295d330814343a9474355d0 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Mon, 7 Jun 2021 15:35:38 +0200 Subject: [PATCH 03/26] Further translate --- main.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index bb0293c3..51c8cdf8 100755 --- a/main.py +++ b/main.py @@ -304,12 +304,12 @@ def gen_code(kontaktdaten): "Beispiel: 02.03.1982\n") while True: try: - geburtsdatum = input("> Geburtsdatum: ") + geburtsdatum = input("> " + i18n.t("i18n.Birthday") + ": ") validate_datum(geburtsdatum) break except ValidationError as exc: - print("Das Datum entspricht nicht dem richtigen Format (DD.MM.YYYY). " - "Bitte erneut versuchen.") + print(i18n.t("i18n.InvalidBirthdayFormat") + ". " + + i18n.t("i18n.PleaseTryAgain") + ".") print() # cookies erneuern und code anfordern @@ -318,18 +318,18 @@ def gen_code(kontaktdaten): if token is not None: # code bestätigen - print("\nDu erhältst gleich eine SMS mit einem Code zur Bestätigung deiner Telefonnummer.\n" - "Trage diesen hier ein. Solltest du dich vertippen, hast du noch 2 weitere Versuche.\n" - "Beispiel: 123-456\n") + print("\n" + i18n.t("i18n.ReceiveSMSCode") + ".\n" + + i18n.t("i18n.EnterSMSCode") + ".\n" + + i18n.t("i18n.Example") + ": 123-456\n") # 3 Versuche für die SMS-Code-Eingabe for _ in range(3): sms_pin = input("> SMS-Code: ").replace("-", "") if its.code_bestaetigen(token, sms_pin): - print("\nDu kannst jetzt mit der Terminsuche fortfahren.\n") + print("\n" + i18n.t("i18n.ContinueSearchForAppointment") + ".\n") return True - print("\nDie Code-Generierung war leider nicht erfolgreich.\n") + print("\n" + i18n.t("i18n.CodeGenerationFailed") + ".\n") return False @@ -490,7 +490,6 @@ def main(): # Lade Sprachen i18n.load_path.append(os.path.join(PATH, "i18n")) i18n.set('fallback', 'de') - print(i18n.t("i18n.test")) # Auf aktuelle Version prüfen try: From 0e84e8b1473818999a34a29c54bd72c7fe8fa66d Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 20:38:56 +0200 Subject: [PATCH 04/26] Add more translations --- main.py | 121 +++++++++++++++++++++----------------------------------- 1 file changed, 46 insertions(+), 75 deletions(-) diff --git a/main.py b/main.py index 51c8cdf8..ab46c5ec 100755 --- a/main.py +++ b/main.py @@ -44,15 +44,10 @@ def update_kontaktdaten_interactive( with open(filepath, 'w', encoding='utf-8') as file: if "plz_impfzentren" not in kontaktdaten: - print( - "Mit einem Code kann in mehreren Impfzentren gleichzeitig nach einem Termin gesucht werden.\n" - "Eine Übersicht über die Gruppierung der Impfzentren findest du hier:\n" - "https://github.com/iamnotturner/vaccipy/wiki/Ein-Code-fuer-mehrere-Impfzentren\n\n" - "Trage nun die PLZ deines Impfzentrums ein. Für mehrere Impfzentren die PLZ's kommagetrennt nacheinander.\n" - "Beispiel: 68163, 69124, 69469\n") + print(i18n.t("i18n.InputPLZs")) input_kontaktdaten_key(kontaktdaten, ["plz_impfzentren"], - "> PLZ's der Impfzentren: ", + "> " + i18n.t('i18n.PLZVacCenters') + ": ", lambda x: list(set([plz.strip() for plz in x.split(",")]))) if "code" not in kontaktdaten and command == "search": @@ -63,37 +58,37 @@ def update_kontaktdaten_interactive( if "anrede" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "anrede"], "> Anrede (Frau/Herr/...): ") + kontaktdaten, ["kontakt", "anrede"], "> " + i18n.t('i18n.Gender') + ": ") if "vorname" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "vorname"], "> Vorname: ") + kontaktdaten, ["kontakt", "vorname"], "> " + i18n.t('i18n.Firstname') + ": ") if "nachname" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "nachname"], "> Nachname: ") + kontaktdaten, ["kontakt", "nachname"], "> " + i18n.t('i18n.Lastname') + ": ") if "strasse" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "strasse"], "> Strasse (ohne Hausnummer): ") + kontaktdaten, ["kontakt", "strasse"], "> " + i18n.t('i18n.Street') + ": ") if "hausnummer" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "hausnummer"], "> Hausnummer: ") + kontaktdaten, ["kontakt", "hausnummer"], "> " + i18n.t('i18n.NumberHouse') + ": ") if "plz" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "plz"], "> PLZ des Wohnorts: ") + kontaktdaten, ["kontakt", "plz"], "> " + i18n.t('i18n.PLZ') + ": ") if "ort" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "ort"], "> Wohnort: ") + kontaktdaten, ["kontakt", "ort"], "> " + i18n.t('i18n.City') + ": ") if "phone" not in kontaktdaten["kontakt"]: input_kontaktdaten_key( kontaktdaten, ["kontakt", "phone"], - "> Telefonnummer: +49", + "> " + i18n.t('i18n.Phonenumber') + ": +49", lambda x: x if x.startswith("+49") else f"+49{remove_prefix(x, '0')}") if "notificationChannel" not in kontaktdaten["kontakt"]: @@ -105,35 +100,31 @@ def update_kontaktdaten_interactive( if "zeitrahmen" not in kontaktdaten and command == "search": kontaktdaten["zeitrahmen"] = {} - if input("> Zeitrahmen festlegen? (y/n): ").lower() != "n": + if input("> " + i18n.t('i18n.ConstrainTimeSlot') + ": ").lower() != "n": print() input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "einhalten_bei"], - "> Für welchen Impftermin soll der Zeitrahmen gelten? (1/2/beide): ") + "> " + i18n.t('i18n.ForWhichAppointment') + ": ") input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "von_datum"], - "> Von Datum (Leer lassen zum Überspringen): ", + "> " + i18n.t('i18n.FromDate') + ": ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "bis_datum"], - "> Bis Datum (Leer lassen zum Überspringen): ", + "> " + i18n.t('i18n.ToDate') + ": ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "von_uhrzeit"], - "> Von Uhrzeit (Leer lassen zum Überspringen): ", + "> " + i18n.t('i18n.FromTime') + ": ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "bis_uhrzeit"], - "> Bis Uhrzeit (Leer lassen zum Überspringen): ", + "> " + i18n.t('i18n.ToTime') + ": ", lambda x: x if x else None) # Leeren String zu None umwandeln - print( - "Trage nun die Wochentage ein, an denen die ausgewählten Impftermine liegen dürfen.\n" - "Mehrere Wochentage können durch Komma getrennt werden.\n" - "Beispiel: Mo, Di, Mi, Do, Fr, Sa, So\n" - "Leer lassen, um alle Wochentage auszuwählen.") + print(i18n.t("i18n.InputWeekday")) input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "wochentage"], - "> Erlaubte Wochentage: ", parse_wochentage) + "> " + i18n.t('i18n.AllowedWeekdays') + ": ", parse_wochentage) json.dump(kontaktdaten, file, ensure_ascii=False, indent=4) @@ -186,15 +177,12 @@ def run_search_interactive(kontaktdaten_path, check_delay): :param kontaktdaten_path: Pfad zur JSON-Datei mit Kontaktdaten. Default: data/kontaktdaten.json im aktuellen Ordner """ - print( - "Bitte trage zunächst deinen Impfcode und deine Kontaktdaten ein.\n" - f"Die Daten werden anschließend lokal in der Datei '{os.path.basename(kontaktdaten_path)}' abgelegt.\n" - "Du musst sie zukünftig nicht mehr eintragen.\n") + print(i18n.t("i18n.InputVacCodeAndContactdata", filename=os.path.basename(kontaktdaten_path))) kontaktdaten = {} if os.path.isfile(kontaktdaten_path): daten_laden = input( - f"> Sollen die vorhandenen Daten aus '{os.path.basename(kontaktdaten_path)}' geladen werden? (y/n): ").lower() + f"> " + i18n.t("i18n.ShouldContactdataBeLoaded",filename=os.path.basename(kontaktdaten_path)) + ": ").lower() if daten_laden.lower() != "n": kontaktdaten = get_kontaktdaten(kontaktdaten_path) @@ -216,23 +204,17 @@ def run_search(kontaktdaten, check_delay): # Hinweis, wenn noch alte Version der Kontaktdaten.json verwendet wird if kontaktdaten.get("plz"): - print( - "ACHTUNG: Du verwendest noch die alte Version der 'Kontaktdaten.json'!\n" - "Lösche vor dem nächsten Ausführen die Datei und fülle die Kontaktdaten bitte erneut aus.\n") + print(i18n.t("i18n.InputAttentionOldVersionOfContactdata")) plz_impfzentren = [kontaktdaten.get("plz")] else: plz_impfzentren = kontaktdaten["plz_impfzentren"] kontakt = kontaktdaten["kontakt"] - print( - f"Kontaktdaten wurden geladen für: {kontakt['vorname']} {kontakt['nachname']}\n") + print(i18n.t("i18n.ContactdataLoadedFor",firstname=kontakt['vorname'],lastname=kontakt['nachname']) + "\n") zeitrahmen = kontaktdaten["zeitrahmen"] except KeyError as exc: - raise ValueError( - "Kontaktdaten konnten nicht aus 'kontaktdaten.json' geladen werden.\n" - "Bitte überprüfe, ob sie im korrekten JSON-Format sind oder gebe " - "deine Daten beim Programmstart erneut ein.\n") from exc + raise ValueError(i18n.t("i18n.ContactdataCouldNotBeLoaded")) from exc ImpfterminService.terminsuche(code=code, plz_impfzentren=plz_impfzentren, kontakt=kontakt, zeitrahmen=zeitrahmen, check_delay=check_delay, PATH=PATH) @@ -285,9 +267,7 @@ def gen_code(kontaktdaten): telefonnummer = f"+49{remove_prefix(telefonnummer, '0')}" except KeyError as exc: raise ValueError( - "Kontaktdaten konnten nicht aus 'kontaktdaten.json' geladen werden.\n" - "Bitte überprüfe, ob sie im korrekten JSON-Format sind oder gebe " - "deine Daten beim Programmstart erneut ein.\n") from exc + i18n.t("i18n.ContactdataCouldNotBeLoaded")) from exc # Erstelle Zufallscode nach Format XXXX-YYYY-ZZZZ # für die Cookie-Generierung @@ -296,20 +276,19 @@ def gen_code(kontaktdaten): two = 'IPY' + random.choice(code_chars) three = ''.join(random.choices(code_chars, k=4)) random_code = f"{one}-{two}-{three}" - print(f"Für die Cookies-Generierung wird ein zufälliger Code verwendet ({random_code}).\n") + print(i18n.t('i18n.UsingRandomCodeForCookieGeneration') + f" ({random_code}).\n") its = ImpfterminService(random_code, [plz_impfzentrum], {}, PATH) - print("Bitte trage nachfolgend dein Geburtsdatum im Format DD.MM.YYYY ein.\n" - "Beispiel: 02.03.1982\n") + print(i18n.t('i18n.PleaseEnterBirthday') + ".\n" + + i18n.t('i18n.Example') + ": 02.03.1982\n") while True: try: - geburtsdatum = input("> " + i18n.t("i18n.Birthday") + ": ") + geburtsdatum = input("> " + i18n.t('i18n.Birthday') + ": ") validate_datum(geburtsdatum) break except ValidationError as exc: - print(i18n.t("i18n.InvalidBirthdayFormat") + ". " + - i18n.t("i18n.PleaseTryAgain") + ".") + print(i18n.t("i18n.InvalidBirthdayFormat")) print() # cookies erneuern und code anfordern @@ -318,18 +297,16 @@ def gen_code(kontaktdaten): if token is not None: # code bestätigen - print("\n" + i18n.t("i18n.ReceiveSMSCode") + ".\n" + - i18n.t("i18n.EnterSMSCode") + ".\n" + - i18n.t("i18n.Example") + ": 123-456\n") + print("\n" + i18n.t("i18n.ReceiveSMSCode")) # 3 Versuche für die SMS-Code-Eingabe for _ in range(3): sms_pin = input("> SMS-Code: ").replace("-", "") if its.code_bestaetigen(token, sms_pin): - print("\n" + i18n.t("i18n.ContinueSearchForAppointment") + ".\n") + print("\n" + i18n.t('i18n.ContinueSearchForAppointment') + ".\n") return True - print("\n" + i18n.t("i18n.CodeGenerationFailed") + ".\n") + print("\n" + i18n.t('i18n.CodeGenerationFailed') + ".\n") return False @@ -359,8 +336,7 @@ def validate_args(args): """ if args.configure_only and args.read_only: - raise ValueError( - "--configure-only und --read-only kann nicht gleichzeitig verwendet werden") + raise ValueError(i18n.t("i18n.ConfigureOnlyReadOnlyNotBoth")) def main(): @@ -425,7 +401,7 @@ def main(): else: assert False except ValidationError as exc: - print(i18n.t("i18n.ErrorIn") + f" {json.dumps(args.file)}:\n{str(exc)}") + print(i18n.t('i18n.ErrorIn') + f" {json.dumps(args.file)}:\n{str(exc)}") else: extended_settings = False @@ -433,15 +409,15 @@ def main(): while True: print( i18n.t("Menu") + "?\n" - "[1] " + i18n.t("i18n.SearchForAppointment") + "\n" - "[2] " + i18n.t("i18n.GenerateVacCode") + "\n" + "[1] " + i18n.t('i18n.SearchForAppointment') + "\n" + "[2] " + i18n.t('i18n.GenerateVacCode') + "\n" f"[x] {i18n.t('i18n.HideAdvancedSettings') if extended_settings else i18n.t('i18n.ShowAdvancedSettings')}\n") if extended_settings: print( - f"[c] --configure-only {'de' if args.configure_only else ''}" + i18n.t("i18n.activate") + "\n" - f"[r] --read-only {'de' if args.read_only else ''}" + i18n.t("i18n.activate") + "\n" - "[s] --retry-sec setzen\n") + f"[c] --configure-only {i18n.t('i18n.deactivate') if args.configure_only else i18n.t('i18n.activate')}\n" + f"[r] --read-only {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n" + f"[s] --retry-sec {i18n.t('i18n.set')}\n") option = input("> Option: ").lower() print() @@ -459,18 +435,18 @@ def main(): validate_args(new_args) args = new_args print( - f"--configure-only {'de' if not args.configure_only else ''}" + i18n.t("i18n.activate") + ".") + f"--configure-only {i18n.t('i18n.deactivate') if not args.read_only else i18n.t('i18n.activate')}.") elif extended_settings and option == "r": new_args = copy.copy(args) new_args.read_only = not new_args.read_only validate_args(new_args) args = new_args print( - f"--read-only {'de' if not args.read_only else ''}" + i18n.t("i18n.activate") + ".") + f"--read-only {i18n.t('i18n.deactivate') if not args.read_only else i18n.t('i18n.activate')}.") elif extended_settings and option == "s": args.retry_sec = int(input("> --retry-sec=")) else: - print(i18n.t("i18n.InvalidInputPleaseTryAgain") + ".") + print(i18n.t('i18n.InvalidInputPleaseTryAgain') + ".") print() except Exception as exc: print(f"\n" + i18n.t("Error") + ":\n{str(exc)}\n") @@ -494,18 +470,13 @@ def main(): # Auf aktuelle Version prüfen try: if not update_available(): - print(i18n.t("i18n.YouAreUsingTheLatestVersionOfVaccipy") + ': ' + get_current_version()) + print(i18n.t('i18n.YouAreUsingTheLatestVersionOfVaccipy') + ': ' + get_current_version()) else: - print(i18n.t("YouAreUsingAnOldVersionOfVaccipy") + "\n" + - i18n.t("PleaseInstallTheLastestVersionLink") + ":\n" + - "https://github.com/iamnotturner/vaccipy/releases/tag/" + get_latest_version()) + print(i18n.t('i18n.YouAreUsingAnOldVersionOfVaccipy') + get_latest_version()) except: - print(i18n.t("i18n.CannotVerifyIfVaccipyIsRunningInItsLatestVersion")) + print(i18n.t('i18n.CannotVerifyIfVaccipyIsRunningInItsLatestVersion')) print() - print(i18n.t("i18n.AutomaticVaccinationAppointments") + "\n") - print(i18n.t("i18n.CheckIfYouAreAllowedForVaccination") + ".\n" + - i18n.t("i18n.PleaseFindThisInformationHere") + ":\n" + - "https://www.impfterminservice.de/terminservice/faq\n") + print(i18n.t('i18n.CheckIfYouAreAllowedForVaccination')) main() From 477bb82c96258e344e1317ac51925c8cc303470b Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:03:09 +0200 Subject: [PATCH 05/26] Using translation with fstrings --- main.py | 69 ++++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/main.py b/main.py index ab46c5ec..8048f8d1 100755 --- a/main.py +++ b/main.py @@ -47,7 +47,7 @@ def update_kontaktdaten_interactive( print(i18n.t("i18n.InputPLZs")) input_kontaktdaten_key(kontaktdaten, ["plz_impfzentren"], - "> " + i18n.t('i18n.PLZVacCenters') + ": ", + f"> {i18n.t('i18n.PLZVacCenters')}: ", lambda x: list(set([plz.strip() for plz in x.split(",")]))) if "code" not in kontaktdaten and command == "search": @@ -58,37 +58,37 @@ def update_kontaktdaten_interactive( if "anrede" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "anrede"], "> " + i18n.t('i18n.Gender') + ": ") + kontaktdaten, ["kontakt", "anrede"], f"> {i18n.t('i18n.Gender')}: ") if "vorname" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "vorname"], "> " + i18n.t('i18n.Firstname') + ": ") + kontaktdaten, ["kontakt", "vorname"], f"> {i18n.t('i18n.Firstname')}: ") if "nachname" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "nachname"], "> " + i18n.t('i18n.Lastname') + ": ") + kontaktdaten, ["kontakt", "nachname"], f"> {i18n.t('i18n.Lastname')}: ") if "strasse" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "strasse"], "> " + i18n.t('i18n.Street') + ": ") + kontaktdaten, ["kontakt", "strasse"], f"> {i18n.t('i18n.Street')}: ") if "hausnummer" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "hausnummer"], "> " + i18n.t('i18n.NumberHouse') + ": ") + kontaktdaten, ["kontakt", "hausnummer"], f"> {i18n.t('i18n.HouseNumber')}: ") if "plz" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "plz"], "> " + i18n.t('i18n.PLZ') + ": ") + kontaktdaten, ["kontakt", "plz"], f"> {i18n.t('i18n.PLZ')}: ") if "ort" not in kontaktdaten["kontakt"] and command == "search": input_kontaktdaten_key( - kontaktdaten, ["kontakt", "ort"], "> " + i18n.t('i18n.City') + ": ") + kontaktdaten, ["kontakt", "ort"], f"> {i18n.t('i18n.City')}: ") if "phone" not in kontaktdaten["kontakt"]: input_kontaktdaten_key( kontaktdaten, ["kontakt", "phone"], - "> " + i18n.t('i18n.Phonenumber') + ": +49", + f"> {i18n.t('i18n.Phonenumber')}: +49", lambda x: x if x.startswith("+49") else f"+49{remove_prefix(x, '0')}") if "notificationChannel" not in kontaktdaten["kontakt"]: @@ -100,31 +100,31 @@ def update_kontaktdaten_interactive( if "zeitrahmen" not in kontaktdaten and command == "search": kontaktdaten["zeitrahmen"] = {} - if input("> " + i18n.t('i18n.ConstrainTimeSlot') + ": ").lower() != "n": + if input(f"> {i18n.t('i18n.ConstrainTimeSlot')}: ").lower() != "n": print() input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "einhalten_bei"], - "> " + i18n.t('i18n.ForWhichAppointment') + ": ") + f"> {i18n.t('i18n.ForWhichAppointment')}: ") input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "von_datum"], - "> " + i18n.t('i18n.FromDate') + ": ", + f"> {i18n.t('i18n.FromDate')}: ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "bis_datum"], - "> " + i18n.t('i18n.ToDate') + ": ", + f"> {i18n.t('i18n.ToDate')}: ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "von_uhrzeit"], - "> " + i18n.t('i18n.FromTime') + ": ", + f"> {i18n.t('i18n.FromTime')}: ", lambda x: x if x else None) # Leeren String zu None umwandeln input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "bis_uhrzeit"], - "> " + i18n.t('i18n.ToTime') + ": ", + f"> {i18n.t('i18n.ToTime')}: ", lambda x: x if x else None) # Leeren String zu None umwandeln print(i18n.t("i18n.InputWeekday")) input_kontaktdaten_key( kontaktdaten, ["zeitrahmen", "wochentage"], - "> " + i18n.t('i18n.AllowedWeekdays') + ": ", parse_wochentage) + f"> {i18n.t('i18n.AllowedWeekdays')}: ", parse_wochentage) json.dump(kontaktdaten, file, ensure_ascii=False, indent=4) @@ -182,14 +182,14 @@ def run_search_interactive(kontaktdaten_path, check_delay): kontaktdaten = {} if os.path.isfile(kontaktdaten_path): daten_laden = input( - f"> " + i18n.t("i18n.ShouldContactdataBeLoaded",filename=os.path.basename(kontaktdaten_path)) + ": ").lower() + f"> {i18n.t('i18n.ShouldContactdataBeLoaded',filename=os.path.basename(kontaktdaten_path))}: ").lower() if daten_laden.lower() != "n": kontaktdaten = get_kontaktdaten(kontaktdaten_path) print() kontaktdaten = update_kontaktdaten_interactive( kontaktdaten, "search", kontaktdaten_path) - return run_search(kontaktdaten, check_delay) + return run_search(kontaktdaten, check_delay, kontaktdaten_path) def run_search(kontaktdaten, check_delay): @@ -204,17 +204,17 @@ def run_search(kontaktdaten, check_delay): # Hinweis, wenn noch alte Version der Kontaktdaten.json verwendet wird if kontaktdaten.get("plz"): - print(i18n.t("i18n.InputAttentionOldVersionOfContactdata")) + print(i18n.t("i18n.InputAttentionOldVersionOfContactdata",filename="kontaktdaten.json")) # Kontaktdaten_path not available plz_impfzentren = [kontaktdaten.get("plz")] else: plz_impfzentren = kontaktdaten["plz_impfzentren"] kontakt = kontaktdaten["kontakt"] - print(i18n.t("i18n.ContactdataLoadedFor",firstname=kontakt['vorname'],lastname=kontakt['nachname']) + "\n") + print(f"{i18n.t('i18n.ContactdataLoadedFor',firstname=kontakt['vorname'],lastname=kontakt['nachname'])}\n") zeitrahmen = kontaktdaten["zeitrahmen"] except KeyError as exc: - raise ValueError(i18n.t("i18n.ContactdataCouldNotBeLoaded")) from exc + raise ValueError(i18n.t('i18n.ContactdataCouldNotBeLoaded',filename='kontaktdaten.json')) from exc # Kontaktdaten_path not available ImpfterminService.terminsuche(code=code, plz_impfzentren=plz_impfzentren, kontakt=kontakt, zeitrahmen=zeitrahmen, check_delay=check_delay, PATH=PATH) @@ -233,16 +233,12 @@ def gen_code_interactive(kontaktdaten_path): :param kontaktdaten_path: Pfad zur JSON-Datei mit Kontaktdaten. Default: kontaktdaten.json im aktuellen Ordner """ - print( - "Du kannst dir jetzt direkt einen Impf-Code erstellen.\n" - "Dazu benötigst du eine Mailadresse, Telefonnummer und die PLZ deines Impfzentrums.\n" - f"Die Daten werden anschließend lokal in der Datei '{os.path.basename(kontaktdaten_path)}' abgelegt.\n" - "Du musst sie zukünftig nicht mehr eintragen.\n") + print(i18n.t('i18n.InfoVacCode',filename=os.path.basename(kontaktdaten_path))) kontaktdaten = {} if os.path.isfile(kontaktdaten_path): daten_laden = input( - f"> Sollen die vorhandenen Daten aus '{os.path.basename(kontaktdaten_path)}' geladen werden (y/n)?: ").lower() + f"> {i18n.t('i18n.ShouldContactdataBeLoaded',filename=os.path.basename(kontaktdaten_path))}?: ").lower() if daten_laden.lower() != "n": kontaktdaten = get_kontaktdaten(kontaktdaten_path) @@ -267,7 +263,7 @@ def gen_code(kontaktdaten): telefonnummer = f"+49{remove_prefix(telefonnummer, '0')}" except KeyError as exc: raise ValueError( - i18n.t("i18n.ContactdataCouldNotBeLoaded")) from exc + i18n.t("i18n.ContactdataCouldNotBeLoaded",filename='kontaktdata.json')) from exc # Kontaktdaten_path not available here # Erstelle Zufallscode nach Format XXXX-YYYY-ZZZZ # für die Cookie-Generierung @@ -280,11 +276,10 @@ def gen_code(kontaktdaten): its = ImpfterminService(random_code, [plz_impfzentrum], {}, PATH) - print(i18n.t('i18n.PleaseEnterBirthday') + ".\n" + - i18n.t('i18n.Example') + ": 02.03.1982\n") + print(f"{i18n.t('i18n.PleaseEnterBirthday')}.\n{i18n.t('i18n.Example')}: 02.03.1982\n") while True: try: - geburtsdatum = input("> " + i18n.t('i18n.Birthday') + ": ") + geburtsdatum = input(f"> {i18n.t('i18n.Birthday')}: ") validate_datum(geburtsdatum) break except ValidationError as exc: @@ -297,16 +292,16 @@ def gen_code(kontaktdaten): if token is not None: # code bestätigen - print("\n" + i18n.t("i18n.ReceiveSMSCode")) + print(f"\n{i18n.t('i18n.ReceiveSMSCode')}") # 3 Versuche für die SMS-Code-Eingabe for _ in range(3): sms_pin = input("> SMS-Code: ").replace("-", "") if its.code_bestaetigen(token, sms_pin): - print("\n" + i18n.t('i18n.ContinueSearchForAppointment') + ".\n") + print(f"\n{i18n.t('i18n.ContinueSearchForAppointment')}.\n") return True - print("\n" + i18n.t('i18n.CodeGenerationFailed') + ".\n") + print(f"\n{i18n.t('i18n.CodeGenerationFailed')}.\n") return False @@ -409,8 +404,8 @@ def main(): while True: print( i18n.t("Menu") + "?\n" - "[1] " + i18n.t('i18n.SearchForAppointment') + "\n" - "[2] " + i18n.t('i18n.GenerateVacCode') + "\n" + f"[1] {i18n.t('i18n.SearchForAppointment')}\n" + f"[2] {i18n.t('i18n.GenerateVacCode')}\n" f"[x] {i18n.t('i18n.HideAdvancedSettings') if extended_settings else i18n.t('i18n.ShowAdvancedSettings')}\n") if extended_settings: @@ -449,7 +444,7 @@ def main(): print(i18n.t('i18n.InvalidInputPleaseTryAgain') + ".") print() except Exception as exc: - print(f"\n" + i18n.t("Error") + ":\n{str(exc)}\n") + print(f"\n{i18n.t('i18n.Error')}:\n{str(exc)}\n") if __name__ == "__main__": From 92e76a180fe631397dfc62d87ab203e68da44129 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:35:01 +0200 Subject: [PATCH 06/26] Add todos --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index f1c36e3e..c51cce10 100755 --- a/main.py +++ b/main.py @@ -250,7 +250,7 @@ def run_search(kontaktdaten, check_delay): # Hinweis, wenn noch alte Version der Kontaktdaten.json verwendet wird if kontaktdaten.get("plz"): - print(i18n.t("i18n.InputAttentionOldVersionOfContactdata",filename="kontaktdaten.json")) # Kontaktdaten_path not available + print(i18n.t("i18n.InputAttentionOldVersionOfContactdata",filename="kontaktdaten.json")) # TODO Kontaktdaten_path not available plz_impfzentren = [kontaktdaten.get("plz")] else: plz_impfzentren = kontaktdaten["plz_impfzentren"] @@ -262,7 +262,7 @@ def run_search(kontaktdaten, check_delay): zeitrahmen = kontaktdaten["zeitrahmen"] except KeyError as exc: - raise ValueError(i18n.t('i18n.ContactdataCouldNotBeLoaded',filename='kontaktdaten.json')) from exc # Kontaktdaten_path not available + raise ValueError(i18n.t('i18n.ContactdataCouldNotBeLoaded',filename='kontaktdaten.json')) from exc # TODO Kontaktdaten_path not available ImpfterminService.terminsuche( codes=codes, @@ -317,7 +317,7 @@ def gen_code(kontaktdaten): telefonnummer = f"+49{remove_prefix(telefonnummer, '0')}" except KeyError as exc: raise ValueError( - i18n.t("i18n.ContactdataCouldNotBeLoaded",filename='kontaktdata.json')) from exc # Kontaktdaten_path not available here + i18n.t("i18n.ContactdataCouldNotBeLoaded",filename='kontaktdata.json')) from exc # TODO Kontaktdaten_path not available here its = ImpfterminService([], {}, PATH) From e8d09f84f0266a7a4efc3d5a3148d9935e204470 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:35:12 +0200 Subject: [PATCH 07/26] Fix menu --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index c51cce10..ad4b8c9c 100755 --- a/main.py +++ b/main.py @@ -470,8 +470,8 @@ def main(): print( f"[c] --configure-only {i18n.t('i18n.deactivate') if args.configure_only else i18n.t('i18n.activate')}\n" f"[r] --read-only {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n" - f"[s] --retry-sec {i18n.t('i18n.set')}\n") - f"[n] --configure-notifications {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n\n" + f"[s] --retry-sec {i18n.t('i18n.set')}\n" + f"[n] --configure-notifications {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n\n") option = input("> Option: ").lower() @@ -506,7 +506,7 @@ def main(): validate_args(new_args) args = new_args print( - f"--configure-notifications {'de' if not args.configure_notifications else ''}aktiviert.") + f"--configure-notifications {i18n.t('i18n.deactivate') if not args.configure_notifications else i18n.t('i18n.activate')}.") else: print(i18n.t('i18n.InvalidInputPleaseTryAgain') + ".") print() From 60b39d7b2b85acabcaef92b309c495a9a9565600 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:35:29 +0200 Subject: [PATCH 08/26] Make date format more clear for none germany people --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index ad4b8c9c..81c20ca0 100755 --- a/main.py +++ b/main.py @@ -321,7 +321,7 @@ def gen_code(kontaktdaten): its = ImpfterminService([], {}, PATH) - print(f"{i18n.t('i18n.PleaseEnterBirthday')}.\n{i18n.t('i18n.Example')}: 02.03.1982\n") + print(f"{i18n.t('i18n.PleaseEnterBirthday')}.\n{i18n.t('i18n.Example')}: 24.03.1982\n") while True: try: geburtsdatum = input(f"> {i18n.t('i18n.Birthday')}: ") From e1302250fae4c13b78a19af2e14d97945990e6d9 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:39:02 +0200 Subject: [PATCH 09/26] Add requirements --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index f90f617d..fe30c8b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,5 @@ PyQt5==5.15.4 PyQt5-Qt5==5.15.2 PyQt5-sip==12.9.0 requests>=2.25.1 +python-i18n>=0.3.9 +PyYAML>=5.4.1 \ No newline at end of file From 534b7f24da6ef799f5e74c125a1d0e6ce1139287 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:47:01 +0200 Subject: [PATCH 10/26] Add translation files --- i18n/i18n.de.yml | 99 ++++++++++++++++++++++++++++++++++++++++++++++ i18n/i18n.en.yml | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 i18n/i18n.de.yml create mode 100644 i18n/i18n.en.yml diff --git a/i18n/i18n.de.yml b/i18n/i18n.de.yml new file mode 100644 index 00000000..7fe3da34 --- /dev/null +++ b/i18n/i18n.de.yml @@ -0,0 +1,99 @@ +de: + YouAreUsingTheLatestVersionOfVaccipy: Du verwendest die aktuellste Version von vaccipy + CannotVerifyIfVaccipyIsRunningInItsLatestVersion: vaccipy konnte nicht auf die neuste Version geprüft werden. + YouAreUsingAnOldVersionOfVaccipy: |- + Du verwendest eine alte Version von vaccipy. + Bitte installiere die aktuellste Version. Link zum Download: + https://github.com/iamnotturner/vaccipy/releases/tag/ + CheckIfYouAreAllowedForVaccination: |- + Automatische Terminbuchung für den Corona Impfterminservice. + Vor der Ausführung des Programms ist die Berechtigung zur Impfung zu prüfen. + Ob Anspruch auf eine Impfung besteht, kann hier nachgelesen werden. + https://www.impfterminservice.de/terminservice/faq + activate: aktivieren + deactivate: deaktivieren + set: setzen + ErrorIn: Fehler in + InvalidInputPleaseTryAgain: Falscheingabe! Bitte erneut versuchen + Error: Fehler + GenerateVacCode: Vermittlungscode generieren + SearchForAppointment: Termin suchen + ShowAdvancedSettings: Erweiterte Einstellungen anzeigen + HideAdvancedSettings: Erweiterte Einstellung verbergen + Menu: Was möchtest du tun + PathContactData: Pfad zur JSON-Datei für Kontaktdaten + ConfigureOnlyDescription: Nur Kontaktdaten erfassen und in JSON-Datei abspeichern + ReadOnlyDescription: Es wird nicht nach fehlenden Kontaktdaten gefragt. Stattdessen wird ein Fehler angezeigt, falls benötigte Kontaktdaten in der JSON-Datei fehlen. + RetrySecDescription: Wartezeit zwischen zwei Versuchen (in Sekunden) + CodeGenerationFailed: Die Code-Generierung war leider nicht erfolgreich + ContinueSearchForAppointment: Du kannst jetzt mit der Terminsuche fortfahren + ReceiveSMSCode: |- + Du erhältst gleich eine SMS mit einem Code zur Bestätigung deiner Telefonnummer. + Trage diesen hier ein. Solltest du dich vertippen, hast du noch 2 weitere Versuche. + Beispiel: 123-456 + InvalidBirthdayFormat: |- + Das Datum entspricht nicht dem richtigen Format (DD.MM.YYYY). + Bitte erneut versuchen. + Birthday: Geburtstag + PleaseEnterBirthday: Bitte trage nachfolgend dein Geburtsdatum im Format DD.MM.YYYY ein + UsingRandomCodeForCookieGeneration: Für die Cookies-Generierung wird ein zufälliger Code verwendet + Gender: Anrede (Frau/Herr/...) + Firstname: Vorname + Lastname: Nachname + Street: Strasse (ohne Hausnummer) + City: Wohnort + HouseNumber: Hausnummer + PLZ: PLZ des Wohnorts + Phonenumber: Telefonnummer + PLZVacCenters: PLZ's der Impfzentren + InputPLZs: |- + Mit einem Code kann in mehreren Impfzentren gleichzeitig nach einem Termin gesucht werden. + Eine Übersicht über die Gruppierung der Impfzentren findest du hier: + https://github.com/iamnotturner/vaccipy/wiki/Ein-Code-fuer-mehrere-Impfzentren + Trage nun die PLZ deines Impfzentrums ein. Für mehrere Impfzentren die PLZ's kommagetrennt nacheinander. + Beispiel: 68163, 69124, 69469 + InputWeekday: |- + Trage nun die Wochentage ein, an denen die ausgewählten Impftermine liegen dürfen. + Mehrere Wochentage können durch Komma getrennt werden. + Beispiel: Mo, Di, Mi, Do, Fr, Sa, So + Leer lassen, um alle Wochentage auszuwählen. + AllowedWeekdays: Erlaubte Wochentage + InputAttentionOldVersionOfContactdata: |- + ACHTUNG: Du verwendest noch die alte Version der '%{filename}'! + Lösche vor dem nächsten Ausführen die Datei und fülle die Kontaktdaten bitte erneut aus. + ContactdataCouldNotBeLoaded: |- + Kontaktdaten konnten nicht aus '%{filename}' geladen werden. + Bitte überprüfe, ob sie im korrekten JSON-Format sind oder gebe deine Daten beim Programmstart erneut ein. + ConfigureOnlyReadOnlyNotBoth: --configure-only und --read-only kann nicht gleichzeitig verwendet werden + ConstrainTimeSlot: Zeitrahmen festlegen (y/n) + ForWhichAppointment: Für welchen Impftermin soll der Zeitrahmen gelten? (1/2/beide) + FromDate: Von Datum (Leer lassen zum Überspringen) + ToDate: Bis Datum (Leer lassen zum Überspringen) + FromTime: Von Uhrzeit (Leer lassen zum Überspringen) + ToTime: Bis Uhrzeit (Leer lassen zum Überspringen) + InputVacCodeAndContactdata: |- + Bitte trage zunächst deinen Vermittlungscode und deine Kontaktdaten ein. + Die Daten werden anschließend lokal in der Datei '%{filename}' abgelegt. + Du musst sie zukünftig nicht mehr eintragen. + ShouldContactdataBeLoaded: |- + Sollen die vorhandenen Daten aus '%{filename}' geladen werden? (y/n) + ContactdataLoadedFor: |- + Kontaktdaten wurden geladen für: %{firstname} %{lastname} + InfoVacCode: |- + Du kannst dir jetzt direkt einen Vermittlungscode erstellen. + Dazu benötigst du eine Mailadresse, Telefonnummer und die PLZ deines Impfzentrums. + Die Daten werden anschließend lokal in der Datei '%{filename}' abgelegt." + Du musst sie zukünftig nicht mehr eintragen. + + + + + + + + + + + + + diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml new file mode 100644 index 00000000..8f531cfb --- /dev/null +++ b/i18n/i18n.en.yml @@ -0,0 +1,100 @@ +en: + YouAreUsingTheLatestVersionOfVaccipy: You are using the latest version of vaccipy + CannotVerifyIfVaccipyIsRunningInItsLatestVersion: Cannot verify if vaccipy is running in its latest version. + YouAreUsingAnOldVersionOfVaccipy: |- + You are using an old version of vaccipy. + Please install the lastest version. Link: + https://github.com/iamnotturner/vaccipy/releases/tag/ + CheckIfYouAreAllowedForVaccination: |- + Automatic vaccination appointment for corona "Impfterminservice" + Check if you are allowed for vaccination before running this program. + Please find this information here. + https://www.impfterminservice.de/terminservice/faq + activate: activate + deactivate: deactivate + set: set + ErrorIn: Error in + InvalidInputPleaseTryAgain: Invalid input! Please try again + Error: Fehler + GenerateVacCode: Generate vaccination code + SearchForAppointment: Search for appointment + ShowAdvancedSettings: Show advanced settings + HideAdvancedSettings: Hide advanced settings + Menu: Menu + PathContactData: Path to contactdata file + ConfigureOnlyDescription: Only create and save contactdata file + ReadOnlyDescription: Do not ask for contactdata. Throughs errors if contactdata file is not present + RetrySecDescription: Delay between to two successive request (in seconds) + CodeGenerationFailed: Code generation was not successful + ContinueSearchForAppointment: Please continue by searching for an appointment + ReceiveSMSCode: |- + You will receive a SMS code to verify your phonenumber. + Enter this code here. You got 3 tries. + Example: 123-456 + InvalidBirthdayFormat: |- + Invalid date format (DD.MM.YYYY). + Please try again. + Birthday: Birthday + PleaseEnterBirthday: Please enter your birthday (DD.MM.YYYY) + UsingRandomCodeForCookieGeneration: Using random code for cookie generation + Gender: Gender ('Frau' for Ms./'Herr' for Mr./...) + Firstname: Firstname + Lastname: Lastname + Street: Street (without house number) + City: City + HouseNumber: House number + PLZ: PLZ/ZIP code + Phonenumber: Phonenumber + PLZVacCenters: PLZ/ZIP codes of vac. centers + InputPLZs: |- + You can use one 'Vermittlungscode' to search at multiple vaccination centers. + Please find a table with vaccination centers and their grouping here: + https://github.com/iamnotturner/vaccipy/wiki/Ein-Code-fuer-mehrere-Impfzentren + Please enter the PLZ/ZIP code of your desired vaccination centers, separated by commata. + Example: 68163, 69124, 69469 + InputWeekday: |- + Please enter weekdays at which you can manage to make the appointment. + Enter multiple day be separating them with commata. + Example: Mo, Di, Mi, Do, Fr, Sa, So + Please leave the field empty to select every weekday. + AllowedWeekdays: Allowed weekdays + InputAttentionOldVersionOfContactdata: |- + ATTENTION: You are using an old version of '%{filename}'! + Delete the file before restarting the program and enter the contact data again. + ContactdataCouldNotBeLoaded: |- + Contactdata file '%{filename}' could not be loaded. + Please check if they are in a correct JSON format or enter your data again at the programmstart. + ConfigureOnlyReadOnlyNotBoth: --configure-only und --read-only kann nicht gleichzeitig verwendet werden + ConstrainTimeSlot: Would you like to restrict the time slot (y/n) + ForWhichAppointment: For which appointment would you like to restrict the splot? (1/2/beide) + FromDate: From date (empty to skip) + ToDate: To date (empty to skip) + FromTime: From time (empty to skip) + ToTime: To time (empty to skip) + InputVacCodeAndContactdata: |- + Please insert you vaccination code and your contact data first. + Both will be save in the file '%{filename}'. + There is no need to enter them again afterwards. + ShouldContactdataBeLoaded: |- + Should the contactdata be loaded from '%{filename}'? (y/n) + ContactdataLoadedFor: |- + Contactdata for %{firstname} %{lastname} where loaded + InfoVacCode: |- + You can create an appointment now. + You will need your mailaddress, phonenumber and the plz/zip code of your desired vaccation center. + This information will be saved in the file '%{filename}' afterwards. + You will not need to input them afterwards again. + + + + + + + + + + + + + + From 8422eec12f781e25cd90067bfda98552a397dd66 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Wed, 9 Jun 2021 23:49:48 +0200 Subject: [PATCH 11/26] empty -> blank in eng translation --- i18n/i18n.en.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index 8f531cfb..e7cb17cf 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -67,10 +67,10 @@ en: ConfigureOnlyReadOnlyNotBoth: --configure-only und --read-only kann nicht gleichzeitig verwendet werden ConstrainTimeSlot: Would you like to restrict the time slot (y/n) ForWhichAppointment: For which appointment would you like to restrict the splot? (1/2/beide) - FromDate: From date (empty to skip) - ToDate: To date (empty to skip) - FromTime: From time (empty to skip) - ToTime: To time (empty to skip) + FromDate: From date (leave blank to skip) + ToDate: To date (leave blank to skip) + FromTime: From time (leave blank to skip) + ToTime: To time (leave blank to skip) InputVacCodeAndContactdata: |- Please insert you vaccination code and your contact data first. Both will be save in the file '%{filename}'. From 3f82aa851d83790132c4bfe3f217d7f151d55040 Mon Sep 17 00:00:00 2001 From: noppelmax Date: Wed, 9 Jun 2021 23:52:43 +0200 Subject: [PATCH 12/26] Update main.py Co-authored-by: haslersn --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 81c20ca0..d7021f4a 100755 --- a/main.py +++ b/main.py @@ -46,6 +46,7 @@ def update_kontaktdaten_interactive( with open(filepath, 'w', encoding='utf-8') as file: if "plz_impfzentren" not in kontaktdaten: print(i18n.t("i18n.InputPLZs")) + print() input_kontaktdaten_key(kontaktdaten, ["plz_impfzentren"], f"> {i18n.t('i18n.PLZVacCenters')}: ", From 51827738a9031460921d238a49ae77598daba464 Mon Sep 17 00:00:00 2001 From: noppelmax Date: Wed, 9 Jun 2021 23:52:59 +0200 Subject: [PATCH 13/26] Update i18n/i18n.en.yml Co-authored-by: haslersn --- i18n/i18n.en.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index e7cb17cf..e8b74d45 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -84,17 +84,3 @@ en: You will need your mailaddress, phonenumber and the plz/zip code of your desired vaccation center. This information will be saved in the file '%{filename}' afterwards. You will not need to input them afterwards again. - - - - - - - - - - - - - - From 9885a1f4bdf9876f0d35caf4d3bb2313aeaea751 Mon Sep 17 00:00:00 2001 From: noppelmax Date: Wed, 9 Jun 2021 23:53:04 +0200 Subject: [PATCH 14/26] Update i18n/i18n.de.yml Co-authored-by: haslersn --- i18n/i18n.de.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/i18n/i18n.de.yml b/i18n/i18n.de.yml index 7fe3da34..55e9be21 100644 --- a/i18n/i18n.de.yml +++ b/i18n/i18n.de.yml @@ -84,16 +84,3 @@ de: Dazu benötigst du eine Mailadresse, Telefonnummer und die PLZ deines Impfzentrums. Die Daten werden anschließend lokal in der Datei '%{filename}' abgelegt." Du musst sie zukünftig nicht mehr eintragen. - - - - - - - - - - - - - From 3bb6616bad44dfd196092a19a2f062e78f668786 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:04:28 +0200 Subject: [PATCH 15/26] Fix menu translation --- i18n/i18n.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index e7cb17cf..0ab7e9b2 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -20,7 +20,7 @@ en: SearchForAppointment: Search for appointment ShowAdvancedSettings: Show advanced settings HideAdvancedSettings: Hide advanced settings - Menu: Menu + Menu: What would you like to do PathContactData: Path to contactdata file ConfigureOnlyDescription: Only create and save contactdata file ReadOnlyDescription: Do not ask for contactdata. Throughs errors if contactdata file is not present From 37255c98e46bcdfbd314280b00152787378ae63c Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:07:23 +0200 Subject: [PATCH 16/26] Set default language to `de` --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 81c20ca0..679db297 100755 --- a/main.py +++ b/main.py @@ -527,6 +527,7 @@ def main(): """) # Lade Sprachen i18n.load_path.append(os.path.join(PATH, "i18n")) + i18n.set('locale', 'de') i18n.set('fallback', 'de') # Auf aktuelle Version prüfen From 73141f1257c1e6afefdc740b4a6e0fb5074bd980 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:12:57 +0200 Subject: [PATCH 17/26] Some fixes to the translation (en) --- i18n/i18n.en.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index 4d9c7982..5ccc4a33 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -16,7 +16,7 @@ en: ErrorIn: Error in InvalidInputPleaseTryAgain: Invalid input! Please try again Error: Fehler - GenerateVacCode: Generate vaccination code + GenerateVacCode: Generate vaccination code ('Vermittlungscode') SearchForAppointment: Search for appointment ShowAdvancedSettings: Show advanced settings HideAdvancedSettings: Hide advanced settings @@ -45,7 +45,7 @@ en: HouseNumber: House number PLZ: PLZ/ZIP code Phonenumber: Phonenumber - PLZVacCenters: PLZ/ZIP codes of vac. centers + PLZVacCenters: PLZ/ZIP codes of vaccination centers InputPLZs: |- You can use one 'Vermittlungscode' to search at multiple vaccination centers. Please find a table with vaccination centers and their grouping here: @@ -72,7 +72,7 @@ en: FromTime: From time (leave blank to skip) ToTime: To time (leave blank to skip) InputVacCodeAndContactdata: |- - Please insert you vaccination code and your contact data first. + Please insert you 'Vermittlungscode' and your contact data first. Both will be save in the file '%{filename}'. There is no need to enter them again afterwards. ShouldContactdataBeLoaded: |- @@ -81,6 +81,6 @@ en: Contactdata for %{firstname} %{lastname} where loaded InfoVacCode: |- You can create an appointment now. - You will need your mailaddress, phonenumber and the plz/zip code of your desired vaccation center. + You will need your mailaddress, phonenumber and the plz/zip code of your desired vaccination center. This information will be saved in the file '%{filename}' afterwards. You will not need to input them afterwards again. From c8b8db7d3d316d1f1d248386cedac42f9d479329 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:31:15 +0200 Subject: [PATCH 18/26] Add basic test for translation files. Checks if they contain the same number of entries and is every key is present in every language file. --- test/test_translationfiles.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/test_translationfiles.py diff --git a/test/test_translationfiles.py b/test/test_translationfiles.py new file mode 100644 index 00000000..c4be139b --- /dev/null +++ b/test/test_translationfiles.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import os +import yaml + + +languages = ["en","de"] +dat = [] + + +for lang in languages: + with open(os.path.join("..","i18n","i18n."+lang+".yml"),'r') as fs: + try: + dat.append(yaml.safe_load(fs)[lang]) + except yaml.YAMLError as exc: + print(exc) + exit(-1) + + +lengths = [ len(x) for x in dat ] + +l = lengths[0] + +# Check that every language has the same number of entries! +for i in range(len(languages)): + if len(dat[i]) != l: + print(f"Language {languages[i]} has to less entries!") + exit(-1) + +# Check if all keys are present in every language +list(dat[0].keys()) +for i in range(len(languages)): + if list(dat[0].keys()) != list(dat[i].keys()): + print(f"Keys are different for language {languages[0]} and {languages[i]}") + exit(-1) + +print("Everything seems fine!") + + +print(l) \ No newline at end of file From b01a9c37f796b678883925bb88f809694524861d Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:34:56 +0200 Subject: [PATCH 19/26] Patch from haslersn --- shell.nix | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shell.nix b/shell.nix index 11e44496..fd142a49 100644 --- a/shell.nix +++ b/shell.nix @@ -61,6 +61,16 @@ let }; doCheck = false; }; + + python-i18n = buildPythonPackage rec { + pname = "python-i18n"; + version = "0.3.9"; + src = fetchPypi { + inherit pname version; + sha256 = "1s74f7sgay30kj80pqx9aa74d0slwklfzjynzgmsgwsb6v9g75yz"; + }; + doCheck = false; + }; }; }; in @@ -77,6 +87,9 @@ mkShell { p.cloudscraper p.idna p.plyer + p.python-i18n + p.python-prctl + p.pyyaml p.selenium p.urllib3 ])) From 2f3c3fb9d783e052ff3459afc22c9b7ce07bd4a6 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:42:36 +0200 Subject: [PATCH 20/26] Add note to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2bc57317..5484d38e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![build](https://github.com/iamnotturner/vaccipy/actions/workflows/build.yaml/badge.svg?branch=master)](https://github.com/iamnotturner/vaccipy/actions/workflows/build.yaml) [![deploy](https://github.com/iamnotturner/vaccipy/actions/workflows/deploy.yaml/badge.svg)](https://github.com/iamnotturner/vaccipy/actions/workflows/deploy.yaml) +English: Please run the script with `--lang=en` appended to get english logs. + Automatisierte Impfterminbuchung auf [www.impfterminservice.de](https://www.impfterminservice.de/). > **Disclaimer** From 7629f4c0e795d87d0bd98c937521b43fafe08e16 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:44:53 +0200 Subject: [PATCH 21/26] =?UTF-8?q?Translate=20"SMS-Code=20ung=C3=BCltig"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- i18n/i18n.de.yml | 1 + i18n/i18n.en.yml | 1 + main.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/i18n/i18n.de.yml b/i18n/i18n.de.yml index 55e9be21..999c7345 100644 --- a/i18n/i18n.de.yml +++ b/i18n/i18n.de.yml @@ -84,3 +84,4 @@ de: Dazu benötigst du eine Mailadresse, Telefonnummer und die PLZ deines Impfzentrums. Die Daten werden anschließend lokal in der Datei '%{filename}' abgelegt." Du musst sie zukünftig nicht mehr eintragen. + SMSCodeInvalid: SMS-Code ungültig \ No newline at end of file diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index 5ccc4a33..b1199e1e 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -84,3 +84,4 @@ en: You will need your mailaddress, phonenumber and the plz/zip code of your desired vaccination center. This information will be saved in the file '%{filename}' afterwards. You will not need to input them afterwards again. + SMSCodeInvalid: SMS-Code invalid diff --git a/main.py b/main.py index 00cd56bf..6ae8f3ef 100755 --- a/main.py +++ b/main.py @@ -351,7 +351,7 @@ def gen_code(kontaktdaten): if its.code_bestaetigen(token, cookies, sms_pin, plz_impfzentrum): print(f"\n{i18n.t('i18n.ContinueSearchForAppointment')}.") return True - print("\nSMS-Code ungültig") + print(f"\n{i18n.t('i18n.SMSCodeInvalid')}") print(f"{i18n.t('i18n.CodeGenerationFailed')}.") return False From d48c54b644501f881043289c6576709803bfa5ee Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 00:58:33 +0200 Subject: [PATCH 22/26] Add possibility to change language via menu --- i18n/i18n.de.yml | 5 ++++- i18n/i18n.en.yml | 3 +++ main.py | 20 ++++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/i18n/i18n.de.yml b/i18n/i18n.de.yml index 999c7345..7c38e903 100644 --- a/i18n/i18n.de.yml +++ b/i18n/i18n.de.yml @@ -84,4 +84,7 @@ de: Dazu benötigst du eine Mailadresse, Telefonnummer und die PLZ deines Impfzentrums. Die Daten werden anschließend lokal in der Datei '%{filename}' abgelegt." Du musst sie zukünftig nicht mehr eintragen. - SMSCodeInvalid: SMS-Code ungültig \ No newline at end of file + SMSCodeInvalid: SMS-Code ungültig + LangDescription: Sprache auswählen (en,de) + LangInvalid: Sprache ungültig + ChangeLanguage: Sprache ändern \ No newline at end of file diff --git a/i18n/i18n.en.yml b/i18n/i18n.en.yml index b1199e1e..6be32cc6 100644 --- a/i18n/i18n.en.yml +++ b/i18n/i18n.en.yml @@ -85,3 +85,6 @@ en: This information will be saved in the file '%{filename}' afterwards. You will not need to input them afterwards again. SMSCodeInvalid: SMS-Code invalid + LangDescription: Select language (en,de) + LangInvalid: Selected language invalid + ChangeLanguage: Change language diff --git a/main.py b/main.py index 6ae8f3ef..47c8cb3f 100755 --- a/main.py +++ b/main.py @@ -412,6 +412,11 @@ def main(): "--configure-notifications", action='store_true', help="Gibt bei der Erfassung der Kontaktdaten die Möglichkeit, Benachrichtungen über Pushover und Telegram zu konfigurieren.") + base_subparser.add_argument( + "-l", + "--lang", + action='store_true', + help=i18n.t("i18n.LangDescription")) parser_search = subparsers.add_parser( "search", parents=[base_subparser], help=i18n.t("i18n.SearchForAppointment")) @@ -439,6 +444,8 @@ def main(): args.retry_sec = 60 if not hasattr(args, "configure_notifications"): args.configure_notifications = False + if not hasattr(args, "lang"): + args.lang = "de" try: validate_args(args) @@ -462,7 +469,7 @@ def main(): while True: print( - i18n.t("Menu") + "?\n" + f"{i18n.t('i18n.Menu')}?\n" f"[1] {i18n.t('i18n.SearchForAppointment')}\n" f"[2] {i18n.t('i18n.GenerateVacCode')}\n" f"[x] {i18n.t('i18n.HideAdvancedSettings') if extended_settings else i18n.t('i18n.ShowAdvancedSettings')}\n") @@ -472,7 +479,8 @@ def main(): f"[c] --configure-only {i18n.t('i18n.deactivate') if args.configure_only else i18n.t('i18n.activate')}\n" f"[r] --read-only {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n" f"[s] --retry-sec {i18n.t('i18n.set')}\n" - f"[n] --configure-notifications {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n\n") + f"[n] --configure-notifications {i18n.t('i18n.deactivate') if args.read_only else i18n.t('i18n.activate')}\n" + f"[l] --lang {i18n.t('i18n.ChangeLanguage')}: {'[en],de' if args.lang=='en' else 'en,[de]'}\n\n") # TODO This is just a quick fix! Optimize! option = input("> Option: ").lower() @@ -501,6 +509,14 @@ def main(): f"--read-only {i18n.t('i18n.deactivate') if not args.read_only else i18n.t('i18n.activate')}.") elif extended_settings and option == "s": args.retry_sec = int(input("> --retry-sec=")) + elif extended_settings and option == "l": + lang = input("> --lang=") + # Check if lang is available. + if lang in ["de","en"]: + args.lang = lang + i18n.set('locale', lang) + else: + print(i18n.t('i18n.LangInvalid')) elif extended_settings and option == "n": new_args = copy.copy(args) new_args.configure_notifications = not new_args.configure_notifications From 752c4d5e38fce2869753fee17217486737b98114 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 01:30:07 +0200 Subject: [PATCH 23/26] Add --lang/-l to argument list --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 47c8cb3f..c5cee46a 100755 --- a/main.py +++ b/main.py @@ -445,7 +445,7 @@ def main(): if not hasattr(args, "configure_notifications"): args.configure_notifications = False if not hasattr(args, "lang"): - args.lang = "de" + args.lang = 'de' try: validate_args(args) @@ -453,6 +453,8 @@ def main(): parser.error(str(exc)) # parser.error terminates the program with status code 2. + i18n.set('locale', args.lang) + if args.command is not None: try: if args.command == "search": From 2b5250f809be02d27d28e9af9519863fd2929c31 Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 01:30:55 +0200 Subject: [PATCH 24/26] fix format, add --lang to parser instead of base_subparser --- main.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index c5cee46a..f1ccbb5b 100755 --- a/main.py +++ b/main.py @@ -390,6 +390,14 @@ def main(): create_missing_dirs(PATH) parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--lang", + default='de', + choices=['de', 'en'], + action="store", + help=i18n.t("i18n.LangDescription")) + subparsers = parser.add_subparsers(help="commands", dest="command") base_subparser = argparse.ArgumentParser(add_help=False) @@ -412,14 +420,13 @@ def main(): "--configure-notifications", action='store_true', help="Gibt bei der Erfassung der Kontaktdaten die Möglichkeit, Benachrichtungen über Pushover und Telegram zu konfigurieren.") - base_subparser.add_argument( - "-l", - "--lang", - action='store_true', - help=i18n.t("i18n.LangDescription")) + parser_search = subparsers.add_parser( - "search", parents=[base_subparser], help=i18n.t("i18n.SearchForAppointment")) + "search", + parents=[base_subparser], + help=i18n.t("i18n.SearchForAppointment")) + parser_search.add_argument( "-s", "--retry-sec", From 5ff941474bf15bbbb1b86cf72022bf3cb6bf261b Mon Sep 17 00:00:00 2001 From: Maximilian Noppel Date: Thu, 10 Jun 2021 01:42:08 +0200 Subject: [PATCH 25/26] Use global array for available languages --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index f1ccbb5b..985b704b 100755 --- a/main.py +++ b/main.py @@ -15,7 +15,7 @@ get_current_version, pushover_validation, telegram_validation PATH = os.path.dirname(os.path.realpath(__file__)) - +LANGUAGES = ["de","en"] def update_kontaktdaten_interactive( known_kontaktdaten, @@ -394,7 +394,7 @@ def main(): "-l", "--lang", default='de', - choices=['de', 'en'], + choices=LANGUAGES, action="store", help=i18n.t("i18n.LangDescription")) @@ -521,7 +521,7 @@ def main(): elif extended_settings and option == "l": lang = input("> --lang=") # Check if lang is available. - if lang in ["de","en"]: + if lang in LANGUAGES: args.lang = lang i18n.set('locale', lang) else: From 8036e7c10414cafe12fd9e0eaf4d214e5a185421 Mon Sep 17 00:00:00 2001 From: noppelmax Date: Wed, 16 Jun 2021 09:09:59 +0200 Subject: [PATCH 26/26] Update shell.nix Co-authored-by: haslersn --- shell.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/shell.nix b/shell.nix index fd142a49..5e1af48e 100644 --- a/shell.nix +++ b/shell.nix @@ -88,7 +88,6 @@ mkShell { p.idna p.plyer p.python-i18n - p.python-prctl p.pyyaml p.selenium p.urllib3