colobot-data/i18n-tools/scripts/common.py

250 lines
7.7 KiB
Python

import errno
import io
import os
import polib
"""
Conversion functions to avoid mixed \ and / path separators under Windows
"""
def convert_input_path(slash_path):
if not slash_path:
return None
return slash_path.replace('/', os.sep)
def convert_output_path(system_path):
if not system_path:
return None
return system_path.replace(os.sep, '/')
"""
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 = convert_output_path(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)
for x in self.previous_catalog.obsolete_entries():
self.previous_catalog.remove(x)
self.previous_catalog.save(self.file_name, newline='\n')
"""
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):
if self.language == 'pt':
return 'B';
else:
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)
for x in self.catalog.obsolete_entries():
self.catalog.remove(x)
self.catalog.save(self.file_name, newline='\n')
"""
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', newline='\n')
if self._output_file_name:
self._output_file = io.open(self._output_file_name, 'w', encoding='utf-8', newline='\n')
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