1import os 2import re 3 4from common import TranslationJob, nice_mkdir, nice_path_join 5 6""" 7 Types of input lines 8""" 9TYPE_WHITESPACE = 1 # whitespace only 10TYPE_HEADER = 2 # header (beginning with \b or \t) 11TYPE_BULLET = 3 # bullet point 12TYPE_IMAGE = 4 # image (beginning with \image) 13TYPE_CODE = 5 # code (beginning with \s;) 14TYPE_PLAIN = 6 # plain text 15 16class HelpTranslationJob(TranslationJob): 17 def __init__(self, **kwargs): 18 TranslationJob.__init__(self, **kwargs) 19 self.template_file = kwargs['template_file'] 20 self.language_file = kwargs['language_file'] 21 self.line_buffer = None 22 23 def process_file(self): 24 while True: 25 (paragraph, paragraph_type) = self.read_input_paragraph() 26 if not paragraph: 27 break 28 29 if paragraph_type == TYPE_WHITESPACE: 30 self.process_whitespace(paragraph[0]) 31 elif paragraph_type == TYPE_HEADER: 32 self.process_header(paragraph[0]) 33 elif paragraph_type == TYPE_BULLET: 34 self.process_bullet(paragraph[0]) 35 elif paragraph_type == TYPE_IMAGE: 36 self.process_image(paragraph[0]) 37 elif paragraph_type == TYPE_CODE: 38 self.process_code(paragraph) 39 elif paragraph_type == TYPE_PLAIN: 40 self.process_plain(paragraph) 41 42 """ 43 Read one or more lines of input with same line type and return the list as paragraph 44 Exception is types which are processed as single lines, giving only paragraph with one line 45 """ 46 def read_input_paragraph(self): 47 paragraph = None 48 paragraph_type = None 49 while True: 50 line = None 51 line_type = None 52 if self.line_buffer: 53 (line, line_type) = self.line_buffer 54 self.line_buffer = None 55 else: 56 line = self.read_input_line() 57 if line: 58 line_type = self.check_line_type(line.text) 59 60 if not line: 61 break 62 63 if not paragraph_type: 64 paragraph_type = line_type 65 66 if paragraph_type == line_type: 67 if not paragraph: 68 paragraph = [] 69 paragraph.append(line) 70 else: 71 self.line_buffer = (line, line_type) 72 break 73 74 if line_type in [TYPE_WHITESPACE, TYPE_HEADER, TYPE_BULLET, TYPE_IMAGE]: 75 break 76 77 return (paragraph, paragraph_type) 78 79 def check_line_type(self, line): 80 if re.match(r'^\s*$', line) or re.match(r'^\\[nctr];$', line): 81 return TYPE_WHITESPACE 82 elif re.match(r'^\\[bt];', line): 83 return TYPE_HEADER 84 elif re.match(r'^\s*([0-9]\)|[o-])', line): 85 return TYPE_BULLET 86 elif re.match(r'^\\image.*;$', line): 87 return TYPE_IMAGE 88 elif re.match(r'^\\s;', line): 89 return TYPE_CODE 90 else: 91 return TYPE_PLAIN 92 93 def process_whitespace(self, line): 94 self.write_output_line(line.text) 95 96 def process_header(self, line): 97 match = re.match(r'^(\\[bt];)(.*)', line.text) 98 header_type = match.group(1) 99 header_text = match.group(2) 100 translated_header_text = self.translate_text(header_text, line.occurrence, header_type + ' header') 101 self.write_output_line(header_type + translated_header_text) 102 103 def process_bullet(self, line): 104 match = re.match(r'^(\s*)([0-9]\)|[o-])(\s*)(.*)', line.text) 105 spacing_before_bullet = match.group(1) 106 bullet_point = match.group(2) 107 spacing_after_bullet = match.group(3) 108 text = match.group(4) 109 translated_text = self.translate_text( 110 text, line.occurrence, "Bullet: '{0}'".format(bullet_point)) 111 self.write_output_line(spacing_before_bullet + bullet_point + spacing_after_bullet + translated_text) 112 113 def process_image(self, line): 114 match = re.match(r'^(\\image )(.*)( \d* \d*;)$', line.text) 115 image_command = match.group(1) 116 image_source = match.group(2) 117 image_coords = match.group(3) 118 translated_image_source = self.translate_text(image_source, line.occurrence, 'Image filename') 119 self.write_output_line(image_command + translated_image_source + image_coords) 120 121 def process_code(self, paragraph): 122 text_lines = [] 123 for line in paragraph: 124 match = re.match(r'^\\s;(.*)', line.text) 125 code_line = match.group(1) 126 text_lines.append(code_line) 127 128 joined_text_lines = '\n'.join(text_lines) 129 translated_text_lines = self.translate_text(joined_text_lines, paragraph[0].occurrence, 'Source code') 130 for line in translated_text_lines.split('\n'): 131 self.write_output_line(r'\s;' + line) 132 133 def process_plain(self, paragraph): 134 text_lines = [] 135 for line in paragraph: 136 text_lines.append(line.text) 137 138 joined_text_lines = '\n'.join(text_lines) 139 translated_text_lines = self.translate_text(joined_text_lines, paragraph[0].occurrence, 'Plain text') 140 for line in translated_text_lines.split('\n'): 141 self.write_output_line(line) 142 143 def translate_text(self, text, occurrence, type_comment): 144 converted_text = convert_escape_syntax_to_tag_syntax(text) 145 self.template_file.insert_entry(converted_text, occurrence, type_comment) 146 147 if not self.language_file: 148 return text 149 150 translated_text = self.language_file.translate(converted_text) 151 return convert_tag_syntax_to_escape_syntax(translated_text) 152 153def convert_escape_syntax_to_tag_syntax(text): 154 # Replace \button $id; as pseudo xHTML <button $id/> tags 155 text = re.sub(r'\\(button|key) ([^;]*?);', r'<\1 \2/>', text) 156 # Put \const;Code\norm; sequences into pseudo-HTML <format const> tags 157 text = re.sub(r'\\(const|type|token|key);([^\\;]*?)\\norm;', r'<format \1>\2</format>', text) 158 # Transform CBot links \l;text\u target; into pseudo-HTML <a target>text</a> 159 text = re.sub(r'\\l;(.*?)\\u (.*?);', r'<a \2>\1</a>', text) 160 # Cleanup pseudo-html targets separated by \\ to have a single character | 161 text = re.sub(r'<a (.*?)\\(.*?)>', r'<a \1|\2>', text) 162 # Replace remnants of \const; \type; \token, \norm; or \key; as pseudo xHTML <const/> tags 163 text = re.sub(r'\\(const|type|token|norm|key);', r'<\1/>', text) 164 # Put \c;Code\n; sequences into pseudo-HTML <code> tags 165 text = re.sub(r'\\c;([^\\;]*?)\\n;', r'<code>\1</code>', text) 166 # Replace remnants of \s; \c; \b; or \n; as pseudo xHTML <s/> tags 167 text = re.sub(r'\\([scbn]);', r'<\1/>', text) 168 return text 169 170def convert_tag_syntax_to_escape_syntax(text): 171 # Invert the replace remnants of \s; \c; \b; or \n; as pseudo xHTML <s/> tags 172 text = re.sub(r'<([scbn])/>', r'\\\1;', text) 173 # Invert the put of \c;Code\n; sequences into pseudo-HTML <code> tags 174 text = re.sub(r'<code>([^\\;]*?)</code>', r'\\c;\1\\n;', text) 175 # Invert the replace remnants of \const; \type; \token or \norm; as pseudo xHTML <const/> tags 176 text = re.sub(r'<(const|type|token|norm|key)/>', r'\\\1;', text) 177 # Invert the cleanup of pseudo-html targets separated by \\ to have a single character | 178 text = re.sub(r'<a (.*?)\|(.*?)>', r'<a \1\\\2>', text) 179 # Invert the transform of CBot links \l;text\u target; into pseudo-HTML <a target>text</a> 180 text = re.sub(r'<a (.*?)>(.*?)</a>', r'\\l;\2\\u \1;', text) 181 # Invert the put \const;Code\norm; sequences into pseudo-HTML <format const> tags 182 text = re.sub(r'<format (const|type|token|key)>([^\\;]*?)</format>', r'\\\1;\2\\norm;', text) 183 # Invert the replace of \button $id; as pseudo xHTML <button $id/> tags 184 text = re.sub(r'<(button|key) (.*?)/>', r'\\\1 \2;', text) 185 return text 186 187""" 188 Create jobs for help translation 189 190 Assumes that input_dir has structure like so: 191 ${input_dir}/E/help_file1.txt 192 ... 193 ${input_dir}/E/help_fileN.txt 194 195 The output files will be saved in: 196 ${output_dir}/${language_char1}/${install_subdir}/help_file1.txt 197 ... 198 ${output_dir}/${language_charM}/${install_subdir}/help_fileN.txt 199""" 200def create_help_translation_jobs(input_dir, output_dir, install_subdir, template_file, language_files): 201 translation_jobs = [] 202 203 e_dir = os.path.join(input_dir, 'E') 204 input_files = sorted(os.listdir(e_dir)) 205 206 if not install_subdir: 207 install_subdir = '' 208 209 language_files_list = [] 210 if len(language_files) > 0: 211 language_files_list = language_files 212 else: 213 # We need at least one dummy language file to create any jobs 214 language_files_list = [None] 215 216 for language_file in language_files_list: 217 output_translation_dir = None 218 if language_file: 219 output_translation_dir = nice_path_join(output_dir, language_file.language_char(), install_subdir) 220 nice_mkdir(output_translation_dir) 221 222 for input_file in input_files: 223 translation_jobs.append(HelpTranslationJob( 224 input_file = os.path.join(e_dir, input_file), 225 output_file = nice_path_join(output_translation_dir, input_file), 226 template_file = template_file, 227 language_file = language_file)) 228 229 return translation_jobs 230