Notes for "Translation - Check if code and template are prepared for it" task

Table of contents:

Translation - Check if code and template are prepared for it.

Snippets from IRC

Why not use %s placeholders in strings

<ThomasWaldmann> it is clear why %s or %d is no good?

<kennym> ThomasWaldmann: no, not really. Could you please explain that?

<kennym> you use %(blabla)s instead

<Roboraider> I'm back.

<dreimark> kennym: one who later has to translate it knows better what the meaning is

<dreimark> and the grammar logic can be completly different

<kennym> dreimark: so it's only a convention?

<ThomasWaldmann> python / jinja2 has placeholders in strings

<ThomasWaldmann> "%s is %s" depends on sequence

<dreimark> if you have two anonymous placeholders

<kennym> oh, sure!

<dreimark> it can get a different meaning in a translated string

<ThomasWaldmann> ngettext has no abbreviation btw, so you'll write ngettext(...) for that

Iteration #1

Check existing i18n and correct bad English.

TODO

Regex to find gettext calls:

_\(\"|_\(\'|N_\("

Check lines with "flash("

apps/frontend/views.py
495:        flash(*msg)
523:        flash(*msg)
594:                flash(msg, "error")
596:                flash(_('Account created, please log in now.'), "info")
660:                    flash(msg, "error")
661:            flash(_("If this account exists, you will be notified."), "info")
722:                flash(_("Your password has been changed, you can log in now."), "info")
724:                flash(_('Your token is invalid!'), "error")
771:                flash(hint, "info")
781:            flash(msg, "error")
803:    flash(_("You are now logged out."), "info")
935:                flash(_("Your password has been changed."), "info")
967:        flash(_("You must log in to use bookmarks."), "error")

support/flaskext/babel.py
223:        flash(gettext('Language was changed'))

support/flask/helpers.py
204:def flash(message, category='message'):

Nothing to do here...

MoinMoin/

No results.

MoinMoin/auth

http.py
62:            response = Response(_('Please log in first.'), 401,

ldap_login.py
129:            return ContinueLogin(user_obj, _('Missing password. Please enter user name and password.'))
199:                        return ContinueLogin(user_obj, _("Invalid username or password."))
245:                return CancelLogin(_("Invalid username or password."))
259:            return ContinueLogin(user_obj, _("LDAP server %(server)s failed.", server=server))

__init__.py
234:            return ContinueLogin(user_obj, _('Missing password. Please enter user name and password.'))
242:            return ContinueLogin(user_obj, _("Invalid username or password."))
245:        msg = _('If you do not have an account, <a href="%(register_url)s">you can create one now</a>. ',
247:        msg += _('<a href="%(recover_url)s">Forgot your password?</a>',

Nothing to do here, either.

MoinMoin/config

default.py
333:        return _("Password is too short.")
335:        return _("Password has not enough different characters.")
341:        return _("Password is too easy (password contains name or name contains password).")
350:            return _("Password is too easy (keyboard sequence).")

Better: "Password is too easily guessable."

MoinMoin/converter

html_out.py
598:                                  children=[_('Contents'), headtogglelink])

macro.py
79:            elem_error.append(_('<<%(macro_name)s: execution failed [%(error_msg)s] (see also the log)>>',

Nothing to do.

MoinMoin/datastruct

No results.

MoinMoin/filter

No results.

MoinMoin/items

__init__.py
1243:        title = _('Edit drawing %(filename)s (opens in new window)', filename=item_name)
1258:            title = _('Clickable drawing: %(filename)s', filename=item_name)
1314:        title = _('Edit drawing %(filename)s (opens in new window)', filename=self.name)
1331:            title = _('Clickable drawing: %(filename)s', filename=self.name)

Nothing to do.

MoinMoin/macro

WikiConfig.py
42:            moin_page.h(attrib={moin_page.outline_level: '1'}, children=[_("Wiki configuration")]))
44:        desc = _("This table shows all settings in this wiki that do not have default values. "
58:        for text in [_('Variable name'), _('Setting'), ]:

WikiConfigHelp.py
42:            for text in [_('Variable name'), _('Default'), _('Description'), ]:

GetText.py
21:        translation = _(' '.join(args.positional))

GoTo.py
29:        _("Go To Page"))) #HHH ?

HighlighterList.py
25:        column_titles = [_('Lexer description'),
26:                         _('Lexer names'),
27:                         _('File patterns'),
28:                         _('Mimetypes'),

Nothing to do.

MoinMoin/mail

sendmail.py
84:        return (1, _("No recipients, nothing to do"))
158:            return (0, _("Connection to mailserver '%(server)s' failed: %(reason)s",
174:            return (0, _("Mail not sent"))
177:    return (1, _("Mail sent OK"))

"Mail sent OK" -- bad English. "Mail sent successfully" -- better

MoinMoin/script

No results

MoinMoin/search

Xapian/search.py
65:            return self._getHits(pages), (search_results.estimate_is_exact and '' or _('about'), search_results.matches_estimated)

results.py
286:            _("Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s "
299:                formatter.text(_("seconds"))),
694:                        f.text(_('Previous')),
715:                f.text(_('Next')),
747:            rev_str = '%s: %d (%s)' % (_('rev'), rev, _('current'))
749:            rev_str = '%s: %d' % (_('rev'), rev, )
750:        lastmod_str = _('last modified: %s') % p.mtime(printable=True)
809:        self.matchLabel = (_('match'), _('matches'))

Oh, cool! Xapian! I love Xapian! Oops... I'm just the translator... looks all right.

MoinMoin/security

163:    textcha_incorrect_msg = N_('The entered TextCha was incorrect.')
164:    textcha_invalid_msg = N_('The TextCha question is invalid or has expired. Please try again.')
183:    textcha = String.using(label=N_('TextCha')).validated_by(TextChaValid())

Why do you use N_? Isn't calling gettext(), aka "_", enough?

Answer:

<dreimark> kennym: in the past we had to write words as CamelCase to get a link to a new item
<dreimark> so yes it was by design. Now we can write it also as [[Site Map]] but that also in moin-2.0 not correct
<dreimark> because in moin-2.0 it is not anymore a macro on a page
<dreimark> it is a view now
<dreimark> +sitemap
<kennym> dreimark: and _() doesn't do the job?
<dreimark> it does, there is no counter involved

Fixed.

MoinMoin/signalling

No results.

MoinMoin/static

No results.

MoinMoin/storage

No results.

MoinMoin/themes

__init__.py
45:        title = N_('Access denied')
46:        description = N_('You are not allowed to access this resource.')
338:            (_('Global History'), 'global_history', 'frontend.global_history', False, ),
339:            (_('Global Items Index'), 'global_index', 'frontend.global_index', False, ),
340:            (_('Global Tags Index'), 'global_tags', 'frontend.global_tags', False, ),
341:            (_('Wanted Items'), 'wanted_items', 'frontend.wanted_items', False, ),
342:            (_('Orphaned Items'), 'orphaned_items', 'frontend.orphaned_items', False, ),
344:            (_('-----------------------------------'), 'show', 'frontend.show_item', True),

Does this sequence of dashes have to be localized???

345:            (_('What links here?'), 'backlinks', 'frontend.backlinks', False, ),
346:            (_('Local Site Map'), 'sitemap', 'frontend.sitemap', False, ),
347:            (_('Items with similar names'), 'similar_names', 'frontend.similar_names', False, ),
348:            (_('-----------------------------------'), 'show', 'frontend.show_item', True),

and this?

349:            (_('Copy Item'), 'copy', 'frontend.copy_item', False, ),
350:            (_('Rename Item'), 'rename', 'frontend.rename_item', False, ),
351:            (_('Delete Item'), 'delete', 'frontend.delete_item', False, ),
352:            (_('Destroy Item'), 'destroy', 'frontend.destroy_item', False, ),
363:    text = _('anonymous')  # link text

Answer:

<kennym> and this one: _('-----------------------------------')
<kennym> is there any reason to localize dashes?
<dreimark> where did you find that?
<kennym> dreimark: http://moinmo.in/KennyMeyer/Notes#MoinMoin.2BAC8-themes
<kennym> dreimark: in MoinMoin/themes/__init__.py
<dreimark> ThomasWaldmann: http://hg.moinmo.in/moin/2.0-dev/annotate/edc4268a45db/MoinMoin/theme/__init__.py#l437
<dreimark> kennym: it is not needed.

Fixed.

This has to be there. Reverted.

MoinMoin/templates:

MoinMoin/util

paramparser.py
311:                _('Argument "%(name)s" must be a boolean value, not "%(value)s"', name=name, value=arg))
314:                _('Argument must be a boolean value, not "%(value)s"', value=arg))
340:                _('Argument "%(name)s" must be an integer value, not "%(value)s"', name=name, value=arg))
343:                _('Argument must be an integer value, not "%(value)s"', value=arg))
368:                _('Argument "%(name)s" must be a floating point value, not "%(value)s"', name=name, value=arg))
371:                _('Argument must be a floating point value, not "%(value)s"', value=arg))
398:                _('Argument "%(name)s" must be a complex value, not "%(value)s"', name=name, value=arg))
401:                _('Argument must be a complex value, not "%(value)s"', value=arg))
453:                _('Argument "%(name)s" must be one of "%(choices)s", not "%(value)s"',
459:                _('Argument must be one of "%(choices)s", not "%(value)s"',
685:            raise ValueError(_('Too many arguments'))
690:            raise ValueError(_('Cannot have arguments without name following'
706:                    raise ValueError(_('Argument "%(name)s" is required', name=argname))

Nothing to do.

contrib/

No results.

Iteration #2

Check for untranslated strings.

TODO

MoinMoin/apps

Looks OK.

MoinMoin/auth

Also, looks OK.

MoinMoin/config

diff -r 6a47b7bfe7ef MoinMoin/config/default.py
--- a/MoinMoin/config/default.py        Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/config/default.py        Sat Dec 25 13:34:15 2010 -0300
@@ -80,9 +80,9 @@
         self.cache.item_template_regexact = re.compile(u'^%s$' % self.item_template_regex, re.UNICODE)
 
         if not isinstance(self.superusers, list):
-            msg = """The superusers setting in your wiki configuration is not a list
-                     (e.g. ['Sample User', 'AnotherUser']).
-                     Please change it in your wiki configuration and try again."""
+            msg = _("""The superusers setting in your wiki configuration is not
+                    a list (e.g. ['Sample User', 'AnotherUser']).  Please change
+                    it in your wiki configuration and try again.""")
             raise error.ConfigurationError(msg)

MoinMoin/converter

Looks OK.

MoinMoin/datastructs

Looks OK.

MoinMoin/filter

Looks OK.

MoinMoin/items

Not sure if these have to be i18nised:

There are actually more.

diff -r 6a47b7bfe7ef MoinMoin/items/__init__.py
--- a/MoinMoin/items/__init__.py        Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/items/__init__.py        Sat Dec 25 14:27:27 2010 -0300
@@ -212,7 +212,9 @@
             from MoinMoin.util.tree import moin_page, xlink
             input_conv = reg.get(Type(self.mimetype), type_moin_document)
             if not input_conv:
-                raise TypeError("We cannot handle the conversion from %s to the DOM tree" % self.mimetype)
+                raise TypeError(_("We cannot handle the conversion from \
+                                  %(mimetype)s to the DOM tree",
+                                  mimetype=self.mimetype))
             link_conv = reg.get(type_moin_document, type_moin_document,
                     links='extern', url_root=Iri(request.url_root))
             smiley_conv = reg.get(type_moin_document, type_moin_document,
@@ -345,7 +347,8 @@
             new_rev.write(content)
             hash.update(content)
         else:
-            raise StorageError("unsupported content object: %r" % content)
+            raise StorageError(_("unsupported content object: %(content)r",
+                                 content=content))
         return hash_name, unicode(hash.hexdigest())
 
     def copy(self, name, comment=u''):
@@ -648,15 +651,17 @@
     def _render_data_diff(self, oldrev, newrev):
         hash_name = app.cfg.hash_algorithm
         if oldrev[hash_name] == newrev[hash_name]:
-            return "The items have the same data hash code (that means they very likely have the same data)."
+            return _("The items have the same data hash code (that means they \
+                     very likely have the same data).")
         else:
-            return "The items have different data."
+            return _("The items have different data.")
 
     _render_data_diff_text = _render_data_diff
     _render_data_diff_raw = _render_data_diff
 
     def _convert(self):
-        return "Impossible to convert the data to the mimetype : %s" % request.values.get('mimetype')
+        return _("Impossible to convert the data to the mimetype :\
+                 %(mimetype)s", mimetype=request.values.get('mimetype'))
 
     def do_get(self):
         hash = self.rev.get(app.cfg.hash_algorithm)
@@ -745,7 +750,10 @@
         @param expected_members: set of expected member file names
         """
         if not name in expected_members:
-            raise StorageError("tried to add unexpected member %r to container item %r" % (name, self.name))
+            raise StorageError(_("tried to add unexpected member %(unexp_name)r to \
+                                 container item %(name)r", 
+                                 unexp_name=name,
+                                 name=self.name))
         if isinstance(name, unicode):
             name = name.encode('utf-8')
         temp_fname = os.path.join(tempfile.gettempdir(), 'TarContainer_' +
@@ -758,14 +766,15 @@
             content = StringIO(content) # we need a file obj
         elif not hasattr(content, 'read'):
             logging.error("unsupported content object: %r" % content)
-            raise StorageError("unsupported content object: %r" % content)
+            raise StorageError(_("unsupported content object: %(content)r",
+                                 content=content)
         assert content_length >= 0  # we don't want -1 interpreted as 4G-1
         ti.size = content_length
         tf.addfile(ti, content)
         tf_members = set(tf.getnames())
         tf.close()
         if tf_members - expected_members:
-            msg = "found unexpected members in container item %r" % (self.name, )
+            msg = _("found unexpected members in container item %(item)r", item=self.name)
             logging.error(msg)
             os.remove(temp_fname)
             raise StorageError(msg)
@@ -873,7 +882,7 @@
         elif content_type == 'image/gif':
             output_type = 'GIF'
         else:
-            raise ValueError("content_type %r not supported" % content_type)
+            raise ValueError(_("content_type %(content_type)r not supported", content_type=content_type))
 
         # revision obj has read() seek() tell(), thus this works:
         image = PILImage.open(self.rev)
@@ -970,7 +979,8 @@
             elif content_type == 'image/gif':
                 output_type = 'GIF'
             else:
-                raise ValueError("content_type %r not supported" % content_type)
+                raise ValueError(_("content_type %(content_type)r not supported",
+                                   content_type=content_type))
 
             oldimage = PILImage.open(oldrev)
             newimage = PILImage.open(newrev)
diff -r 6a47b7bfe7ef MoinMoin/macro/Anchor.py
--- a/MoinMoin/macro/Anchor.py  Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/macro/Anchor.py  Sat Dec 25 14:27:27 2010 -0300
@@ -12,7 +12,7 @@
 class Macro(MacroInlineBase):
     def macro(self, anchor=unicode):
         if not anchor:
-            raise ValueError("Anchor: you need to give an anchor name.")
+            raise ValueError("Anchor: you need to specify an anchor name.")
 
         return moin_page.span(attrib={moin_page.id: anchor})
 
diff -r 6a47b7bfe7ef MoinMoin/mail/sendmail.py
--- a/MoinMoin/mail/sendmail.py Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/mail/sendmail.py Sat Dec 25 14:27:27 2010 -0300
@@ -173,8 +173,8 @@
             logging.exception("sendmail failed with an exception.")
             return (0, _("Mail not sent"))
 
-    logging.debug("Mail sent OK")
-    return (1, _("Mail sent OK"))
+    logging.debug("Mail sent successfully")
+    return (1, _("Mail sent sucessfully"))
 
 def encodeSpamSafeEmail(email_address, obfuscation_text=''):
     """ Encodes a standard email address to an obfuscated address

MoinMoin/macro

There are also some strings I am not sure of if they have to be i18nised.

MoinMoin/mail

diff -r 6a47b7bfe7ef MoinMoin/mail/sendmail.py --- a/MoinMoin/mail/sendmail.py Sat Dec 25 11:10:13 2010 -0300 +++ b/MoinMoin/mail/sendmail.py Sat Dec 25 14:10:58 2010 -0300 @@ -173,8 +173,8 @@

- logging.debug("Mail sent OK") - return (1, _("Mail sent OK")) + logging.debug("Mail sent successfully") + return (1, _("Mail sent sucessfully"))

MoinMoin/script

<ThomasWaldmann> kennym: no don't i18n commandline. this is admin stuff, admins know english.

MoinMoin/search

--- a/MoinMoin/search/queryparser/__init__.py   Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/search/queryparser/__init__.py   Sat Dec 25 14:42:24 2010 -0300
@@ -15,6 +15,7 @@
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
+from MoinMoin import _, N_
 from MoinMoin import config
 from MoinMoin.util.paramparser import parse_quoted_separated_ext, ParserPrefix, BracketError
 from MoinMoin.search.queryparser.expressions import AndExpression, OrExpression, TextSearch, TitleSearch, \
@@ -61,7 +62,7 @@
                             orexpr = OrExpression(terms)
                         terms = AndExpression(orexpr)
                     else:
-                        raise QueryError('Nothing to OR')
+                        raise QueryError(_('Nothing to OR'))
                     remaining = self._analyse_items(items)
                     if remaining.__class__ == OrExpression:
                         for sub in remaining.subterms():
@@ -77,7 +78,7 @@
                     # being parsed rather than rejecting an empty string
                     # before parsing...
                     if not item:
-                        raise QueryError("Term too short")
+                        raise QueryError(_("Term too short"))
                     regex = self.regex
                     case = self.case
                     if self.titlesearch:
@@ -97,7 +98,7 @@
                 while len(item) > 1:
                     m = item[0]
                     if m is None:
-                        raise QueryError("Invalid search prefix")
+                        raise QueryError(_("Invalid search prefix"))
                     elif m == M:
                         negate = True
                     elif "title".startswith(m):
@@ -117,7 +118,7 @@
                     elif "domain".startswith(m):
                         domain = True
                     else:
-                        raise QueryError("Invalid search prefix")
+                        raise QueryError(_("Invalid search prefix"))
                     item = item[1:]
 
                 text = item[0]

MoinMoin/security

diff -r 6a47b7bfe7ef MoinMoin/security/__init__.py
--- a/MoinMoin/security/__init__.py     Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/security/__init__.py     Sat Dec 25 14:47:22 2010 -0300
@@ -23,6 +23,7 @@
 
 from flask import flaskg
 
+from MoinMoin import _, N_
 from MoinMoin import user
 
 
@@ -343,7 +344,7 @@
                 entries, self.rest = self.rest.split(':', 1)
             except ValueError:
                 self.finished = 1
-                raise StopIteration("Can't parse rest of string")
+                raise StopIteration(_("Can't parse rest of string"))
             if entries == '':
                 entries = []
             else:
diff -r 6a47b7bfe7ef MoinMoin/security/textcha.py
--- a/MoinMoin/security/textcha.py      Sat Dec 25 11:10:13 2010 -0300
+++ b/MoinMoin/security/textcha.py      Sat Dec 25 14:47:22 2010 -0300
@@ -40,7 +40,7 @@
 from flatland import Form, String
 from flatland.validation import Validator
 
-from MoinMoin import _
+from MoinMoin import _, N_
 
 SHA1_LEN = 40 # length of hexdigest
 TIMESTAMP_LEN = 10 # length of timestamp
@@ -180,4 +180,4 @@
 class TextChaizedForm(Form):
     """a form providing TextCha support"""
     textcha_question = String
-    textcha = String.using(label=_('TextCha')).validated_by(TextChaValid())
+    textcha = String.using(label=N_('TextCha')).validated_by(TextChaValid())

MoinMoin/signalling

Looks OK.

MoinMoin/static

Looks OK.

MoinMoin/storage

Looks OK.

MoinMoin/support

3rd party libraries. Don't touch.

MoinMoin/templates

A lot of changes. Updated to newstyle-gettext.

MoinMoin/util

Looks OK.

MoinMoin: KennyMeyer/Notes/Translation_check_if_code_and_template_are_prepared_for_it (last edited 2010-12-26 10:02:32 by KennyMeyer)