From 6ec6f4edd81abcd6791d32e71367103f3bda0f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 20 Jan 2020 11:26:30 +0100 Subject: [PATCH] Use locale.localize() and new format to format Decimal This allows to use the new format syntax on Decimal which prevents a conversion to float. As locale.localize() is not yet added to released Python, we monkey patch older version. We also replace locale.format by locale.format_string as it has been deprecated in Python 3.7. issue8574 review282011002 --- tryton/__init__.py | 22 +++++++++++++++++++ tryton/common/domain_parser.py | 3 ++- tryton/common/timedelta.py | 3 ++- tryton/gui/window/view_form/model/field.py | 8 +++---- .../view_form/view/form_gtk/dictionary.py | 5 +++-- .../window/view_form/view/graph_gtk/bar.py | 3 ++- .../window/view_form/view/graph_gtk/graph.py | 2 +- .../window/view_form/view/graph_gtk/line.py | 3 ++- .../window/view_form/view/graph_gtk/pie.py | 15 +++++++------ tryton/gui/window/view_form/view/list.py | 15 +++++++------ 10 files changed, 54 insertions(+), 25 deletions(-) diff --git a/tryton/__init__.py b/tryton/__init__.py index 1a26d47f39..d3b439655e 100644 --- a/tryton/__init__.py +++ b/tryton/__init__.py @@ -2,6 +2,7 @@ # this repository contains the full copyright notices and license terms. __version__ = "5.5.0" import gi +import locale gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') @@ -16,3 +17,24 @@ try: import goocalendar except ImportError: pass + +if not hasattr(locale, 'localize'): + def localize(formatted, grouping=False, monetary=False): + if '.' in formatted: + seps = 0 + parts = formatted.split('.') + if grouping: + parts[0], seps = locale._group(parts[0], monetary=monetary) + decimal_point = locale.localeconv()[ + monetary and 'mon_decimal_point' or 'decimal_point'] + formatted = decimal_point.join(parts) + if seps: + formatted = locale._strip_padding(formatted, seps) + else: + seps = 0 + if grouping: + formatted, seps = locale._group(formatted, monetary=monetary) + if seps: + formatted = locale._strip_padding(formatted, seps) + return formatted + setattr(locale, 'localize', localize) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index 8e69d0380d..7ace157aae 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -319,7 +319,8 @@ def format_value(field, value, target=None, context=None): digit = len(str(value * factor).rstrip('0').split('.')[1]) except IndexError: digit = 0 - return locale.format('%.*f', (digit, value * factor or 0), True) + return locale.localize( + '{0:.{1}f}'.format(value * factor or 0, digit), True) def format_selection(): selections = dict(field['selection']) diff --git a/tryton/common/timedelta.py b/tryton/common/timedelta.py index 8dab3de361..c60d48059c 100644 --- a/tryton/common/timedelta.py +++ b/tryton/common/timedelta.py @@ -56,7 +56,8 @@ def format(value, converter=None): for (k, _), v in zip(converter[:-3], values): if v: - text.append(locale.format('%d', v, True) + _get_separators()[k]) + text.append( + locale.format_string('%d', v, True) + _get_separators()[k]) if any(values[-3:]) or not text: time = '%02d:%02d' % tuple(values[-3:-1]) if values[-1] or value: diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 65e9a5b6a4..b27b2b17f6 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -367,14 +367,14 @@ class FloatField(Field): value = record.value.get(self.name) if value is not None: digits = self.digits(record, factor=factor) + d = value * factor + if not isinstance(d, Decimal): + d = Decimal(repr(d)) if digits: p = int(digits[1]) else: - d = value * factor - if not isinstance(d, Decimal): - d = Decimal(repr(d)) p = -int(d.as_tuple().exponent) - return locale.format('%.*f', (p, value * factor), True) + return locale.localize('{0:.{1}f}'.format(d, p), True) else: return '' diff --git a/tryton/gui/window/view_form/view/form_gtk/dictionary.py b/tryton/gui/window/view_form/view/form_gtk/dictionary.py index 4ddb0d02b5..a3f95fa05e 100644 --- a/tryton/gui/window/view_form/view/form_gtk/dictionary.py +++ b/tryton/gui/window/view_form/view/form_gtk/dictionary.py @@ -238,7 +238,7 @@ class DictIntegerEntry(DictEntry): def set_value(self, value): if value is not None: - txt_val = locale.format('%d', value, True) + txt_val = locale.format_string('%d', value, True) else: txt_val = '' self.widget.set_text(txt_val) @@ -275,7 +275,8 @@ class DictFloatEntry(DictIntegerEntry): self.widget.digits = None self.widget.set_width_chars(self.width) if value is not None: - txt_val = locale.format('%.*f', (digits[1], value), True) + txt_val = locale.localize( + '{0:.{1}f}'.format(value, digits[1]), True) else: txt_val = '' self.widget.set_text(txt_val) diff --git a/tryton/gui/window/view_form/view/graph_gtk/bar.py b/tryton/gui/window/view_form/view/graph_gtk/bar.py index 164d516e66..0574734fb6 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/bar.py +++ b/tryton/gui/window/view_form/view/graph_gtk/bar.py @@ -93,7 +93,8 @@ class Bar(Graph): label = common.timedelta.format( datetime.timedelta(seconds=bar.yval), converter) else: - label = locale.format('%.2f', bar.yval, True) + label = locale.localize( + '{:.2f}'.format(bar.yval), True) label += '\n' label += str(self.labels[bar.xname]) self.popup.set_text(label) diff --git a/tryton/gui/window/view_form/view/graph_gtk/graph.py b/tryton/gui/window/view_form/view/graph_gtk/graph.py index 0b0e84948d..1b812e686f 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/graph.py +++ b/tryton/gui/window/view_form/view/graph_gtk/graph.py @@ -178,7 +178,7 @@ class Graph(Gtk.DrawingArea): for i in range(int(self.yrange / base) + 1): val = int(self.minyval / base) * base + i * base h = (val - self.minyval) * self.yscale - label = locale.format('%.2f', val, True) + label = locale.localize('{:.2f}'.format(val), True) ylabels.append((h, label)) return ylabels diff --git a/tryton/gui/window/view_form/view/graph_gtk/line.py b/tryton/gui/window/view_form/view/graph_gtk/line.py index 36deceee8b..ef75ac0a08 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/line.py +++ b/tryton/gui/window/view_form/view/graph_gtk/line.py @@ -201,7 +201,8 @@ class Line(Graph): yfields_timedelta[point.yname]) label += common.timedelta.format(point.yval, converter) else: - label += locale.format('%.2f', point.yval, True) + label += locale.localize( + '{:2f}'.format(point.yval), True) label += '\n' label += str(self.labels[point.xname]) self.popup.set_text(label) diff --git a/tryton/gui/window/view_form/view/graph_gtk/pie.py b/tryton/gui/window/view_form/view/graph_gtk/pie.py index ccca4d563f..467e0f6d71 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/pie.py +++ b/tryton/gui/window/view_form/view/graph_gtk/pie.py @@ -33,7 +33,7 @@ class Pie(Graph): math.cos(normalisedAngle) * (self.radius + 10) label = '%s (%s%%)' % (self.labels[slice.xname], - locale.format('%.2f', slice.fraction * 100)) + locale.localize('{:.2f}'.format(slice.fraction * 100))) extents = cr.text_extents(label) labelWidth = extents[2] labelHeight = extents[3] @@ -156,12 +156,13 @@ class Pie(Graph): sum = common.timedelta.format( datetime.timedelta(seconds=self.sum), converter) else: - value = locale.format('%.2f', - slice.fraction * self.sum) - sum = locale.format('%.2f', self.sum) - label = '%s (%s%%)\n%s/%s' % (self.labels[slice.xname], - locale.format('%.2f', slice.fraction * 100), - value, sum) + value = locale.localize( + '{:.2f}'.format(slice.fraction * self.sum)) + sum = locale.localize('{:.2f}'.format(self.sum)) + label = '%s (%s%%)\n%s/%s' % ( + self.labels[slice.xname], + locale.localize('{:.2f}'.format(slice.fraction * 100)), + value, sum) self.popup.set_text(label) self.queue_draw() else: diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index f9e5755531..61bcd42a27 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -1100,14 +1100,15 @@ class ViewTree(View): selected_sum = common.timedelta.format( selected_sum, converter) sum_ = common.timedelta.format(sum_, converter) - elif digit: - selected_sum = locale.format( - '%.*f', (digit, selected_sum or 0), True) - sum_ = locale.format('%.*f', (digit, sum_ or 0), True) + elif digit is not None: + selected_sum = locale.localize( + '{0:.{1}f}'.format(selected_sum or 0, digit), True) + sum_ = locale.localize( + '{0:.{1}f}'.format(sum_ or 0, digit), True) else: - selected_sum = locale.format( - '%s', selected_sum or 0, True) - sum_ = locale.format('%s', sum_ or 0, True) + selected_sum = locale.localize( + '{}'.format(selected_sum or 0), True) + sum_ = locale.localize('{}'.format(sum_ or 0), True) text = '%s / %s' % (selected_sum, sum_) else: