Add support for fuzzy_translation and improve translate feature

issue2224
review199001
This commit is contained in:
Cédric Krier committed 2012-01-27 21:17:35 +01:00
1 parent 49784a401c
commit cbec9a9959
5 files changed
+216 -200

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add support for fuzzy_translation
* Activate save button on editing
* Refactor set/get and set/get_client of fields
* Display binary size alongside the buttons in treeview
@@ -4,13 +4,13 @@ import gettext
import gobject
import gtk
from interface import WidgetInterface
from interface import WidgetInterface, TranslateMixin
from tryton.common import Tooltips
_ = gettext.gettext
class Char(WidgetInterface):
class Char(WidgetInterface, TranslateMixin):
"Char"
def __init__(self, field_name, model_name, attrs=None):
@@ -42,6 +42,26 @@ class Char(WidgetInterface):
focus_entry.connect('key-press-event', self.send_modified)
self.widget.pack_start(self.entry)
self.button = None
if attrs.get('translate'):
self.button = self.translate_button()
self.widget.pack_start(self.button, False, False)
def translate_widget(self):
entry = gtk.Entry()
entry.set_property('activates_default', True)
entry.set_width_chars(10)
entry.set_max_length(int(self.attrs.get('size', 0)))
return entry
@staticmethod
def translate_widget_set(widget, value):
widget.set_text(value)
@staticmethod
def translate_widget_get(widget):
return widget.get_text()
def _color_widget(self):
if self.autocomplete:
return self.entry.get_child()
@@ -97,6 +117,8 @@ class Char(WidgetInterface):
self.entry.set_button_sensitivity(sensitivity[value])
else:
self.entry.set_editable(not value)
if self.button:
self.button.set_sensitive(not value)
if value:
self.widget.set_focus_chain([])
else:
@@ -2,7 +2,16 @@
#this repository contains the full copyright notices and license terms.
import gtk
import gobject
import gettext
from tryton.common import COLORS
import tryton.common as common
import tryton.rpc as rpc
from tryton.exceptions import TrytonServerError
from tryton.gui.window.nomodal import NoModal
from tryton.common import TRYTON_ICON
_ = gettext.gettext
class WidgetInterface(object):
@@ -154,3 +163,146 @@ class WidgetInterface(object):
def cancel(self):
pass
class TranslateDialog(NoModal):
def __init__(self, widget, languages):
NoModal.__init__(self)
self.widget = widget
self.win = gtk.Dialog(_('Translation'), self.parent,
gtk.DIALOG_DESTROY_WITH_PARENT)
self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
self.win.set_icon(TRYTON_ICON)
self.win.connect('response', self.response)
self.accel_group = gtk.AccelGroup()
self.win.add_accel_group(self.accel_group)
self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK).add_accelerator(
'clicked', self.accel_group, gtk.keysyms.Return,
gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
self.widgets = {}
table = gtk.Table(len(languages), 2)
table.set_homogeneous(False)
table.set_col_spacings(3)
table.set_row_spacings(2)
table.set_border_width(1)
for i, language in enumerate(languages):
if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
label = _(':') + language['name']
else:
label = language['name'] + _(':')
label = gtk.Label(label)
label.set_alignment(1.0, 0.5)
table.attach(label, 0, 1, i, i + 1, xoptions=gtk.FILL)
context = rpc.CONTEXT.copy()
context['language'] = language['code']
try:
value = rpc.execute('model', self.widget.record.model_name,
'read', self.widget.record.id, [self.widget.field_name],
context)[self.widget.field_name]
except TrytonServerError, exception:
common.process_exception(exception)
return
widget = self.widget.translate_widget()
self.widgets[language['code']] = (widget, value)
self.widget.translate_widget_set(widget, value)
table.attach(widget, 1, 2, i, i + 1)
vbox = gtk.VBox()
vbox.pack_start(table, False, True)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_NONE)
viewport.add(vbox)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
scrolledwindow.add(viewport)
self.win.vbox.pack_start(scrolledwindow, True, True)
sensible_allocation = self.sensible_widget.get_allocation()
self.win.set_default_size(int(sensible_allocation.width * 0.9),
int(sensible_allocation.height * 0.9))
self.register()
self.win.show_all()
common.center_window(self.win, self.parent, self.sensible_widget)
def response(self, win, response):
if response == gtk.RESPONSE_OK:
for code, widget in self.widgets.iteritems():
widget, old_value = widget
new_value = self.widget.translate_widget_get(widget)
if new_value == old_value:
continue
context = rpc.CONTEXT.copy()
context['language'] = code
context['fuzzy_translation'] = False
args = ('model', self.widget.record.model_name, 'write',
self.widget.record.id, {
self.widget.field_name: new_value,
}, context)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
self.widget.record.cancel()
self.widget.view.display()
self.destroy()
def destroy(self):
self.win.destroy()
NoModal.destroy(self)
class TranslateMixin:
def translate_button(self):
button = gtk.Button()
img = gtk.Image()
img.set_from_stock('tryton-locale', gtk.ICON_SIZE_SMALL_TOOLBAR)
button.set_image(img)
button.set_relief(gtk.RELIEF_NONE)
button.connect('clicked', self.translate)
return button
def translate(self, widget):
if self.record.id < 0 or self.record.modified:
common.message(
_('You need to save the record before adding translations!'))
return
try:
lang_ids = rpc.execute('model', 'ir.lang', 'search', [
('translatable', '=', True),
], rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
return
if not lang_ids:
common.message(_('No other language available!'))
return
try:
languages = rpc.execute('model', 'ir.lang', 'read', lang_ids,
['code', 'name'], rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
return
TranslateDialog(self, languages)
def translate_widget(self):
raise NotImplemented
@staticmethod
def translate_widget_set(widget, value):
raise NotImplemented
@staticmethod
def translate_widget_get(widget):
raise NotImplemented
@@ -2,13 +2,10 @@
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
import copy
import logging
import tryton.rpc as rpc
from tryton.gui.window.view_form.view.interface import ParserInterface
import tryton.common as common
from tryton.config import CONFIG, TRYTON_ICON
from tryton.exceptions import TrytonServerError
from tryton.config import CONFIG
from tryton.common.button import Button
_ = gettext.gettext
@@ -129,7 +126,6 @@ class _container(object):
self.cont = []
self.col = []
self.tooltips = tooltips
self.trans_box = []
def new(self, col=4):
table = gtk.Table(1, col)
@@ -155,7 +151,7 @@ class _container(object):
table.resize(height + 1, self.col[-1])
def wid_add(self, widget, name='', yexpand=False, ypadding=2, rowspan=1,
colspan=1, translate=False, fname=None, help_tip=False,
colspan=1, fname=None, help_tip=False,
yfill=False, xexpand=True, xfill=True, xpadding=3):
(table, width, height) = self.cont[-1]
if colspan > self.col[-1]:
@@ -176,15 +172,6 @@ class _container(object):
if help_tip:
self.tooltips.set_tip(widget, help_tip)
self.tooltips.enable()
if translate:
button = gtk.Button()
img = gtk.Image()
img.set_from_stock('tryton-locale', gtk.ICON_SIZE_SMALL_TOOLBAR)
button.set_image(img)
button.set_relief(gtk.RELIEF_NONE)
self.trans_box.append((button, name, fname,
widget.get_children()[0]))
widget.get_child().pack_start(button, fill=False, expand=False)
widget.show_all()
table.attach(widget, width, width + colspan,
height, height + rowspan,
@@ -454,7 +441,7 @@ class ParserForm(ParserInterface):
for attr_name in ('relation', 'domain', 'selection',
'relation_field', 'string', 'views', 'invisible',
'add_remove', 'sort', 'context', 'size', 'filename',
'autocomplete'):
'autocomplete', 'translate'):
if attr_name in fields[name].attrs and \
not attr_name in attrs:
attrs[attr_name] = fields[name].attrs[attr_name]
@@ -471,13 +458,9 @@ class ParserForm(ParserInterface):
widget_act.widget.set_size_request(
int(attrs.get('width', -1)),
int(attrs.get('height', -1)))
translate = False
if ftype in ('char', 'text'):
translate = fields[name].attrs.get('translate', False)
container.wid_add(Alignment(widget_act.widget, attrs),
fields[name].attrs['string'], fname=name,
help_tip=hlp,
translate=translate,
colspan=colspan,
yexpand=yexpand, yfill=yfill,
xexpand=xexpand, xfill=xfill)
@@ -559,185 +542,9 @@ class ParserForm(ParserInterface):
paned.pack1(widget, resize=True, shrink=True)
elif not paned.get_child2():
paned.pack2(widget, resize=True, shrink=True)
for (button, src, name, widget) in container.trans_box:
button.connect('clicked', self.translate, model_name, name,
src, widget)
return container.pop(), dict_widget, button_list, on_write, \
notebook_list, cursor_widget
def translate(self, widget, model_name, name, src, widget_entry):
obj_id = self.screen.current_record.id
if obj_id < 0:
common.message(
_('You need to save the record before adding translations!'))
return False
obj_id = self.screen.current_record.save(force_reload=False)
try:
lang_ids = rpc.execute('model', 'ir.lang',
'search', [('translatable', '=', '1')],
rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
return False
if not lang_ids:
common.message(_('No other language available!'))
return False
try:
lang_ids += rpc.execute('model', 'ir.lang',
'search', [('code', '=', 'en_US')],
rpc.CONTEXT)
langs = rpc.execute('model', 'ir.lang',
'read', lang_ids, ['code', 'name'],
rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
return False
code = rpc.CONTEXT.get('language', 'en_US')
widget_entry = widget_entry.get_children()[0]
if isinstance(widget_entry, gtk.ScrolledWindow):
widget_entry = widget_entry.get_child()
#widget accessor functions
def value_get(widget):
if isinstance(widget, gtk.Entry):
return widget.get_text()
elif isinstance(widget, gtk.TextView):
buf = widget.get_buffer()
iter_start = buf.get_start_iter()
iter_end = buf.get_end_iter()
return buf.get_text(iter_start, iter_end, False)
else:
return None
def value_set(widget, value):
if not value:
value = ''
if isinstance(widget, gtk.Entry):
widget.set_text(value)
elif isinstance(widget, gtk.TextView):
buf = widget.get_buffer()
buf.delete(buf.get_start_iter(), buf.get_end_iter())
iter_start = buf.get_start_iter()
buf.insert(iter_start, value)
def widget_duplicate(widget):
if isinstance(widget, gtk.Entry):
entry = gtk.Entry()
entry.set_property('activates_default', True)
entry.set_max_length(widget.get_max_length())
entry.set_width_chars(widget.get_width_chars())
return entry, gtk.FILL
elif isinstance(widget, gtk.TextView):
textview = gtk.TextView()
textview.set_wrap_mode(gtk.WRAP_WORD)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_ALWAYS)
scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
scrolledwindow.set_size_request(-1, 80)
scrolledwindow.add(textview)
textview.set_accepts_tab(False)
return scrolledwindow, gtk.FILL | gtk.EXPAND
else:
return None, False
parent = common.get_toplevel_window()
win = gtk.Dialog(_('Add Translation'), parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
win.set_has_separator(True)
win.vbox.set_spacing(5)
win.set_property('default-width', 600)
win.set_property('default-height', 400)
win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
win.set_icon(TRYTON_ICON)
accel_group = gtk.AccelGroup()
win.add_accel_group(accel_group)
win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
but_ok = win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
but_ok.add_accelerator('clicked', accel_group, gtk.keysyms.Return,
gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
vbox = gtk.VBox(spacing=5)
entries_list = []
table = gtk.Table(len(langs), 2)
table.set_homogeneous(False)
table.set_col_spacings(3)
table.set_row_spacings(0)
table.set_border_width(1)
i = 0
for lang in langs:
context = copy.copy(rpc.CONTEXT)
context['language'] = lang['code']
try:
val = rpc.execute('model', model_name,
'read', [obj_id], [name], context)
except TrytonServerError, exception:
common.process_exception(exception)
return False
val = val[0]
if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
label = gtk.Label(_(':') + lang['name'])
else:
label = gtk.Label(lang['name'] + _(':'))
label.set_alignment(1.0, 0.5)
(entry, yoptions) = widget_duplicate(widget_entry)
if code == lang['code']:
value_set(entry, value_get(widget_entry))
else:
value_set(entry, val[name])
entries_list.append((val['id'], lang['code'], entry))
table.attach(label, 0, 1, i, i + 1, yoptions=False,
xoptions=gtk.FILL, ypadding=2, xpadding=3)
table.attach(entry, 1, 2, i, i + 1, yoptions=yoptions,
ypadding=2, xpadding=3)
i += 1
vbox.pack_start(table)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_NONE)
viewport.add(vbox)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
scrolledwindow.add(viewport)
win.vbox.add(scrolledwindow)
win.show_all()
response = win.run()
if response == gtk.RESPONSE_OK:
to_save = [(x[0], x[1], value_get(x[2])) for x in entries_list]
while to_save != []:
new_val = {}
new_val['id'], new_val['code'], new_val['value'] = \
to_save.pop()
#update form field
if new_val['code'] == code:
value_set(widget_entry, new_val['value'])
context = copy.copy(rpc.CONTEXT)
context['language'] = new_val['code']
args = ('model', model_name, 'write', [obj_id],
{str(name): new_val['value']}, context)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
if response != gtk.RESPONSE_CANCEL:
self.screen.current_record.reload()
win.destroy()
parent.present()
return response != gtk.RESPONSE_CANCEL
from calendar import Calendar, DateTime
from float import Float
from integer import Integer
@@ -1,7 +1,7 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
from interface import WidgetInterface
from interface import WidgetInterface, TranslateMixin
from tryton.config import CONFIG
try:
@@ -10,7 +10,7 @@ except ImportError:
gtkspell = None
class TextBox(WidgetInterface):
class TextBox(WidgetInterface, TranslateMixin):
def __init__(self, field_name, model_name, attrs=None):
super(TextBox, self).__init__(field_name, model_name, attrs=attrs)
@@ -36,12 +36,46 @@ class TextBox(WidgetInterface):
self.widget.pack_start(self.scrolledwindow)
self.lang = None
self.button = None
if attrs.get('translate'):
self.button = self.translate_button()
self.widget.pack_start(self.button, False, False)
def translate_widget(self):
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
scrolledwindow.set_shadow_type(gtk.SHADOW_ETCHED_IN)
scrolledwindow.set_size_request(-1, 80)
textview = gtk.TextView()
textview.set_wrap_mode(gtk.WRAP_WORD)
textview.set_accepts_tab(False)
scrolledwindow.add(textview)
return scrolledwindow
@staticmethod
def translate_widget_set(widget, value):
textview = widget.get_child()
buf = textview.get_buffer()
buf.delete(buf.get_start_iter(), buf.get_end_iter())
buf.insert(buf.get_start_iter(), value or '')
@staticmethod
def translate_widget_get(widget):
textview = widget.get_child()
buf = textview.get_buffer()
return buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
def grab_focus(self):
return self.textview.grab_focus()
def _readonly_set(self, value):
super(TextBox, self)._readonly_set(value)
self.textview.set_editable(not value)
if self.button:
self.button.set_sensitive(not value)
if value:
self.widget.set_focus_chain([])
else: