diff --git a/CMakeLists.txt b/CMakeLists.txt index 82c3d0e2..9f28554f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,11 @@ cmake_minimum_required(VERSION 2.8) +find_package(PythonInterp 2.6) + +if(NOT PYTHONINTERP_FOUND) + message(WARNING "Python not found, help and level files will NOT be translated!") +endif() + if(NOT DEFINED COLOBOT_INSTALL_DATA_DIR) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(COLOBOT_INSTALL_DATA_DIR ${CMAKE_INSTALL_PREFIX}/data CACHE PATH "Colobot shared data directory") diff --git a/help/CMakeLists.txt b/help/CMakeLists.txt index 69470bb8..3ff52821 100644 --- a/help/CMakeLists.txt +++ b/help/CMakeLists.txt @@ -1,28 +1,34 @@ cmake_minimum_required(VERSION 2.8) -include(../i18n-tools/CommonI18N.cmake) -include(../i18n-tools/HelpI18N.cmake) +include(../i18n-tools/I18NTools.cmake) set(HELP_INSTALL_DATA_DIR ${COLOBOT_INSTALL_DATA_DIR}/help) ## # Add help category directory ## -function(add_help_category help_category_dir install_dest_dir) - file(GLOB help_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${help_category_dir}/E/*.txt) - list(SORT help_files) - if(PO4A AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${help_category_dir}/po/) - generate_help_i18n(translated_help_dirs - "${help_files}" - ${help_category_dir}/po - ${DATA_BINARY_DIR}/help-po/${help_category_dir}) - else() - set(translated_help_dirs "") +function(add_help_category help_category_dir install_subdir) + + message(STATUS "Adding translation targets for help/${help_category_dir}") + + file(GLOB english_help_files ${help_category_dir}/E/*) + install(FILES ${english_help_files} DESTINATION ${HELP_INSTALL_DATA_DIR}/E/${install_subdir}) + + if(PYTHONINTERP_FOUND) + set(work_dir ${DATA_BINARY_DIR}/help-po/${help_category_dir}) + generate_translations(translated_help_files + "help" + ${CMAKE_CURRENT_SOURCE_DIR} + ${help_category_dir} + ${help_category_dir}/po + ${work_dir} + "${install_subdir}") + + install_preserving_relative_paths("${translated_help_files}" + ${work_dir} + ${HELP_INSTALL_DATA_DIR}) endif() - install(DIRECTORY ${help_category_dir}/E/ DESTINATION ${HELP_INSTALL_DATA_DIR}/E/${install_dest_dir}) - foreach(translated_help_dir ${translated_help_dirs}) - install(DIRECTORY ${DATA_BINARY_DIR}/help-po/${help_category_dir}/${translated_help_dir}/ DESTINATION ${HELP_INSTALL_DATA_DIR}/${translated_help_dir}/${install_dest_dir}) - endforeach() + endfunction() diff --git a/i18n-tools/.gitignore b/i18n-tools/.gitignore new file mode 100644 index 00000000..7a60b85e --- /dev/null +++ b/i18n-tools/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/i18n-tools/CommonI18N.cmake b/i18n-tools/CommonI18N.cmake deleted file mode 100644 index 65c0ad8c..00000000 --- a/i18n-tools/CommonI18N.cmake +++ /dev/null @@ -1,22 +0,0 @@ -## -# Common function used in other I18N CMake modules -## - -## -# Get language code from *.po file name e.g. "de.po" -> "de" -## -function(get_language_code result_language_code po_file) - get_filename_component(po_file_name ${po_file} NAME) - string(REPLACE ".po" "" language_code ${po_file_name}) - set(${result_language_code} ${language_code} PARENT_SCOPE) -endfunction() - -## -# Get language char from *.po file name e.g. "de.po" -> "D" -## -function(get_language_char result_language_char po_file) - get_filename_component(po_file_name ${po_file} NAME) - string(REGEX REPLACE ".\\.po" "" language_char ${po_file_name}) - string(TOUPPER ${language_char} language_char) - set(${result_language_char} ${language_char} PARENT_SCOPE) -endfunction() diff --git a/i18n-tools/HelpI18N.cmake b/i18n-tools/HelpI18N.cmake deleted file mode 100644 index 10840928..00000000 --- a/i18n-tools/HelpI18N.cmake +++ /dev/null @@ -1,83 +0,0 @@ -## -# Meta-infrastructure to allow po-based translation of Colobot help files -## - -find_program(PO4A po4a) - -if(NOT PO4A) - message(WARNING "PO4A not found, help files will NOT be translated!") -endif() - -## -# Generate translated help files in separate directories per language -## -function(generate_help_i18n - result_generated_help_dirs # output variable to return names of directories with translated files - source_help_files # input help files - po_dir # directory with translations - work_dir) # directory where to save generated files - - # generated config file for po4a - set(po4a_cfg_file ${work_dir}/help_po4a.cfg) - - # get translations from po directory - get_filename_component(abs_po_dir ${po_dir} ABSOLUTE) - file(WRITE ${po4a_cfg_file} "[po_directory] ${abs_po_dir}\n") - - # prepare output directories - set(output_help_subdirs "") - file(GLOB po_files ${po_dir}/*.po) - foreach(po_file ${po_files}) - get_language_char(language_char ${po_file}) - #set(language_help_subdir ${work_dir}/${language_char}) - list(APPEND output_help_subdirs ${language_char}) - endforeach() - - # add translation rules for help files - foreach(source_help_file ${source_help_files}) - get_filename_component(abs_source_help_file ${source_help_file} ABSOLUTE) - get_filename_component(help_file_name ${source_help_file} NAME) - - file(APPEND ${po4a_cfg_file} "\n[type:colobothelp] ${abs_source_help_file}") - foreach(po_file ${po_files}) - # generated file for single language - get_language_code(language_code ${po_file}) - get_language_char(language_char ${po_file}) - set(generated_help_file ${work_dir}/${language_char}/${help_file_name}) - file(APPEND ${po4a_cfg_file} " \\\n ${language_code}:${generated_help_file}") - endforeach() - endforeach() - - # dummy files to signal that scripts have finished running - set(translation_signalfile ${work_dir}/translations) - set(po_clean_signalfile ${work_dir}/po_clean) - - # script to run po4a and generate translated files - add_custom_command(OUTPUT ${translation_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/run_po4a.sh - ${po4a_cfg_file} - ${translation_signalfile} - DEPENDS ${po_files}) - - file(GLOB pot_file ${po_dir}/*.pot) - set(po_files ${po_files} ${pot_file}) - - # script to do some cleanups in updated *.po and *.pot files - string(REPLACE ";" ":" escaped_po_files "${po_files}") - add_custom_command(OUTPUT ${po_clean_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/clean_po_files.sh - ${escaped_po_files} - ${translation_signalfile} - ${po_clean_signalfile} - DEPENDS ${translation_signalfile} - ) - - # generate some unique string for target name - string(REGEX REPLACE "[/\\]" "_" target_suffix ${po_dir}) - - # target to run both scripts - add_custom_target(i18n_${target_suffix} ALL DEPENDS ${translation_signalfile} ${po_clean_signalfile}) - - # return the translated files - set(${result_generated_help_dirs} ${output_help_subdirs} PARENT_SCOPE) -endfunction() diff --git a/i18n-tools/I18NTools.cmake b/i18n-tools/I18NTools.cmake new file mode 100644 index 00000000..00f20591 --- /dev/null +++ b/i18n-tools/I18NTools.cmake @@ -0,0 +1,85 @@ +## +# Meta-infrastructure to allow po-based translation of Colobot help files +## + +## +# Generate translated files with Python script +## +function(generate_translations + result_output_files # output variable to return file names of translated files + type # type of files to process + working_dir # working directory for the commands to run + input_dir # directory with source files + po_dir # directory with translations + output_dir # directory where to save generated files + output_subdir) # optional installation subdirectory + + if(output_subdir STREQUAL "") + set(output_subdir_opt "") + else() + set(output_subdir_opt "--output_subdir") + endif() + + # first command is used to get list of input and output files when running CMake to + # execute appropriate CMake install commands and set up dependencies properly + execute_process(COMMAND ${PYTHON_EXECUTABLE} + ${DATA_SOURCE_DIR}/i18n-tools/scripts/process_translations.py + --mode print_files + --type ${type} + --input_dir ${input_dir} + --po_dir ${po_dir} + --output_dir ${output_dir} + ${output_subdir_opt} ${output_subdir} + WORKING_DIRECTORY ${working_dir} + OUTPUT_VARIABLE files_list) + + string(REGEX REPLACE "(.*)\n(.*)" "\\1" input_files "${files_list}") + string(REGEX REPLACE "(.*)\n(.*)" "\\2" output_files "${files_list}") + + # return the list of output files to parent + set(${result_output_files} ${output_files} PARENT_SCOPE) + + # dummy file to indicate success + set(signal_file ${output_dir}/translation) + + # po files are also dependency + file(GLOB po_files ${po_dir}/*) + + # actual command used to generate translations executed when building project + add_custom_command(OUTPUT ${signal_file} + COMMAND ${PYTHON_EXECUTABLE} + ${DATA_SOURCE_DIR}/i18n-tools/scripts/process_translations.py + --mode generate + --type ${type} + --input_dir ${input_dir} + --po_dir ${po_dir} + --output_dir ${output_dir} + ${output_subdir_opt} ${output_subdir} + --signal_file ${signal_file} + WORKING_DIRECTORY ${working_dir} + DEPENDS ${input_files} ${po_files}) + + # generate some unique string for target name + string(REGEX REPLACE "[/\\]" "_" target_suffix ${po_dir}) + + # target to run the command + add_custom_target(i18n_${target_suffix} ALL DEPENDS ${signal_file}) + +endfunction() + +## +# Convenience function to installing generated files while keeping +# their relative paths in output directory +## +function(install_preserving_relative_paths + output_files # list of output files + output_dir # output directory + destination_dir) # install destination directory + + foreach(output_file ${output_files}) + file(RELATIVE_PATH rel_output_file ${output_dir} ${output_file}) + get_filename_component(rel_output_file_dir ${rel_output_file} DIRECTORY) + install(FILES ${output_file} DESTINATION ${destination_dir}/${rel_output_file_dir}) + endforeach() + +endfunction() diff --git a/i18n-tools/LevelsI18N.cmake b/i18n-tools/LevelsI18N.cmake deleted file mode 100644 index 8d18fe27..00000000 --- a/i18n-tools/LevelsI18N.cmake +++ /dev/null @@ -1,193 +0,0 @@ -## -# Meta-infrastructure to allow po-based translation of Colobot level files -## - -find_program(PO4A po4a) - -if(NOT PO4A) - message(WARNING "PO4A not found, level files will NOT be translated!") -endif() - -## -# Generate translated chaptertitle files using po4a -## -function(generate_chaptertitles_i18n - result_translated_chaptertitle_files # output variable to return names of translated chaptertitle files - source_chaptertitle_prefix_dir # prefix directory for chaptertitle files - source_chaptertitle_files # input chaptertitle files relative to prefix dir - po_dir # directory with translations (*.po, *.pot files) - work_dir) # directory where to save generated files - - # generated dummy file for translation of "E", "D", "F", "P", etc. language letters - # TODO find a better way to provide translations than this hack - set(langchar_file ${work_dir}/chaptertitles_langchar.txt) - file(WRITE ${langchar_file} "E") - - # generated config file for po4a - set(po4a_cfg_file ${work_dir}/chaptertitles_po4a.cfg) - - # get translations from po directory - get_filename_component(abs_po_dir ${po_dir} ABSOLUTE) - file(WRITE ${po4a_cfg_file} "[po_directory] ${abs_po_dir}\n") - - # add content of dummy language file to translation - file(APPEND ${po4a_cfg_file} "[type:text] ${langchar_file}") - - set(abs_source_chaptertitle_files "") - set(translated_chaptertitle_files "") - file(GLOB po_files ${po_dir}/*.po) - - foreach(source_chaptertitle_file ${source_chaptertitle_files}) - get_filename_component(abs_source_chaptertitle_file ${source_chaptertitle_prefix_dir}/${source_chaptertitle_file} ABSOLUTE) - set(output_chaptertitle_file ${work_dir}/${source_chaptertitle_file}) - - # translation rule for chaptertitle file - file(APPEND ${po4a_cfg_file} "\n[type:colobotlevel] ${abs_source_chaptertitle_file}") - - foreach(po_file ${po_files}) - # generated file for single language - get_language_code(language_code ${po_file}) - set(generated_language_file ${output_chaptertitle_file}.${language_code}) - file(APPEND ${po4a_cfg_file} " \\\n ${language_code}:${generated_language_file}") - endforeach() - - list(APPEND abs_source_chaptertitle_files ${abs_source_chaptertitle_file}) - list(APPEND translated_chaptertitle_files ${output_chaptertitle_file}) - endforeach() - - # dummy files to signal that scripts have finished running - set(translation_signalfile ${work_dir}/translations) - set(po_clean_signalfile ${work_dir}/po_clean) - - # script to run po4a and consolidate the translations - string(REPLACE ";" ":" escaped_abs_source_chaptertitle_files "${abs_source_chaptertitle_files}") - string(REPLACE ";" ":" escaped_translated_chaptertitle_files "${translated_chaptertitle_files}") - add_custom_command(OUTPUT ${translation_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/run_po4a.sh ${po4a_cfg_file} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/create_level_translations.sh - ${escaped_abs_source_chaptertitle_files} - ${escaped_translated_chaptertitle_files} - ${translation_signalfile} - DEPENDS ${po_files}) - - file(GLOB pot_file ${po_dir}/*.pot) - set(po_files ${po_files} ${pot_file}) - - # script to do some cleanups in updated *.po and *.pot files - string(REPLACE ";" ":" escaped_po_files "${po_files}") - add_custom_command(OUTPUT ${po_clean_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/clean_po_files.sh - ${escaped_po_files} - ${translation_signalfile} - ${po_clean_signalfile} - DEPENDS ${translation_signalfile} - ) - - # generate some unique string for target name - string(REGEX REPLACE "[/\\]" "_" target_suffix ${po_dir}) - - # target to run both scripts - add_custom_target(i18n_${target_suffix} ALL DEPENDS ${translation_signalfile} ${po_clean_signalfile}) - - # return the translated files - set(${result_translated_chaptertitle_files} ${translated_chaptertitle_files} PARENT_SCOPE) -endfunction() - -## -# Generate translated level and help files using po4a -## -function(generate_level_i18n - result_translated_level_file # output variable to return names of translaed level files - result_translated_help_files # output variable to return names of translated help files - source_level_file # input scene.txt files - source_help_files # input help files - po_dir # directory with translations (*.po, *.pot files) - work_dir) # directory where to save generated files - - # generated dummy file for translation of "E", "D", "F", "P", etc. language letters - # TODO find a better way to provide translations than this hack - set(langchar_file ${work_dir}/scene_langchar.txt) - file(WRITE ${langchar_file} "E") - - # generated config file for po4a - set(po4a_cfg_file ${work_dir}/scene_po4a.cfg) - - # get translations from po directory - get_filename_component(abs_po_dir ${po_dir} ABSOLUTE) - file(WRITE ${po4a_cfg_file} "[po_directory] ${abs_po_dir}\n") - - # add content of dummy language file to translation - file(APPEND ${po4a_cfg_file} "[type:text] ${langchar_file}") - - # translation rule for scene file - get_filename_component(abs_source_level_file ${source_level_file} ABSOLUTE) - file(APPEND ${po4a_cfg_file} "\n[type:colobotlevel] ${abs_source_level_file}") - - get_filename_component(source_level_file_name ${source_level_file} NAME) - set(output_level_file ${work_dir}/${source_level_file_name}) - - file(GLOB po_files ${po_dir}/*.po) - foreach(po_file ${po_files}) - get_language_code(language_code ${po_file}) - # generated file for single language - set(generated_language_file ${output_level_file}.${language_code}) - file(APPEND ${po4a_cfg_file} " \\\n ${language_code}:${generated_language_file}") - endforeach() - - # translation rules for help files - set(output_help_dir ${work_dir}/help) - set(translated_help_files "") - - foreach(source_help_file ${source_help_files}) - get_filename_component(help_file_name ${source_help_file} NAME) - - file(APPEND ${po4a_cfg_file} "\n[type:colobothelp] ${source_help_file}") - foreach(po_file ${po_files}) - # generated file for single language - get_language_code(language_code ${po_file}) - get_language_char(language_char ${po_file}) - string(REPLACE ".E." ".${language_char}." generated_help_file_name ${help_file_name}) - set(generated_help_file ${output_help_dir}/${generated_help_file_name}) - file(APPEND ${po4a_cfg_file} " \\\n ${language_code}:${generated_help_file}") - - list(APPEND translated_help_files ${generated_help_file}) - endforeach() - endforeach() - - # dummy files to signal that scripts have finished running - set(translation_signalfile ${work_dir}/translations) - set(po_clean_signalfile ${work_dir}/po_clean) - - # script to run po4a and consolidate the translations - add_custom_command(OUTPUT ${translation_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/run_po4a.sh ${po4a_cfg_file} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/create_level_translations.sh - ${abs_source_level_file} - ${output_level_file} - ${translation_signalfile} - DEPENDS ${po_files}) - - file(GLOB pot_file ${po_dir}/*.pot) - set(po_files ${po_files} ${pot_file}) - - # script to do some cleanups in updated *.po and *.pot files - string(REPLACE ";" ":" escaped_po_files "${po_files}") - add_custom_command(OUTPUT ${po_clean_signalfile} - COMMAND ${DATA_SOURCE_DIR}/i18n-tools/scripts/clean_po_files.sh - ${escaped_po_files} - ${translation_signalfile} - ${po_clean_signalfile} - DEPENDS ${translation_signalfile} - ) - - # generate some unique string for target name - string(REGEX REPLACE "[/\\]" "_" target_suffix ${po_dir}) - - # target to run both scripts - add_custom_target(i18n_${target_suffix} ALL DEPENDS ${translation_signalfile} ${po_clean_signalfile}) - - # return the translated files - set(${result_translated_level_file} ${output_level_file} PARENT_SCOPE) - set(${result_translated_help_files} ${translated_help_files} PARENT_SCOPE) -endfunction() - diff --git a/i18n-tools/scripts/clean_po_files.sh b/i18n-tools/scripts/clean_po_files.sh deleted file mode 100755 index decd88d7..00000000 --- a/i18n-tools/scripts/clean_po_files.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -## -# Script to do some cleaning up of merged/generated *.po and *.pot files -# -# It is basically a sed wrapper that does two things: -# - remove information about absolute filenames which were used to generate translations -# - remove modification date of file -# -# By doing these two things, it makes sure that *.po and *.pot files do not change -# compared to versions stored in repository when building the project -# -# The arguments are a colon-separated list of *.po or *.pot files and -# two dummy signal files used by build system that must be updated -## - -# stop on errors -set -e - -if [ $# -ne 3 ]; then - echo "Invalid arguments!" >&2 - echo "Usage: $0 po_file1[:po_file2;...] translation_signalfile po_clean_signalfile" >&2 - exit 1 -fi - -PO_FILES="$1" -TRANSLATION_SIGNALFILE="$2" -PO_CLEAN_SIGNALFILE="$3" - -IFS=':' read -a po_files_array <<< "$PO_FILES" - -for po_file in "${po_files_array[@]}"; do - # strip unnecessary part of file names - sed -i -e 's|^#: .*data/\(.*\)$|#: \1|' "$po_file" - # remove the creation date - sed -i -e 's|^\("POT-Creation-Date:\).*$|\1 DATE\\n"|' "$po_file" -done - -# update the dummy signal files to indicate success -# we also have to touch translation signalfile because it's supposed to be modified later than po files -touch "$TRANSLATION_SIGNALFILE" -touch "$PO_CLEAN_SIGNALFILE" \ No newline at end of file diff --git a/i18n-tools/scripts/common.py b/i18n-tools/scripts/common.py new file mode 100644 index 00000000..3fab2607 --- /dev/null +++ b/i18n-tools/scripts/common.py @@ -0,0 +1,229 @@ +import errno +import io +import os +import polib + +""" + Works like shell's "mkdir -p" and also behaves nicely if given None argument +""" +def nice_mkdir(path): + if path is None: + return + + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + +""" + Works as os.path.join, but behaves nicely if given None argument +""" +def nice_path_join(*paths): + for path in paths: + if path is None: + return None + + return os.path.join(*paths) + +""" + Wrapper class over POFile, acting as translation template file + + It actually hold two POFile instances: + previous_catalog is the content of PO file read from disk + current_catalog is created empty and filled with entries from input files + + Once all processing is done, the content of previous_catalog is merged with current_catalog + and the result is saved to disk. +""" +class TemplateFile: + def __init__(self, file_name): + self.file_name = file_name + self.dir_name = os.path.dirname(file_name) + self.language = 'en' + self.current_catalog = polib.POFile(wrapwidth = 0) + if os.path.exists(file_name): + self.previous_catalog = polib.pofile(file_name, wrapwidth = 0) + else: + self.previous_catalog = polib.POFile(wrapwidth = 0) + + """ + Wrapper over inserting template file entry + If entry does not exist, it is created; + otherwise it is modified to indicate multiple occurrences + """ + def insert_entry(self, text, occurrence, type_comment): + entry = self.current_catalog.find(text) + relative_file_name = os.path.relpath(occurrence.file_name, self.dir_name) + occurrence = (relative_file_name, occurrence.line_number) + if entry: + entry.comment = self._merge_comment(entry.comment, type_comment) + if occurrence not in entry.occurrences: + entry.occurrences.append(occurrence) + else: + comment = 'type: ' + type_comment + new_entry = polib.POEntry(msgid = text, + comment = comment, + occurrences = [occurrence], + flags = ['no-wrap']) + + self.current_catalog.append(new_entry) + + def _merge_comment(self, previous_comment, type_comment): + new_comment = previous_comment + + previous_types = previous_comment.replace('type: ', '') + previous_types_list = previous_types.split(', ') + + if type_comment not in previous_types_list: + new_comment += ', ' + type_comment + + return new_comment + + """ + Merges previous_catalog with current_catalog and saved the result to disk + """ + def merge_and_save(self): + self.previous_catalog.merge(self.current_catalog) + self.previous_catalog.save(self.file_name) + +""" + Wrapper class over POFile, acting as language translation file +""" +class LanguageFile: + def __init__(self, file_name): + self.file_name = file_name + # get language from file name e.g. "/foo/de.po" -> "de" + (self.language, _) = os.path.splitext(os.path.basename(file_name)) + if os.path.exists(file_name): + self.catalog = polib.pofile(file_name, wrapwidth = 0) + else: + self.catalog = polib.POFile(wrapwidth = 0) + + """ + Return single language character e.g. "de" -> "D" + """ + def language_char(self): + return self.language[0].upper() + + """ + Try to translate given text; if not found among translations, + return the original + """ + def translate(self, text): + entry = self.catalog.find(text) + if entry and entry.msgstr != '': + return entry.msgstr + return text + + """ + Merges entries with current_catalog from template file and saves the result to disk + """ + def merge_and_save(self, template_file): + self.catalog.merge(template_file.current_catalog) + self.catalog.save(self.file_name) + +""" + Locates the translation files in po_dir +""" +def find_translation_file_names(po_dir): + pot_file_name = os.path.join(po_dir, 'translations.pot') # default + po_file_names = [] + for file_name in os.listdir(po_dir): + if file_name.endswith('.pot'): + pot_file_name = os.path.join(po_dir, file_name) + elif file_name.endswith('.po'): + po_file_names.append(os.path.join(po_dir, file_name)) + + return (pot_file_name, po_file_names) + +""" + Creates template and language files by reading po_dir +""" +def create_template_and_language_files(po_dir): + (pot_file_name, po_file_names) = find_translation_file_names(po_dir) + + template_file = TemplateFile(pot_file_name) + language_files = [] + for po_file_name in po_file_names: + language_files.append(LanguageFile(po_file_name)) + + return (template_file, language_files) + +""" + Structure representing occurrence of text +""" +class Occurrence: + def __init__(self, file_name, line_number): + self.file_name = file_name + self.line_number = line_number + +""" + Structure representing line read from input file +""" +class InputLine: + def __init__(self, text, occurrence): + self.text = text + self.occurrence = occurrence + + +""" + Base class for single translation process, + translating one input file into one output file + + It provides wrapper code for reading consecutive lines of text and saving the result +""" +class TranslationJob: + def __init__(self, **kwargs): + self._input_line_counter = 0 + self._input_file_name = kwargs['input_file'] + self._input_file = None + + self._output_file_name = kwargs['output_file'] + self._output_file = None + + """ + Launch translation process + Actual processing is done in process_file() function which must be implemented by subclasses + """ + def run(self): + try: + self._open_files() + self.process_file() + finally: + self._close_files() + + def _open_files(self): + self._input_file = io.open(self._input_file_name, 'r', encoding='utf-8') + if self._output_file_name: + self._output_file = io.open(self._output_file_name, 'w', encoding='utf-8') + + def _close_files(self): + self._input_file.close() + if self._output_file: + self._output_file.close() + + """ + Return next line, occurrene pair from input file or None if at end of input + """ + def read_input_line(self): + line = self._input_file.readline() + if line == '': + return None + + self._input_line_counter += 1 + return InputLine(line.rstrip('\n'), Occurrence(self._input_file_name, self._input_line_counter)) + + """ + Write line to output file, if present + """ + def write_output_line(self, line): + if self._output_file: + self._output_file.write(line + '\n') + + def get_input_file_name(self): + return self._input_file_name + + def get_output_file_name(self): + return self._output_file_name diff --git a/i18n-tools/scripts/create_level_translations.sh b/i18n-tools/scripts/create_level_translations.sh deleted file mode 100755 index 88d9f9ba..00000000 --- a/i18n-tools/scripts/create_level_translations.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -## -# Script to consolidate multiple translated level files (scene.txt or chaptertitle.txt), -# generated previously by PO4A, into a single all-in-one output file -# -# It supports multiple pairs of source and output files and makes the assumption that -# each source file was processed by PO4A to yield output files named like $output_file.$language_code -# -# Basically, it is a simple sed wrapper that uses the source file and the translated files to copy-paste -# content into resulting output file -# -# The arugments are list of source files as a colon-separated list, list of output files also as colon-separated list -# and dummy signal file used by build system -## - -# Stop on errors -set -e - -if [ $# -ne 3 ]; then - echo "Invalid arguments!" >&2 - echo "Usage: $0 source_file1[:source_file2:...] output_file1[:output_file2:...] translation_signalfile" >&2 - exit 1 -fi - -SOURCE_FILES="$1" -OUTPUT_FILES="$2" -TRANSLATION_SIGNALFILE="$3" - -IFS=':' read -a source_files_array <<< "$SOURCE_FILES" -IFS=':' read -a output_files_array <<< "$OUTPUT_FILES" - -for index in "${!source_files_array[@]}"; do - source_file="${source_files_array[index]}" - output_file="${output_files_array[index]}" - - # generate output file - echo -n "" > "$output_file" - - # first, write original English headers - sed -n '/^Title/p;/^Resume/p;/^ScriptName/p' "$source_file" >> "$output_file" - - # now, copy translated headers from translated files - # (translated files are named output file + suffix with language code) - for translated_file in $output_file.*; do - sed -n '/^Title/p;/^Resume/p;/^ScriptName/p' "$translated_file" >> "$output_file" - done - echo "// End of level headers translations" >> "$output_file" - echo "" >> "$output_file" - - # copy the rest of source file, excluding headers - sed -e '/^Title/d;/^Resume/d;/^ScriptName/d' "$source_file" >> "$output_file" -done - -# update the dummy signal file to indicate success -touch "$TRANSLATION_SIGNALFILE" \ No newline at end of file diff --git a/i18n-tools/scripts/perllib/Locale/Po4a/Colobothelp.pm b/i18n-tools/scripts/perllib/Locale/Po4a/Colobothelp.pm deleted file mode 100644 index c2a683c6..00000000 --- a/i18n-tools/scripts/perllib/Locale/Po4a/Colobothelp.pm +++ /dev/null @@ -1,194 +0,0 @@ -# Locale::Po4a::Colobothelp -- Convert Colobot help files -# -# This program is free software; you may redistribute it and/or modify it -# under the terms of GPLv3. -# - -use Locale::Po4a::TransTractor qw(process new); -use Locale::Po4a::Common; -use Locale::Po4a::Text; - -package Locale::Po4a::Colobothelp; - -use 5.006; -use strict; -use warnings; - -require Exporter; - -use vars qw(@ISA @EXPORT $AUTOLOAD); -@ISA = qw(Locale::Po4a::TransTractor); -@EXPORT = qw(); - -my @comments = (); - -sub initialize {} - -sub parse { - my $self = shift; - my ($line,$ref); - my $paragraph=""; - my $wrapped_mode = 1; - my $s_mode = 0; - my $expect_header = 1; - my $end_of_paragraph = 0; - ($line,$ref)=$self->shiftline(); - while (defined($line)) { - chomp($line); - $self->{ref}="$ref"; - ($paragraph,$wrapped_mode,$s_mode,$expect_header,$end_of_paragraph) = parse_colobothelp($self,$line,$ref,$paragraph,$wrapped_mode,$s_mode,$expect_header,$end_of_paragraph); - if ($end_of_paragraph) { - do_paragraph($self,offlink($paragraph),$wrapped_mode); - $paragraph=""; - $wrapped_mode = 1; - $end_of_paragraph = 0; - } - ($line,$ref)=$self->shiftline(); - } - if (length $paragraph) { - $paragraph =~ s/\n$//; - do_paragraph($self,$paragraph,$wrapped_mode); - $self->pushline("\n"); - } -} - -sub parse_colobothelp { - my ($self,$line,$ref,$paragraph,$wrapped_mode,$s_mode,$expect_header,$end_of_paragraph) = @_; - - if (($s_mode == 1) and ($line !~ /^\\s;/)) { - # Process the end of \s; blocks - $s_mode = 0; - # substr removes the last superfluous \n - my $s_block = onlink($self->translate(substr(offlink($paragraph),0,-1),$ref,"\\s; block (usually verbatim code)")); - $s_block =~ s/(\n|^)/$1\\s;/g; - $self->pushline($s_block."\n"); - $paragraph=""; - $wrapped_mode = 0; - } - - if ( $line =~ /^\s*$/ - or $line =~ m/^\\[nctr];$/) { - # Break paragraphs on lines containing only spaces or any of \n; \c; \t; \r; (alone) - - # Drop the latest EOL to avoid having it in the translation - my $dropped_eol = ($paragraph =~ s/\n$//); - do_paragraph($self,$paragraph,$wrapped_mode); - $self->pushline("\n") if $dropped_eol; # Therefore only add it back if it was removed - $paragraph=""; - $wrapped_mode = 0; - $self->pushline($line."\n"); - } elsif ($line =~ s/^(\\s;)//) { - # Lines starting with \s; are special (yellow-background, usually code-block) - # Break paragraph before them - if($s_mode == 0) { - $s_mode = 1; - my $dropped_eol = ($paragraph =~ s/\n$//); - do_paragraph($self,$paragraph,$wrapped_mode); - $self->pushline("\n") if $dropped_eol; # Therefore only add it back if it was removed - $paragraph=""; - $wrapped_mode = 0; - } - $paragraph .= $line."\n"; - } elsif ($line =~ s/^(\\[bt];)//) { - # Break paragraphs on \b; or \t; headers - do_paragraph($self,$paragraph,$wrapped_mode); - $paragraph=""; - $wrapped_mode = 1; - - $self->pushline($1.onlink($self->translate(offlink($line),$ref,"$1 header")."\n")); - } elsif ($line =~ /^\\image (.*) (\d*) (\d*);$/) { - # Discard lines with \image name lx ly; tags - do_paragraph($self,$paragraph,$wrapped_mode); - $paragraph=""; - $wrapped_mode = 1; - - $self->pushline("\\image ".$self->translate($1,$ref,'Image filename')." $2 $3;\n"); - } elsif ( $line =~ /^=+$/ - or $line =~ /^_+$/ - or $line =~ /^-+$/) { - $wrapped_mode = 0; - $paragraph .= $line."\n"; - do_paragraph($self,$paragraph,$wrapped_mode); - $paragraph=""; - $wrapped_mode = 1; - } elsif ($line =~ s/^(\s*)([0-9]\)|[o-])(\s*)//) { - # Break paragraphs on lines starting with either number + parenthesis or any of o- + space - do_paragraph($self,$paragraph,$wrapped_mode); - $paragraph=""; - $wrapped_mode = 1; - - $self->pushline("$1$2$3".onlink($self->translate(offlink($line),$ref,"Bullet: '$2'")."\n")); - } else { - # All paragraphs are non-wrap paragraphs by default - $wrapped_mode = 0; - undef $self->{bullet}; - undef $self->{indent}; - $paragraph .= $line."\n"; - } - return ($paragraph,$wrapped_mode,$s_mode,$expect_header,$end_of_paragraph); -} - -sub offlink { - my ($paragraph) = @_; - # Replace \button $id; as pseudo xHTML