2
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4
# Copyright (C) 2011 David Planella <david.planella@ubuntu.com>
5
# This program is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License version 3, as published
7
# by the Free Software Foundation.
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranties of
11
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12
# PURPOSE. See the GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License along
15
# with this program. If not, see <http://www.gnu.org/licenses/>.
18
# This module implements a class to merge translations contained in Gettext
19
# PO files into different types of files. Right now only merging into HTML
20
# has been implemented.
26
from translate_html import translate_htmlconfig
32
sys.stderr.write('You need the Python Polib library to run this ' +
33
'script.\nYou can install it by running:\n\t' +
34
'$ sudo apt-get install python-polib')
36
# MIME type definitions (type, encoding)
37
HTML_FILE = ('text/html', None)
38
JS_FILE = ('application/javascript', None)
40
PO_FOLDER = translate_htmlconfig.PO_FOLDER
41
POTFILES = translate_htmlconfig.POTFILES
44
class StringMerger(object):
45
"""This class implements an object to load translations from Gettext
46
PO files and merge them into different types of files.
49
def __init__(self, test_mode):
50
self.translations = self._load_translations()
51
self.files = self._load_files()
52
self.test_mode = test_mode
54
def _load_files(self):
55
"""Gets the list of files to merge translations for"""
56
with open(translate_htmlconfig.get_source_file(PO_FOLDER,
59
for line in fp.readlines():
60
if not line.startswith('#'):
62
translate_htmlconfig.get_sources_path(), line)
63
file_list.append(line.strip())
66
def _load_translations(self):
67
"""Loads the PO files to read translations from"""
68
po_dir = os.path.join(translate_htmlconfig.get_sources_path(),
71
for po_file in os.listdir(po_dir):
72
fname, fext = os.path.splitext(po_file)
74
translations.append(fname)
78
"""Merge translations into the final translated files"""
79
for translation in self.translations:
80
for file_to_merge in self.files:
81
merger = getMerger(self.test_mode, translation, file_to_merge)
85
class StringMergerHtml(object):
86
"""HTML string merger. Reads the given translations from PO files and
87
merges them into an HTML file
90
def __init__(self, test_mode, langcode, htmlfile):
91
self.langcode = langcode
92
self.ietf_langcode = langcode_glib_to_ietf(langcode)
93
self.pofile = os.path.join(translate_htmlconfig.get_sources_path(),
94
PO_FOLDER, self.langcode + '.po')
95
self.htmlfile = htmlfile
96
self.test_mode = test_mode
99
"""Does the actual merge operation and writes translated files to
101
htmlfile_rel = self.htmlfile.replace(
102
translate_htmlconfig.get_sources_path(), '..')
103
with codecs.open(self.htmlfile, 'r', 'utf-8') as f:
106
fname = os.path.basename(self.htmlfile)
107
dirname = os.path.join(translate_htmlconfig.get_sources_path(),
109
if not os.path.exists(dirname):
112
html_file_translated = os.path.join(dirname, fname)
113
print >> sys.stderr, 'Translation written at:', \
115
with codecs.open(html_file_translated,
116
'w+', 'utf-8') as fd:
117
po = polib.pofile(self.pofile)
119
html_file_translated = html_file
124
entry_list = po.translated_entries()
126
for entry in entry_list:
127
#FIXME: is this check too strict? (We're limiting merging
128
# translations only if the original file listed in the
129
# PO files source file comments exists)
130
if (htmlfile_rel, '') in entry.occurrences:
131
# Note that we preserve the leading and trailing space
132
# to cater for words or sentences that have been split
133
# and are part of a larger sentence.
134
regex = re.compile(r'>(\s*)' +
135
re.escape(entry.msgid) + r'(\s*)<')
136
msgstr = self._mangle_po_entry(entry)
137
replacement = r'>\g<1>' + msgstr + '\g<2><'
139
html_file_translated = re.sub(regex, replacement,
140
html_file_translated)
142
html_file_translated = self.add_html_language(
144
html_file_translated)
145
fd.write(html_file_translated)
147
def _mangle_po_entry(self, po_entry):
148
"""If the test mode is set, inverts the original English text in the
149
msgid. We do this to spot untranslatable strings."""
150
if self.test_mode and self._is_po_entry_untranslated(po_entry):
151
po_entry = po_entry.msgid[::-1]
153
po_entry = po_entry.msgstr
157
def _is_po_entry_untranslated(self, po_entry):
158
#FIXME: polib has a bug whereby the fuzzy flag is not set when reading
160
is_untranslated = False
161
if not po_entry.translated() and not po_entry.obsolete and \
162
not 'fuzzy' in po_entry.flags:
163
is_untranslated = True
164
return is_untranslated
166
def add_html_language(self, ietf_langcode, html_str):
167
"""Adds the 'lang' and 'dir' attributes to the final translated file's
169
rtl_langs = ['he', 'ps', 'ar', 'ur']
172
if ietf_langcode.split('-')[0] in rtl_langs:
175
regex = re.compile('(<html)(.*?)(>)')
176
repl = r'\1 lang="{0}" dir="{1}"\3'.format(ietf_langcode, lang_dir)
178
html_str = re.sub(regex, repl, html_str)
183
class StringMergerJs(object):
184
"""JavaScript string merger. Currently not implemented.
194
class StringMergerNone(object):
195
"""Dummy string merger
205
def getMerger(test_mode, pofile, path):
206
"""Factory-like function to guess the type of file to merge translations
207
into by its MIME type, and return the appropriate merger class to
211
# Guess the type of the given file
212
filetype, encoding = mimetypes.guess_type(path)
214
# Return the appropriate merger class to handle the type
215
if (filetype, encoding) == HTML_FILE:
216
return StringMergerHtml(test_mode, pofile, path)
217
elif (filetype, encoding) == JS_FILE:
218
return StringMergerJs(test_mode, pofile, path)
220
return StringMergerNone(test_mode, pofile, path)
223
def langcode_glib_to_ietf(glib_langcode):
224
return re.sub('[_@]', '-', glib_langcode)