From d4ce60534ad3a78866e2e7ca6412f2de98ba8a1f Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 2 Sep 2010 10:59:21 +0200 Subject: [PATCH] Reworked the translation script. Renamed the translation files to just their language codes for easier integration into translation tools. --- resources/i18n/{openlp_af.ts => af.ts} | 0 resources/i18n/{openlp_de.ts => de.ts} | 0 resources/i18n/{openlp_en.ts => en.ts} | 0 resources/i18n/{openlp_en_GB.ts => en_GB.ts} | 0 resources/i18n/{openlp_en_ZA.ts => en_ZA.ts} | 2 +- resources/i18n/{openlp_es.ts => es.ts} | 0 resources/i18n/{openlp_et.ts => et.ts} | 0 resources/i18n/{openlp_hu.ts => hu.ts} | 0 resources/i18n/{openlp_ko.ts => ko.ts} | 0 resources/i18n/{openlp_nb.ts => nb.ts} | 0 resources/i18n/{openlp_pt_BR.ts => pt_BR.ts} | 0 resources/i18n/{openlp_sv.ts => sv.ts} | 0 scripts/translation_utils.py | 444 +++++++++++-------- 13 files changed, 270 insertions(+), 176 deletions(-) rename resources/i18n/{openlp_af.ts => af.ts} (100%) rename resources/i18n/{openlp_de.ts => de.ts} (100%) rename resources/i18n/{openlp_en.ts => en.ts} (100%) rename resources/i18n/{openlp_en_GB.ts => en_GB.ts} (100%) rename resources/i18n/{openlp_en_ZA.ts => en_ZA.ts} (99%) rename resources/i18n/{openlp_es.ts => es.ts} (100%) rename resources/i18n/{openlp_et.ts => et.ts} (100%) rename resources/i18n/{openlp_hu.ts => hu.ts} (100%) rename resources/i18n/{openlp_ko.ts => ko.ts} (100%) rename resources/i18n/{openlp_nb.ts => nb.ts} (100%) rename resources/i18n/{openlp_pt_BR.ts => pt_BR.ts} (100%) rename resources/i18n/{openlp_sv.ts => sv.ts} (100%) diff --git a/resources/i18n/openlp_af.ts b/resources/i18n/af.ts similarity index 100% rename from resources/i18n/openlp_af.ts rename to resources/i18n/af.ts diff --git a/resources/i18n/openlp_de.ts b/resources/i18n/de.ts similarity index 100% rename from resources/i18n/openlp_de.ts rename to resources/i18n/de.ts diff --git a/resources/i18n/openlp_en.ts b/resources/i18n/en.ts similarity index 100% rename from resources/i18n/openlp_en.ts rename to resources/i18n/en.ts diff --git a/resources/i18n/openlp_en_GB.ts b/resources/i18n/en_GB.ts similarity index 100% rename from resources/i18n/openlp_en_GB.ts rename to resources/i18n/en_GB.ts diff --git a/resources/i18n/openlp_en_ZA.ts b/resources/i18n/en_ZA.ts similarity index 99% rename from resources/i18n/openlp_en_ZA.ts rename to resources/i18n/en_ZA.ts index 3fcd45621..ebfc86a2e 100644 --- a/resources/i18n/openlp_en_ZA.ts +++ b/resources/i18n/en_ZA.ts @@ -2993,7 +2993,7 @@ The content encoding is not UTF-8. Presentation - Presentation + Presentation diff --git a/resources/i18n/openlp_es.ts b/resources/i18n/es.ts similarity index 100% rename from resources/i18n/openlp_es.ts rename to resources/i18n/es.ts diff --git a/resources/i18n/openlp_et.ts b/resources/i18n/et.ts similarity index 100% rename from resources/i18n/openlp_et.ts rename to resources/i18n/et.ts diff --git a/resources/i18n/openlp_hu.ts b/resources/i18n/hu.ts similarity index 100% rename from resources/i18n/openlp_hu.ts rename to resources/i18n/hu.ts diff --git a/resources/i18n/openlp_ko.ts b/resources/i18n/ko.ts similarity index 100% rename from resources/i18n/openlp_ko.ts rename to resources/i18n/ko.ts diff --git a/resources/i18n/openlp_nb.ts b/resources/i18n/nb.ts similarity index 100% rename from resources/i18n/openlp_nb.ts rename to resources/i18n/nb.ts diff --git a/resources/i18n/openlp_pt_BR.ts b/resources/i18n/pt_BR.ts similarity index 100% rename from resources/i18n/openlp_pt_BR.ts rename to resources/i18n/pt_BR.ts diff --git a/resources/i18n/openlp_sv.ts b/resources/i18n/sv.ts similarity index 100% rename from resources/i18n/openlp_sv.ts rename to resources/i18n/sv.ts diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 340cab11f..f52af4984 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -24,13 +24,31 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -# Short description -# Steps for creating languages: -# 1. make sure that the openlp_en.ts file exist -# 2. go to scripts folder and start: -# python translation_utils.py -a -############################################################################### +""" +This script is used to maintain the translation files in OpenLP. It downloads +the latest translation files from the Pootle translation server, updates the +local translation files from both the source code and the files from Pootle, +and can also generate the compiled translation files. + +Create New Language +------------------- + +To create a new language, simply run this script with the ``-a`` command line +option:: + + @:~$ ./translation_utils.py -a + +Update Translation Files +------------------------ + +The best way to update the translations is to download the files from Pootle, +and then update the local files using both the downloaded files and the source. +This is done easily via the ``-d``, ``-p`` and ``-u`` options:: + + @:~$ ./translation_utils.py -dpu + +""" import os import urllib import re @@ -39,201 +57,277 @@ from optparse import OptionParser from PyQt4 import QtCore from BeautifulSoup import BeautifulSoup -class TranslationUtils(object): +SERVER_URL = u'http://pootle.projecthq.biz/export/openlp/' +IGNORED_PATHS = [u'scripts'] +IGNORED_FILES = [u'setup.py'] + +verbose_mode = False + +class Command(object): + """ + Provide an enumeration of commands. + """ + Download = 1 + Create = 2 + Prepare = 3 + Update = 4 + Generate = 5 + +class CommandStack(object): + """ + This class provides an iterable stack. + """ def __init__(self): - self.ignore_paths = [u'./scripts'] - self.ignore_files = [u'setup.py'] - self.server_url = u'http://pootle.projecthq.biz/export/openlp/' - self.cmd_stack = [] - self.stack_count = 0 - self.verbose = False - + self.current_index = 0 + self.data = [] - def process_stack(self): - if len(self.cmd_stack) > 0: - if len(self.cmd_stack) == self.stack_count: - print u'Process %d commands' % self.stack_count - print u'%d. ' % (self.stack_count-len(self.cmd_stack)+1), - command = self.cmd_stack.pop(0) - if len(command) > 1: - command[0](command[1]) - else: - command[0]() + def __len__(self): + return len(self.data) + + def __getitem__(self, index): + if self.data[index].get(u'arguments'): + return self.data[index][u'command'], self.data[index][u'arguments'] else: - print "Finished all commands" + return self.data[index][u'command'] + + def __iter__(self): + return self + + def next(self): + if self.current_index == len(self.data): + raise StopIteration + else: + current_item = self.data[self.current_index][u'command'] + self.current_index += 1 + return current_item + + def append(self, command, **kwargs): + data = {u'command': command} + if u'arguments' in kwargs: + data[u'arguments'] = kwargs[u'arguments'] + self.data.append(data) + + def reset(self): + self.current_index = 0 - def downloadTranslations(self): - print 'Download Translation files from HQ-Server' - page = urllib.urlopen(u'%s' % (self.server_url)) - soup = BeautifulSoup(page) - languages = soup.findAll(text=re.compile(".*\.ts")) - for language in languages: - filename = os.path.join(u'..', u'resources', u'i18n', - u'openlp_%s' % language) - self.printVerbose(u'Get Translation File: %s' % filename) - self.get_and_write_file(language, filename) - print u' done' - self.process_stack() +def print_verbose(text): + """ + This method checks to see if we are in verbose mode, and if so prints + ``text`` out. - def get_and_write_file(self, language, filename): - page = urllib.urlopen(u'%s%s' % (self.server_url, language)) - content = page.read().decode('utf8') - page.close() - file = open(filename, u'w') - file.write(content.encode('utf8')) - file.close() - - def creation(self, language): - print "Create new Translation File" - """ - Use this option to create a new translation file - this function: - * create the new *.ts file - """ - filename = os.path.join(u'..', u'resources', u'i18n', - u'openlp_%s.ts' % language) - self.get_and_write_file(u'en.ts', filename) - self.printVerbose(""" - Please remind: For permanent providing this language: - this language name have to append to the global list - variable "translations" in this file - and this file have to be uploaded to the Pootle Server - Please contact the developers! - """) - print u' done' - self.process_stack() - - - def preparation(self): - print u'Generating the openlp.pro file' - stringlist = [] - start_dir = os.path.join(u'..') - for root, dirs, files in os.walk(start_dir): - for file in files: - path = u'%s' % root - path = path.replace('\\','/') - path = path.replace('..','.') - - if file.startswith(u'hook-') or file.startswith(u'test_'): - continue + ``text`` + The text to print. + """ + global verbose_mode + if verbose_mode: + print u' %s' % text - cond = False - for search in self.ignore_paths: - if path.startswith(search): - cond = True - if cond: - continue - cond = False - for search in self.ignore_files: - if search == file: - cond = True - if cond: - continue - - if file.endswith(u'.py'): +def run(command): + """ + This method runs an external application. + + ``command`` + The command to run. + """ + print_verbose(command) + process = QtCore.QProcess() + process.start(command) + while (process.waitForReadyRead()): + print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll())) + print_verbose(u'Error(s):\n%s' % process.readAllStandardError()) + print_verbose(u'Output:\n%s' % process.readAllStandardOutput()) + print u' Done.' + +def download_file(source_filename, dest_filename): + """ + Download a file and save it to disk. + + ``source_filename`` + The file to download. + + ``dest_filename`` + The new local file name. + """ + print_verbose(u'Downloading from: %s' % (SERVER_URL + source_filename)) + page = urllib.urlopen(SERVER_URL + source_filename) + content = page.read().decode('utf8') + page.close() + file = open(dest_filename, u'w') + file.write(content.encode('utf8')) + file.close() + +def download_translations(): + """ + This method downloads the translation files from the Pootle server. + """ + print 'Download translation files from Pootle' + page = urllib.urlopen(SERVER_URL) + soup = BeautifulSoup(page) + languages = soup.findAll(text=re.compile(r'.*\.ts')) + for language in languages: + filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', + language) + print_verbose(u'Get Translation File: %s' % filename) + download_file(language, filename) + print u' Done.' + +def prepare_project(): + """ + This method creates the project file needed to update the translation files + and compile them into .qm files. + """ + print u'Generating the openlp.pro file' + lines = [] + start_dir = os.path.abspath(u'..') + start_dir = start_dir + os.sep + print_verbose(u'Starting directory: %s' % start_dir) + for root, dirs, files in os.walk(start_dir): + for file in files: + path = root.replace(start_dir, u'').replace(u'\\', u'/') #.replace(u'..', u'.') + if file.startswith(u'hook-') or file.startswith(u'test_'): + continue + ignore = False + for ignored_path in IGNORED_PATHS: + if path.startswith(ignored_path): + ignore = True + break + if ignore: + continue + ignore = False + for ignored_file in IGNORED_FILES: + if file == ignored_file: + ignore = True + break + if ignore: + continue + if file.endswith(u'.py') or file.endswith(u'.pyw'): + if path: line = u'%s/%s' % (path, file) - self.printVerbose(u'Parsing "%s"' % line) - stringlist.append(u'SOURCES += %s' % line) - elif file.endswith(u'.pyw'): - line = u'%s/%s' % (path, file) - self.printVerbose(u'Parsing "%s"' % line) - stringlist.append(u'SOURCES += %s' % line) - elif file.endswith(u'.ts'): - line = u'%s/%s' % (path, file) - self.printVerbose(u'Parsing "%s"' % line) - stringlist.append(u'TRANSLATIONS += %s' % line) - - stringlist.sort() - self.write_file(os.path.join(start_dir, u'openlp.pro'), stringlist) - print u' done' - self.process_stack() + else: + line = file + print_verbose(u'Parsing "%s"' % line) + lines.append(u'SOURCES += %s' % line) + elif file.endswith(u'.ts'): + line = u'%s/%s' % (path, file) + print_verbose(u'Parsing "%s"' % line) + lines.append(u'TRANSLATIONS += %s' % line) + lines.sort() + file = open(os.path.join(start_dir, u'openlp.pro'), u'w') + file.write(u'\n'.join(lines).encode('utf8')) + file.close() + print u' Done.' - def update(self): - print u'Update the translation files' - cmd = u'pylupdate4 -verbose -noobsolete ../openlp.pro' - self.start_cmd(cmd) +def update_translations(): + print u'Update the translation files' + if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')): + print u'You have no generated a project file yet, please run this ' + \ + u'script with the -p option.' + return + else: + os.chdir(os.path.abspath(u'..')) + run(u'pylupdate4 -verbose -noobsolete openlp.pro') - def generate(self): - print u'Generate the related *.qm files' - cmd = u'lrelease ../openlp.pro' - self.start_cmd(cmd) - - def write_file(self, filename, stringlist): - content = u'' - for line in stringlist: - content = u'%s%s\n' % (content, line) - file = open(filename, u'w') - file.write(content.encode('utf8')) - file.close() +def generate_binaries(): + print u'Generate the related *.qm files' + if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')): + print u'You have no generated a project file yet, please run this ' + \ + u'script with the -p option. It is also recommended that you ' + \ + u'this script with the -u option to update the translation ' + \ + u'files as well.' + return + else: + os.chdir(os.path.abspath(u'..')) + run(u'lrelease openlp.pro') - def printVerbose(self, data): - if self.verbose: - print u' %s' % data +def create_translation(language): + """ + This method creates a new translation file. - def start_cmd(self, command): - self.printVerbose(command) - self.process = QtCore.QProcess() - self.process.start(command) - while (self.process.waitForReadyRead()): - self.printVerbose(u'ReadyRead: %s' % QtCore.QString(self.process.readAll())) - self.printVerbose(self.process.readAllStandardError()) - self.printVerbose(self.process.readAllStandardOutput()) - print u' done' - self.process_stack() - + ``language`` + The language file to create. + """ + print "Create new Translation File" + filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language) + download_file(u'en.ts', filename) + print u'\n** Please Note **\n' + print u'In order to get this file into OpenLP and onto the Pootle ' + \ + u'translation server you will need to subscribe to the OpenLP' + \ + u'Translators mailing list, and request that your language file ' + \ + u'be added to the project.\n' + print u' Done' + +def process_stack(command_stack): + """ + This method looks at the commands in the command stack, and processes them + in the order they are in the stack. + + ``command_stack`` + The command stack to process. + """ + if command_stack: + print u'Processing %d commands...' % len(command_stack) + for command in command_stack: + print u'%d.' % (command_stack.current_index), + if command == Command.Download: + download_translations() + elif command == Command.Prepare: + prepare_project() + elif command == Command.Update: + update_translations() + elif command == Command.Generate: + generate_binaries() + elif command == Command.Create: + command, arguments = command_stack[command_stack.current_index] + create_translation(*arguments) + print u'Finished processing commands.' + else: + print u'No commands to process.' def main(): - # start Main Class - Util = TranslationUtils() - + global verbose_mode # Set up command line options. - usage = u''' - This script handle the translation files for OpenLP. - Usage: %prog [options] - If no option will be used, options "-d -p -u -g" will be set automatically - ''' + usage = u'%prog [options]\nOptions are parsed in the order they are ' + \ + u'listed below. If no options are given, "-dpug" will be used.\n\n' + \ + u'This script is used to manage OpenLP\'s translation files.' parser = OptionParser(usage=usage) - parser.add_option('-d', '--download-ts', action='store_true', - dest='download', help='Load languages from Pootle Server') - parser.add_option('-c', '--create', metavar='lang', - help='creation of new translation file, Parameter: language (e.g. "en_GB"') - parser.add_option('-p', '--prepare', action='store_true', dest='prepare', - help='preparation (generate pro file)') + parser.add_option('-d', '--download-ts', dest='download', + action='store_true', help='download language files from Pootle') + parser.add_option('-c', '--create', dest=u'create', metavar='LANG', + help='create a new translation file for language LANG, e.g. "en_GB"') + parser.add_option('-p', '--prepare', dest='prepare', action='store_true', + help='generate a project file, used to update the translations') parser.add_option('-u', '--update', action='store_true', dest='update', - help='update translation files') - parser.add_option('-g', '--generate', action='store_true', dest='generate', - help='generate qm files') - parser.add_option('-v', '--verbose', action='store_true', dest='verbose', - help='Give more informations while processing') - + help='update translation files (needs a project file)') + parser.add_option('-g', '--generate', dest='generate', action='store_true', + help='compile .ts files into .qm files') + parser.add_option('-v', '--verbose', dest='verbose', action='store_true', + help='show extra information while processing translations') (options, args) = parser.parse_args() + # Create and populate the command stack + command_stack = CommandStack() if options.download: - Util.cmd_stack.append([Util.downloadTranslations]) + command_stack.append(Command.Download) if options.create: - Util.cmd_stack.append([Util.creation, u'%s' % options.create]) + command_stack.append(Command.Create, arguments=[options.create]) if options.prepare: - Util.cmd_stack.append([Util.preparation]) + command_stack.append(Command.Prepare) if options.update: - Util.cmd_stack.append([Util.update]) + command_stack.append(Command.Update) if options.generate: - Util.cmd_stack.append([Util.generate]) - if options.verbose: - Util.verbose = True + command_stack.append(Command.Generate) + verbose_mode = options.verbose + if not command_stack: + command_stack.append(Command.Download) + command_stack.append(Command.Prepare) + command_stack.append(Command.Update) + command_stack.append(Command.Generate) + # Process the commands + process_stack(command_stack) - if len(Util.cmd_stack) == 0: - Util.cmd_stack.append([Util.downloadTranslations]) - Util.cmd_stack.append([Util.preparation]) - Util.cmd_stack.append([Util.update]) - Util.cmd_stack.append([Util.generate]) - - Util.stack_count = len(Util.cmd_stack) - Util.process_stack() - - - if __name__ == u'__main__': if os.path.split(os.path.abspath(u'.'))[1] != u'scripts': print u'You need to run this script from the scripts directory.' else: main() +