Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MANIFEST2
-rw-r--r--NEWS5
-rw-r--r--calculate.py60
-rw-r--r--eqnparser.py76
-rw-r--r--eqnparserhelp.py39
-rw-r--r--layout.py7
-rw-r--r--mathlib.py29
7 files changed, 136 insertions, 82 deletions
diff --git a/MANIFEST b/MANIFEST
index f423067..1b968bc 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,7 +6,7 @@ mathlib.py
plotlib.py
svgimage.py
toolbars.py
-po/calculate.pot
+po/Calculate.pot
po/en.po
po/es.po
po/nl.po
diff --git a/NEWS b/NEWS
index 5c71a73..a322c23 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+* Parser converted to unicode
+* Updates to improve translation #4527
+* Mul/Div symbol i18n #4573
+* Mul/Div button fixed #3526
+
12
11
diff --git a/calculate.py b/calculate.py
index 7a83d4e..ad326c7 100644
--- a/calculate.py
+++ b/calculate.py
@@ -1,3 +1,4 @@
+# -*- coding: UTF-8 -*-
# calculate.py, sugar calculator, by:
# Reinier Heeres <reinier@heeres.eu>
# Miguel Alvarez <miguel@laptop.org>
@@ -114,6 +115,8 @@ class Calculate(activity.Activity):
'plus': '+',
'minus': '-',
'asterisk': '*',
+ 'multiply': u'⨯',
+ 'divide': u'÷',
'slash': '/',
'BackSpace': lambda o: o.remove_character(-1),
'Delete': lambda o: o.remove_character(1),
@@ -162,7 +165,7 @@ class Calculate(activity.Activity):
self.clipboard = gtk.Clipboard()
self.select_reason = self.SELECT_SELECT
- self.buffer = ""
+ self.buffer = u""
self.showing_version = 0
self.showing_error = False
self.show_vars = False
@@ -184,21 +187,21 @@ class Calculate(activity.Activity):
self.presence = presenceservice.get_instance()
self.owner = self.presence.get_owner()
self.owner_id = str(self.owner._properties["nick"])
- _logger.debug(_('Owner_id: %s'), self.owner_id)
+ _logger.debug('Owner_id: %s', self.owner_id)
options = {
'receive_message': self.receive_message,
'on_connect': lambda: self.helper.send_message("req_sync", "")
}
+# self.helper.create_shared_object('old_eqs',
+# {'changed': lambda x: self.buddy_old_eqs_cb(),
+# 'type': 'python'},
+# iv = [])
+# self.helper.create_shared_object('vars',
+# {'changed': lambda x: self.buddy_vars_cb(),
+# 'type': 'python'},
+# iv = [])
self.helper = SharingHelper(self, opt=options)
- self.helper.create_shared_object('old_eqs',
- {'changed': lambda x: self.buddy_old_eqs_cb(),
- 'type': 'python'},
- iv = [])
- self.helper.create_shared_object('vars',
- {'changed': lambda x: self.buddy_vars_cb(),
- 'type': 'python'},
- iv = [])
_logger.info(_('Available functions:'))
for f in self.parser.get_function_names():
@@ -211,7 +214,7 @@ class Calculate(activity.Activity):
return True
def cleanup_cb(self, arg):
- _logger.debug(_('Cleaning up...'))
+ _logger.debug('Cleaning up...')
def equation_pressed_cb(self, eqn):
"""Callback for when an equation box is clicked"""
@@ -317,9 +320,9 @@ class Calculate(activity.Activity):
def process(self):
"""Parse the equation entered and show the result"""
- s = self.text_entry.get_text()
- label = self.label_entry.get_text()
- _logger.debug(_('process(): parsing \'%s\', label: \'%s\''), s, label)
+ s = unicode(self.text_entry.get_text())
+ label = unicode(self.label_entry.get_text())
+ _logger.debug('process(): parsing \'%r\', label: \'%r\'', s, label)
res = self.parser.parse(s)
if type(res) == types.StringType and res.find('</svg>') > -1:
res = SVGImage(data=res)
@@ -331,8 +334,8 @@ class Calculate(activity.Activity):
self.parser.set_var('Ans', self.ml.format_number(eqn.result))
self.helper.send_message("add_eq", str(eqn))
self.showing_error = False
- self.text_entry.set_text('')
- self.label_entry.set_text('')
+ self.text_entry.set_text(u'')
+ self.label_entry.set_text(u'')
# Show error
else:
@@ -344,7 +347,7 @@ class Calculate(activity.Activity):
return res is not None
def refresh_bar(self):
- _logger.debug(_('Refreshing right bar...'))
+ _logger.debug('Refreshing right bar...')
self.refresh_last_eq()
if self.layout.varbut.selected == 0:
self.refresh_history()
@@ -463,7 +466,7 @@ class Calculate(activity.Activity):
self.layout.show_history(list)
def clear(self):
- self.text_entry.set_text('')
+ self.text_entry.set_text(u'')
self.text_entry.grab_focus()
return True
@@ -476,6 +479,8 @@ class Calculate(activity.Activity):
##########################################
def write_file(self, file_path):
+ raise ValueError
+
"""Write journal entries, Calculate Journal Version (cjv) 1.0"""
_logger.info(_('Writing to journal (%s)'), file_path)
@@ -584,7 +589,7 @@ class Calculate(activity.Activity):
if end_ofs - start_ofs <= 0:
return False
partial_name = str[start_ofs:end_ofs]
- _logger.debug(_('tab-completing %s...'), partial_name)
+ _logger.debug('tab-completing %s...', partial_name)
# Lookup matching variables
vars = self.parser.get_var_names(start=partial_name)
@@ -657,15 +662,12 @@ class Calculate(activity.Activity):
return
key = gtk.gdk.keyval_name(event.keyval)
- if key is None:
- if event.hardware_keycode == 219:
- if (event.state & gtk.gdk.SHIFT_MASK):
- key = 'slash'
- else:
- key = 'asterisk'
+ if event.hardware_keycode == 219:
+ if (event.state & gtk.gdk.SHIFT_MASK):
+ key = 'divide'
else:
- key = 'None'
- _logger.debug(_('Key: %s (%r, %r)'), key, event.keyval, event.hardware_keycode)
+ key = 'multiply'
+ _logger.debug('Key: %s (%r, %r)', key, event.keyval, event.hardware_keycode)
if (event.state & gtk.gdk.CONTROL_MASK) and self.CTRL_KEYMAP.has_key(key):
f = self.CTRL_KEYMAP[key]
@@ -716,7 +718,7 @@ class Calculate(activity.Activity):
(start, end) = sel
text = self.text_entry.get_text()
elif len(sel) != 0:
- _logger.error(_('button_pressed(): len(sel) != 0 or 2'))
+ _logger.error('button_pressed(): len(sel) != 0 or 2')
return False
if type == self.TYPE_FUNCTION:
@@ -778,7 +780,7 @@ class Calculate(activity.Activity):
tmp = []
self.clear_equations()
for eq_str in val:
- _logger.info(_('receive_message: %s'), str(eq_str))
+ _logger.debug('receive_message: %s', str(eq_str))
self.add_equation(Equation(str=str(eq_str)))
self.refresh_bar()
diff --git a/eqnparser.py b/eqnparser.py
index 3d2b6d9..c4ccca3 100644
--- a/eqnparser.py
+++ b/eqnparser.py
@@ -1,3 +1,4 @@
+# -*- coding: UTF-8 -*-
# eqnparser.py, generic equation parser by Reinier Heeres <reinier@heeres.eu>
#
# This program is free software; you can redistribute it and/or modify
@@ -86,7 +87,7 @@ class ParserState:
self.result_type = t
return True
elif self.result_type != t:
- _logger.debug(_('Type error'))
+ _logger.debug('Type error')
return False
else:
return True
@@ -96,10 +97,10 @@ class ParserState:
if msg is not None:
self.error_msg = msg
if range is not None:
- _logger.debug(_('Setting range: %r'), range)
+ _logger.debug('Setting range: %r', range)
self.error_range = range
else:
- _logger.debug(_('Setting offset: %d'), self.ofs)
+ _logger.debug('Setting offset: %d', self.ofs)
self.error_range = (self.ofs, self.ofs + 1)
def set_error_range(self, r):
@@ -126,13 +127,13 @@ class EqnParser:
INVALID_OP = ('err', OP_INVALID, 0, lambda x: False)
- NAME_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789 '
- DIGITS = '0123456789'
- SPACE_CHARS = '\t \r\n'
+ NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789 '
+ DIGITS = u'0123456789'
+ SPACE_CHARS = u'\t \r\n'
# These will be filled from register_operator
- OP_START_CHARS = ""
- OP_CHARS = ""
+ OP_START_CHARS = u""
+ OP_CHARS = u""
TYPE_UNKNOWN = 1
TYPE_INT = 2
@@ -207,7 +208,9 @@ class EqnParser:
self.register_operator('-', self.OP_DIADIC, 0, lambda x: self.ml.sub(x[0], x[1]))
self.register_operator('-', self.OP_PRE, 1, lambda x: self.ml.negate(x[0]))
self.register_operator('*', self.OP_DIADIC, 1, lambda x: self.ml.mul(x[0], x[1]))
+ self.register_operator(u'⨯', self.OP_DIADIC, 1, lambda x: self.ml.mul(x[0], x[1]))
self.register_operator('/', self.OP_DIADIC, 1, lambda x: self.ml.div(x[0], x[1]))
+ self.register_operator(u'÷', self.OP_DIADIC, 1, lambda x: self.ml.div(x[0], x[1]))
self.register_operator('^', self.OP_DIADIC, 2, lambda x: self.ml.pow(x[0], x[1]))
self.register_operator('**', self.OP_DIADIC, 2, lambda x: self.ml.pow(x[0], x[1]))
@@ -294,12 +297,12 @@ class EqnParser:
# _logger.error('EqnParser.lookup_var(): recursion detected')
# return None
# self.variables[name].highest_level = level
- if type(self.variables[name]) is types.StringType and self.parse_var[name]:
+ if type(self.variables[name]) is types.UnicodeType and self.parse_var[name]:
return self.parse(self.variables[name], reset=False)
else:
return self.variables[name]
else:
- _logger.debug(_('variable %s not defined'), name)
+ _logger.debug('variable %s not defined', name)
ps.set_type(self.TYPE_SYMBOLIC)
return None
@@ -350,15 +353,16 @@ class EqnParser:
self.ps.set_error(ParserState.PARSE_ERROR, msg=_('Unable to parse argument %d: \'%s\'') % (i, args[i]))
return None
+ res = f(pargs)
try:
- res = f(pargs)
+ pass
# Maybe we should map exceptions to more obvious error messages
except Exception, inst:
res = None
self.ps.set_error(ParserState.PARSE_ERROR, msg=_("Function error: %s") % (str(inst)))
- _logger.debug(_('Function \'%s\' returned %s'), func, self.ml.format_number(res))
+ _logger.debug('Function \'%s\' returned %s', func, self.ml.format_number(res))
return res
def parse_number(self, ps):
@@ -375,14 +379,14 @@ class EqnParser:
ps.next()
# exponent
- if ps.char is not None and ps.char in 'eE':
+ if ps.char is not None and ps.char in u'eE':
ps.next()
- if ps.char is not None and ps.char in '+-':
+ if ps.char is not None and ps.char in u'+-':
ps.next()
while ps.more() and ps.char in self.DIGITS:
ps.next()
- _logger.debug(_('parse_number(): %d - %d: %s'), startofs, ps.ofs, ps.str[startofs:ps.ofs])
+ _logger.debug('parse_number(): %d - %d: %s', startofs, ps.ofs, ps.str[startofs:ps.ofs])
n = self.ml.parse_number(ps.str[startofs:ps.ofs])
return n
@@ -411,7 +415,7 @@ class EqnParser:
break
if op is not None:
- _logger.debug(_('parse_operator(): %d - %d: %s'), startofs, ps.ofs, ps.str[startofs:ps.ofs])
+ _logger.debug('parse_operator(): %d - %d: %s', startofs, ps.ofs, ps.str[startofs:ps.ofs])
return op
else:
return self.INVALID_OP
@@ -431,7 +435,7 @@ class EqnParser:
if pcount == 0 and (ps.ofs - startofs) > 0:
args.append(ps.str[startofs:ps.ofs])
ps.next()
- _logger.debug(_('parse_func_args(): %d - %d: %r'), startofs, ps.ofs, args)
+ _logger.debug('parse_func_args(): %d - %d: %r', startofs, ps.ofs, args)
return args
def parse_var_func(self, ps):
@@ -445,7 +449,7 @@ class EqnParser:
# handle function
if ps.char == '(':
ps.next()
- _logger.debug(_('parse_var_func(): function %d - %d: %s'), startofs, ps.ofs, name)
+ _logger.debug('parse_var_func(): function %d - %d: %s', startofs, ps.ofs, name)
args = self.parse_func_args(ps)
ret = self.eval_func(name, args, ps.level)
if ret is None:
@@ -454,7 +458,7 @@ class EqnParser:
# handle var
else:
- _logger.debug(_('parse_var_func(): variable %d - %d: %s'), startofs, ps.ofs, name)
+ _logger.debug('parse_var_func(): variable %d - %d: %s', startofs, ps.ofs, name)
res = self.lookup_var(name, ps)
if res is None:
ps.set_error(ParserState.PARSE_ERROR, msg=_("Variable '%s' not defined") % (name), range=(startofs, ps.ofs))
@@ -463,14 +467,14 @@ class EqnParser:
def _parse(self, ps, presedence=None):
if presedence is None:
ps.inc_level()
- _logger.debug(_('_parse(): %s, presedence: %r'), ps.state_string(), presedence)
+ _logger.debug('_parse(): %s, presedence: %r', ps.state_string(), presedence)
op = None
left_val = None
right_val = None
while ps.more():
-# _logger.debug(_('Looking at \'%c\', ofs %d in \'%s\''), ps.char, ps.ofs, ps.str)
+# _logger.debug('Looking at %r, ofs %d in %r', ps.char, ps.ofs, ps.str)
# Skip spaces
if ps.char in self.SPACE_CHARS:
@@ -491,7 +495,7 @@ class EqnParser:
ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected"))
return None
else:
- _logger.debug(_('returning %s'), self.ml.format_number(left_val))
+ _logger.debug('returning %s', self.ml.format_number(left_val))
return left_val
if ps.level > 0:
@@ -502,7 +506,7 @@ class EqnParser:
ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected"))
return None
else:
- _logger.debug(_('returning %s'), self.ml.format_number(left_val))
+ _logger.debug('returning %s', self.ml.format_number(left_val))
return left_val
else:
_logger.error_(('Parse error (right parenthesis, no level to close)'))
@@ -510,7 +514,7 @@ class EqnParser:
return None
# Parse number
- elif ps.char in '0123456789.':
+ elif ps.char == '.' or ps.char in self.DIGITS:
if right_val is not None or left_val is not None:
_logger.error(_('Number not expected'))
ps.set_error(ParserState.PARSE_ERROR, msg=_("Number not expected"))
@@ -532,15 +536,16 @@ class EqnParser:
if otype == self.OP_DIADIC:
if presedence is not None and opres <= presedence:
ps.set_ofs(startofs)
- _logger.debug(_('returning %s (by presedence, %d)'), self.ml.format_number(left_val), ps.ofs)
+ _logger.debug('returning %s (by presedence, %d)', self.ml.format_number(left_val), ps.ofs)
return left_val
else:
right_val = self._parse(ps, presedence=opres)
if right_val == None:
return None
+ print('left: %r, right: %r') % (left_val, right_val)
res = of([left_val, right_val])
- _logger.debug(_('OP: %s, %s ==> %s'), self.ml.format_number(left_val), self.ml.format_number(right_val), self.ml.format_number(res))
+ _logger.debug('OP: %s, %s ==> %s', self.ml.format_number(left_val), self.ml.format_number(right_val), self.ml.format_number(res))
left_val = res
right_val = None
op = None
@@ -548,7 +553,7 @@ class EqnParser:
# Operator that goes after value
elif otype == self.OP_POST:
res = of([left_val])
- _logger.debug(_('OP POST: %s ==> %s'), self.ml.format_number(left_val), self.ml.format_number(res))
+ _logger.debug('OP POST: %s ==> %s', self.ml.format_number(left_val), self.ml.format_number(res))
left_val = res
op = None
@@ -558,29 +563,29 @@ class EqnParser:
if right_val is None:
return None
left_val = of([right_val])
- _logger.debug(_('OP PRE: %s ==> %s'), self.ml.format_number(right_val), self.ml.format_number(left_val))
+ _logger.debug('OP PRE: %s ==> %s', self.ml.format_number(right_val), self.ml.format_number(left_val))
op = None
elif otype == self.OP_INVALID:
- _logger.debug(_('Invalid operator'))
+ _logger.debug('Invalid operator')
ps.set_error(ParserState.PARSE_ERROR, msg=_("Invalid operator"), range=(startofs, ps.ofs))
return None
# Parse variable or function
else:
if left_val is not None:
- _logger.debug(_('Operator expected'))
+ _logger.debug('Operator expected: %r', self.OP_START_CHARS)
ps.set_error(ParserState.PARSE_ERROR, msg=_("Operator expected"))
return None
left_val = self.parse_var_func(ps)
if not ps.more() and ps.level > 0:
- _logger.debug(_('Parse error: \')\' expected'))
+ _logger.debug('Parse error: \')\' expected')
ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected"))
return None
elif op is None and left_val is not None:
- _logger.debug(_('returning %s'), self.ml.format_number(left_val))
+ _logger.debug('returning %s', self.ml.format_number(left_val))
return left_val
else:
_logger.error(_('_parse(): returning None'))
@@ -596,7 +601,12 @@ class EqnParser:
def parse(self, eqn, reset=True):
"""Construct ParserState object and call _parse"""
- _logger.debug(_('parse(): %s'), eqn)
+
+ # Parse everything in unicode
+ if type(eqn) is types.StringType:
+ eqn = unicode(eqn)
+
+ _logger.debug('parse(): %r', eqn)
self.reset_variable_level(0)
if reset:
diff --git a/eqnparserhelp.py b/eqnparserhelp.py
index 34da3ae..2eb8d7c 100644
--- a/eqnparserhelp.py
+++ b/eqnparserhelp.py
@@ -19,33 +19,42 @@
# 2007-09-16: rwh, first version
import types
-
from gettext import gettext as _
+import logging
+_logger = logging.getLogger('EqnParser')
+
class Callable:
def __init__(self, f):
self.__call__ = f
-# Just an example implementation; should be fixed for internationalization
class EqnParserHelp():
+ # Unfortunately gettext is not yet initialized at the time _() is called here.
+ # Still do it like this to make sure these strings show up in the POT-file
DICT = {
- "acos": "help_acos",
- "asin": "help_asin",
- "exp": "help_exp",
- "functions": "help_functions",
- "operators": "help_operators",
- "plot": "help_plot",
- "sqrt": "help_sqrt",
- "test": "help_test",
- "variables": "help_variables",
+ # These are the help topics and should explain how things work
+ "acos": _("help_acos"),
+ "asin": _("help_asin"),
+ "cos": _("help_cos"),
+ "exp": _("help_exp"),
+ "functions": _("help_functions"),
+ "operators": _("help_operators"),
+ "plot": _("help_plot"),
+ "sin": _("help_sin"),
+ "sqrt": _("help_sqrt"),
+ "test": _("help_test"),
+ "variables": _("help_variables"),
}
def __init__(self):
- return
+ pass
def help(about):
- if type(about) != types.StringType or len(about) == 0:
+ _logger.debug('help about %r', about)
+
+ t = type(about)
+ if (t != types.StringType and t != types.UnicodeType) or len(about) == 0:
return _("help_usage")
if about == "index":
@@ -56,8 +65,8 @@ class EqnParserHelp():
ret = ""
for (key, val) in EqnParserHelp.DICT.iteritems():
- if about.find(key) != -1:
- ret += _(val)
+ if about == key:
+ ret += val
if ret == "":
ret += _("No help about '%s' available, use help(index) for the index") % (about)
diff --git a/layout.py b/layout.py
index cf0186c..91e5958 100644
--- a/layout.py
+++ b/layout.py
@@ -18,6 +18,9 @@ class CalcLayout:
return gtk.gdk.Color(int(rf*0xFFFF), int(gf*0xFFFF), int(bf*0xFFFF))
def create_button_data(self):
+ mul_sym = self._parent.ml.mul_sym
+ div_sym = self._parent.ml.div_sym
+
self.button_data = [
# [x, y, width, label, bgcol, cb]
[0, 0, 1, '7', self.col_gray2, lambda w: self._parent.add_text('7')],
@@ -43,8 +46,8 @@ class CalcLayout:
[3, 1, 1, '+', self.col_gray3, lambda w: self._parent.add_text('+')],
[4, 1, 1, '-', self.col_gray3, lambda w: self._parent.add_text('-')],
[5, 1, 1, '(', self.col_gray3, lambda w: self._parent.add_text('(')],
- [3, 2, 1, 'x', self.col_gray3, lambda w: self._parent.add_text('*')],
- [4, 2, 1, '/', self.col_gray3, lambda w: self._parent.add_text('/')],
+ [3, 2, 1, mul_sym, self.col_gray3, lambda w: self._parent.add_text(mul_sym)],
+ [4, 2, 1, div_sym, self.col_gray3, lambda w: self._parent.add_text(div_sym)],
[5, 2, 1, ')', self.col_gray3, lambda w: self._parent.add_text(')')],
[3, 3, 3, 'enter', self.col_gray1, lambda w: self._parent.process()],
diff --git a/mathlib.py b/mathlib.py
index 4d98ad8..ae18780 100644
--- a/mathlib.py
+++ b/mathlib.py
@@ -25,6 +25,8 @@ import random
import logging
_logger = logging.getLogger('MathLib')
+from gettext import gettext as _
+
class MathLib:
ANGLE_DEG = math.pi/180
ANGLE_RAD = 1
@@ -49,6 +51,29 @@ class MathLib:
self.set_constant('c_n', self.parse_number('0')) #neutron properties
self.set_constant('m_n', self.parse_number('1.6749272928e-27'))
+ self.setup_i18n()
+
+ def setup_i18n(self):
+ # The separator to mark thousands (default: ',')
+ self.thousand_sep = _('thousand_sep')
+ if self.thousand_sep == 'thousand_sep':
+ self.thousand_sep = ','
+
+ # The separator to mark fractions (default: '.')
+ self.fraction_sep = _('fraction_sep')
+ if self.fraction_sep == 'fraction_sep':
+ self.fraction_sep = '.'
+
+ # The multiplication symbol (default: '*')
+ self.mul_sym = _('mul_sym')
+ if self.mul_sym == 'mul_sym':
+ self.mul_sym = '*'
+
+ # The division symbol (default: '/')
+ self.div_sym = _('div_sym')
+ if self.div_sym == 'div_sym':
+ self.div_sym = '/'
+
def set_angle_type(self, type):
self.angle_scaling = self.d(type)
_logger.debug('Angle type set to:%s', self.angle_scaling)
@@ -115,9 +140,9 @@ class MathLib:
for i in xrange(len(digits)):
if i == dot_pos:
if i == 0:
- res += '0.'
+ res += '0' + self.fraction_sep
else:
- res += '.'
+ res += self.fraction_sep
res += str(digits[i])
if len(digits) < dot_pos: