Add support for fuzzy_translation and improve translate feature
issue2224 review199001
This commit is contained in:
1 parent
49784a401c
commit
cbec9a9959
5 files changed
+216
-200
No files matched your search
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user