Repository: web2py/web2py

Issue:
sequence item 0: expected str instance, bytes found when try open some of table in appadmin
After update web2py

web2py™ | Version 2.17.1-stable+timestamp.2018.08.06.01.02.56
-- | --
Python 3.6.3: /usr/local/bin/uwsgi (prefix: /usr)

Can't open no one table in appadmin

Traceback (most recent call last):
  File "/home/www-data/web2py/gluon/restricted.py", line 219, in restricted
    exec(ccode, environment)
  File "/home/www-data/web2py/applications/putevki/views/appadmin.html", line 196, in <module>
    <p>
  File "/home/www-data/web2py/gluon/globals.py", line 434, in write
    self.body.write(to_native(xmlescape(data)))
  File "/home/www-data/web2py/gluon/html.py", line 125, in xmlescape
    return to_bytes(data.xml())
  File "/home/www-data/web2py/gluon/languages.py", line 431, in xml
    return str(self) if self.M else local_html_escape(str(self), quote=False)
  File "/home/www-data/web2py/gluon/languages.py", line 391, in __str__
    self.T.translate(self.m, self.s))
  File "/home/www-data/web2py/gluon/languages.py", line 1009, in translate
    message = self.params_substitution(message, symbols)
  File "/home/www-data/web2py/gluon/languages.py", line 987, in params_substitution
    message = regex_plural.sub(sub_plural, message)
  File "/home/www-data/web2py/gluon/languages.py", line 980, in sub_plural
    part = regex_plural_tuple.sub(sub_tuple, s)
TypeError: sequence item 0: expected str instance, bytes found



Discussion:
Hey can you provide the table definition of the table that's causing this problem?
db.define_table('adresses',
                Field('id', 'integer'),
                Field('ls', 'text'),
                Field('fio', 'text'),
                Field('home_adres', 'text'),
                Field('tarif', 'text'),
                Field('status', 'text'),
                ),

db.define_table('putevka_setting',
               Field('count_day','integer'),
               Field('min_vyezd_per_day','integer'),
               Field('max_vyezd_per_day','integer'),
               Field('ofice_adress','string'),
               ),
db.define_table('putevka_archive',
#               Field('put_mounth','string'),
               Field('put_start_day','date'),
               Field('put_cur_day','date'),
               Field('put_adress','string'),
               ),

Whith all admin tables 
db.auth_user, db.auth_group & etc.
 
Some details, may be help.

variables
part undefined

re.compile('^{(?P<w>[^[\\]()]+)(?:\\[(?P<i>\\d+)\\])?}$')
<built-in method sub of _sre.SRE_Pattern object>
<function translator.params_substitution.<locals>.sub_plural.<locals>.sub_tuple>
'{!выбрана[0]}' - i think problem in this string, but i don't undastand where from it. 


I'm find where from this string.
this string is from /web2py/applications/application_name/languages/plural-ru.py

![2018-09-26 21-31-41](https://user-images.githubusercontent.com/2355645/46101332-52bac700-c1d4-11e8-9981-110a77ffc5a6.png)

first string.


Resolving patch:
diff --git a/gluon/languages.py b/gluon/languages.py
--- a/gluon/languages.py
+++ b/gluon/languages.py
@@ -311,7 +311,7 @@ def write_plural_dict(filename, contents):
     try:
         fp = LockedFile(filename, 'w')
         fp.write('#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
-        for key in sorted(contents, sort_function):
+        for key in sorted(contents, key=sort_function):
             forms = '[' + ','.join([repr(Utf8(form))
                                     for form in contents[key]]) + ']'
             fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
@@ -325,8 +325,8 @@ def write_plural_dict(filename, contents):
             fp.close()
 
 
-def sort_function(x, y):
-    return cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())
+def sort_function(x):
+    return unicode(x, 'utf-8').lower()
 
 
 def write_dict(filename, contents):
@@ -936,8 +936,8 @@ def sub_tuple(m):
                     word = w[1:]
                     fun = cap_fun
                 if i is not None:
-                    return fun(self.plural(word, symbols[int(i)]))
-                return fun(word)
+                    return to_native(fun(self.plural(word, symbols[int(i)])))
+                return to_native(fun(word))
 
             def sub_dict(m):
                 """ word(key or num)


Test patch: