diff --git a/installer/languages/json2nsh.py b/installer/languages/json2nsh.py new file mode 100755 index 000000000..568ab3a02 --- /dev/null +++ b/installer/languages/json2nsh.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Picard, the next-generation MusicBrainz tagger +# +# Copyright (C) 2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import glob +import json +import os.path + +import nshutil as nsh + + +def language_from_filename(path): + lang = os.path.splitext(os.path.basename(path))[0] + return (nsh.code_to_language(lang), lang) + + +def write_langstring(f, language, identifier, text): + langstring = nsh.make_langstring(language, identifier, text) + f.write(langstring) + + +def main(): + scriptdir = os.path.dirname(os.path.abspath(__file__)) + sourcesdir = os.path.join(scriptdir, 'sources') + + for path in glob.glob(os.path.join(sourcesdir, '*.json')): + language, language_code = language_from_filename(path) + if not language: + print(f'Unknown language code "{language_code}", skipping') + continue + target_file = os.path.join(scriptdir, f'{language}.nsh') + with open(path, 'r', encoding='utf-8') as infile: + data = json.loads(infile.read()) + with open(target_file, 'w+', encoding='utf-8') as outfile: + for identifier, text in data.items(): + write_langstring(outfile, language, identifier, text) + + +if __name__ == "__main__": + main() diff --git a/installer/languages/nsh2json.py b/installer/languages/nsh2json.py new file mode 100755 index 000000000..4779ae467 --- /dev/null +++ b/installer/languages/nsh2json.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Picard, the next-generation MusicBrainz tagger +# +# Copyright (C) 2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import glob +import json +import os.path + +import nshutil as nsh + + +def language_from_filename(path): + lang = os.path.splitext(os.path.basename(path))[0] + return (lang, nsh.language_to_code(lang)) + + +def extract_strings(f): + for line in f: + parsed = nsh.parse_langstring(line) + if parsed: + yield parsed + + +def main(): + scriptdir = os.path.dirname(os.path.abspath(__file__)) + + for path in glob.glob(os.path.join(scriptdir, '*.nsh')): + language, language_code = language_from_filename(path) + if not language_code: + print(f'Unknown language "{language}", skipping') + continue + target_file = os.path.join(scriptdir, 'sources', f'{language_code}.json') + with open(path, 'r', encoding='utf-8') as infile: + output = {} + for identifier, text in extract_strings(infile): + output[identifier] = text + + with open(target_file, 'w+', encoding='utf-8') as outfile: + outfile.write(json.dumps(output, indent=4)) + + +if __name__ == "__main__": + main() diff --git a/installer/languages/nshutil.py b/installer/languages/nshutil.py new file mode 100644 index 000000000..cd70a3fdd --- /dev/null +++ b/installer/languages/nshutil.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Picard, the next-generation MusicBrainz tagger +# +# Copyright (C) 2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import re + + +LANGUAGES = { + 'Arabic': 'ar', + 'Catalan': 'ca', + 'Czech': 'cs', + 'Danish': 'da', + 'Dutch': 'nl', + 'English': 'en', + 'Estonian': 'et', + 'Finnish': 'fi', + 'French': 'fr', + 'German': 'de', + 'Greek': 'el', + 'Hebrew': 'he', + 'Italian': 'it', + 'Japanese': 'ja', + 'Korean': 'ko', + 'Norwegian': 'nb', + 'Polish': 'pl', + 'Portuguese': 'pt', + 'PortugueseBR': 'pt_BR', + 'Russian': 'ru', + 'SimpChinese': 'zh-Hans', + 'Slovak': 'sk', + 'Slovenian': 'sl', + 'Spanish': 'es', + 'Swedish': 'sv', + 'TradChinese': 'zh-Hant', + 'Turkish': 'tr', + 'Ukrainian': 'uk', +} + +_R_LANGUAGES = dict([(code, name) for name, code in LANGUAGES.items()]) + +# See https://nsis.sourceforge.io/Docs/Chapter4.html#varstrings +ESCAPE_CHARS = { + r'$\r': '\r', + r'$\n': '\n', + r'$\t': '\t', + r'$\"': '"', + r'$\'': "'", + r'$\`': '`', +} + +RE_LANGSTRING_LINE = re.compile(r'LangString\s+(?P[A-Za-z0-9_]+)\s+\${LANG_[A-Z]+}\s+["\'`](?P.*)["\'`]$') + + +def language_to_code(language): + return LANGUAGES.get(language) + + +def code_to_language(language_code): + return _R_LANGUAGES.get(language_code) + + +def escape_string(text): + for escape, char in ESCAPE_CHARS.items(): + if char in ("'", "`"): # No need to escape quotes other than "" + continue + text = text.replace(char, escape) + return text + + +def unescape_string(text): + for escape, char in ESCAPE_CHARS.items(): + text = text.replace(escape, char) + return text + + +def parse_langstring(line): + match = RE_LANGSTRING_LINE.match(line) + if match: + return ( + match.group('identifier'), + unescape_string(match.group('text')) + ) + else: + return None + + +def make_langstring(language, identifier, text): + language = language.upper() + text = escape_string(text) + return f'LangString {identifier} ${{LANG_{language}}} "{text}"\n' diff --git a/installer/languages/sources/de.json b/installer/languages/sources/de.json new file mode 100644 index 000000000..71b0ece12 --- /dev/null +++ b/installer/languages/sources/de.json @@ -0,0 +1,16 @@ +{ + "MsgAlreadyInstalled": "${PRODUCT_NAME} ist bereits installiert. \n\nKlicken Sie \u201eOK\u201c um die vorherige Version zu deinstallieren oder \u201eAbbrechen\u201c um das Update abzubrechen.", + "MsgApplicationRunning": "Die Anwendung ${PRODUCT_NAME} wird ausgef\u00fchrt. Bitte schlie\u00dfen und erneut versuchen.", + "MsgRequires64Bit": "Diese Version von ${PRODUCT_NAME} erfordert ein 64-bit Windows-System.", + "MuiDescriptionRequired": "Installiert ${PRODUCT_NAME} mit den f\u00fcr die Ausf\u00fchrung erforderlichen Dateien.", + "MuiDescriptionLang": "Installiert \u00dcbersetzungen von ${PRODUCT_NAME} in verschiedenen Sprachen.", + "MuiDescriptionShortcuts": "Installiert Verkn\u00fcpfungen, um ${PRODUCT_NAME} zu starten.", + "MuiDescriptionDesktop": "Installiert eine Verkn\u00fcpfung auf dem Desktop.", + "MuiDescriptionStarteMenu": "Installiert eine Verkn\u00fcpfung im Startmen\u00fc.", + "OptionRemoveSettings": "Einstellungen und pers\u00f6nliche Daten entfernen", + "SectionDesktop": "Desktop", + "SectionLanguages": "Sprachen", + "SectionRequired": "Programmdateien (erforderlich)", + "SectionShortcuts": "Verkn\u00fcpfungen", + "SectionStartMenu": "Startmen\u00fc" +} \ No newline at end of file diff --git a/installer/languages/sources/en.json b/installer/languages/sources/en.json new file mode 100644 index 000000000..863c26d68 --- /dev/null +++ b/installer/languages/sources/en.json @@ -0,0 +1,16 @@ +{ + "MsgAlreadyInstalled": "${PRODUCT_NAME} is already installed. \n\nClick \"OK\" to uninstall the previous version or \"Cancel\" to cancel this upgrade.", + "MsgApplicationRunning": "The application ${PRODUCT_NAME} is running. Please close it and try again.", + "MsgRequires64Bit": "This version of ${PRODUCT_NAME} requires a 64-bit Windows system.", + "MuiDescriptionRequired": "Installs ${PRODUCT_NAME} along with the necessary files for it run.", + "MuiDescriptionLang": "Installs translations of ${PRODUCT_NAME} in different languages.", + "MuiDescriptionShortcuts": "Installs shortcuts to launch ${PRODUCT_NAME}.", + "MuiDescriptionDesktop": "Installs a shortcut on the desktop.", + "MuiDescriptionStarteMenu": "Installs a shortcut in the Start Menu.", + "OptionRemoveSettings": "Remove settings and personal data", + "SectionDesktop": "Desktop", + "SectionLanguages": "Languages", + "SectionRequired": "Program files (required)", + "SectionShortcuts": "Shortcuts", + "SectionStartMenu": "Start menu" +} \ No newline at end of file diff --git a/installer/languages/sources/fr.json b/installer/languages/sources/fr.json new file mode 100644 index 000000000..da4b201ca --- /dev/null +++ b/installer/languages/sources/fr.json @@ -0,0 +1,16 @@ +{ + "MsgAlreadyInstalled": "${PRODUCT_NAME} est d\u00e9j\u00e0 install\u00e9. \n\nCliquez sur \u00ab\u00a0OK\u00a0\u00bb pour d\u00e9sinstaller la version pr\u00e9c\u00e9dente ou \u00ab\u00a0Annuler\u00a0\u00bb pour annuler cette mise \u00e0 jour.", + "MsgApplicationRunning": "L\u2019application ${PRODUCT_NAME} est en cours d\u2019ex\u00e9cution. Veuillez la stopper et essayer \u00e0 nouveau.", + "MsgRequires64Bit": "Cette version de ${PRODUCT_NAME} requiert un syst\u00e8me Windows 64 bits.", + "MuiDescriptionRequired": "Installe ${PRODUCT_NAME} ainsi que les fichiers n\u00e9cessaires \u00e0 son ex\u00e9cution.", + "MuiDescriptionLang": "Installe les traductions de ${PRODUCT_NAME} dans diff\u00e9rentes langues.", + "MuiDescriptionShortcuts": "Installe les raccourcis pour lancer ${PRODUCT_NAME}.", + "MuiDescriptionDesktop": "Installe un raccourci sur le bureau.", + "MuiDescriptionStarteMenu": "Installe un raccourci dans le menu D\u00e9marrer.", + "OptionRemoveSettings": "Supprimer les pr\u00e9f\u00e9rences et les donn\u00e9es personnelles", + "SectionDesktop": "Bureau", + "SectionLanguages": "Langues", + "SectionRequired": "Fichiers du programme (requis)", + "SectionShortcuts": "Raccourcis", + "SectionStartMenu": "Menu D\u00e9marrer" +} \ No newline at end of file diff --git a/installer/languages/sources/it.json b/installer/languages/sources/it.json new file mode 100644 index 000000000..55956eea3 --- /dev/null +++ b/installer/languages/sources/it.json @@ -0,0 +1,16 @@ +{ + "MsgAlreadyInstalled": "${PRODUCT_NAME} \u00e8 gi\u00e0 installato.\n\nFai clic su 'OK' per disinstallare la versione installata o su 'Annulla' per annullare l'aggiornamento.", + "MsgApplicationRunning": "L'applicazione ${PRODUCT_NAME} \u00e8 in esecuzione.\nChiudi l'applicazione e riprova.", + "MsgRequires64Bit": "Questa versione di ${PRODUCT_NAME} richiede un sistema Windows a 64bit.", + "MuiDescriptionRequired": "Installa ${PRODUCT_NAME} e i relativi file necessari alla sua esecuzione.", + "MuiDescriptionLang": "Installa le traduzioni di ${PRODUCT_NAME} per le differenti lingue.", + "MuiDescriptionShortcuts": "Installa collegamento per eseguire ${PRODUCT_NAME}.", + "MuiDescriptionDesktop": "Installa collegamento sul desktop.", + "MuiDescriptionStarteMenu": "Installa collegamento nel menu Start.", + "OptionRemoveSettings": "Elimina impostazioni e dati personali", + "SectionDesktop": "Desktop", + "SectionLanguages": "Lingue", + "SectionRequired": "File programma (richiesti)", + "SectionShortcuts": "Collegamenti", + "SectionStartMenu": "Menu Start" +} \ No newline at end of file diff --git a/installer/languages/sources/nl.json b/installer/languages/sources/nl.json new file mode 100644 index 000000000..6f4d93cd6 --- /dev/null +++ b/installer/languages/sources/nl.json @@ -0,0 +1,16 @@ +{ + "MsgAlreadyInstalled": "${PRODUCT_NAME} is al ge\u00efnstalleerd. \n\nKlik op \u201cOK\u201d om de vorige versie te verwijderen of op \u201cAnnuleren\u201d om de installatie te annuleren.", + "MsgApplicationRunning": "De applicatie ${PRODUCT_NAME} is nog actief. Sluit haar af en probeer het opnieuw.", + "MsgRequires64Bit": "Voor deze versie van ${PRODUCT_NAME} heb je een 64 bitssysteem nodig.", + "MuiDescriptionRequired": "Installeert ${PRODUCT_NAME} en de bestanden die het nodig heeft om te draaien.", + "MuiDescriptionLang": "Installeert vertalingen van ${PRODUCT_NAME}.", + "MuiDescriptionShortcuts": "Installeert snelkoppelingen om ${PRODUCT_NAME} te starten.", + "MuiDescriptionDesktop": "Installeert een snelkoppeling op het bureaublad.", + "MuiDescriptionStarteMenu": "Installeert een snelkoppeling in het startmenu.", + "OptionRemoveSettings": "Verwijder instellingen en persoonlijke gegevens", + "SectionDesktop": "Bureaublad", + "SectionLanguages": "Talen", + "SectionRequired": "Programmabestanden (vereist)", + "SectionShortcuts": "Snelkoppelingen", + "SectionStartMenu": "Startmenu" +} \ No newline at end of file